blob: a5ebcf1c208ccd8525feb68725f32ba0622e69ef [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
#define ARRAY_CROSSOVER_FOR_VALIDATE 0
namespace Js
{
class SegmentBTree
{
// This is an auxiliary data structure to speed finding the correct array segment for sparse arrays.
// Rather than implement remove we only implement SwapSegment which requires the segment to be
// swapped is in the same relative order as the segment it replaces.
// The B-tree algorithm used is adapted from the pseudo-code in
// Introduction to Algorithms by Corman, Leiserson, and Rivest.
protected:
Field(uint32*) keys; // keys[i] == segments[i]->left
Field(SparseArraySegmentBase**) segments; // Length of segmentCount. Allocated with Leaf, no need to annotate inner pointer
Field(SegmentBTree*) children; // Length of segmentCount+1.
Field(uint32) segmentCount; // number of sparseArray segments in the Node
public:
static const uint MinDegree = 20; // Degree is the minimum branching factor. (If non-root, and non-leaf.)
// non-root nodes are between MinDegree and MinDegree*2-1 in size.
// e.g. For MinDegree == 32 -> this is 31 to 62 keys
// and 32 to 63 children (every key is surrounded by before and after children).
//
// Allocations are simply the max possible sizes of nodes
// We may do something more clever in the future.
static const uint32 MinKeys = MinDegree - 1; // Minimum number of keys in any non-root node.
static const uint32 MaxKeys = MinDegree*2 - 1;// Max number of keys in any node
static const uint32 MaxDegree = MinDegree*2; // Max number of children
static uint32 GetLazyCrossOverLimit(); // = MinDegree*3; // This is the crossover point for using the segmentBTee in our Arrays
// Ideally this doesn't belong here.
// Putting it here simply acknowledges that this BTree is not generic.
// The implementation is tightly coupled with it's use in arrays.
// The segment BTree adds memory overhead, we only want to incur it if
// it is needed to prevent O(n) effects from using large sparse arrays
// the BtreeNode is implicit:
// btreenode := (children[0], segments[0], children[1], segments[1], ... segments[segmentCount-1], children[segmentCount])
// Children pointers to the left contain segments strictly less than the segment to the right
// Children points to the right contain segments strictly greater than the segment to the left.
// Segments do not overlap, so the left index in a segment is sufficient to determine ordering.
// keys are replicated in another array so that we do not incur the overhead of touching the memory for segments
// that are uninteresting.
public:
SegmentBTree();
void SwapSegment(uint32 originalKey, SparseArraySegmentBase* oldSeg, SparseArraySegmentBase* newSeg);
template<typename Func>
void Walk(Func& func) const;
protected:
BOOL IsLeaf() const;
BOOL IsFullNode() const;
static void InternalFind(SegmentBTree* node, uint32 itemIndex, SparseArraySegmentBase*& prev, SparseArraySegmentBase*& matchOrNext);
static void SplitChild(Recycler* recycler, SegmentBTree* tree, uint32 count, SegmentBTree* root);
static void InsertNonFullNode(Recycler* recycler, SegmentBTree* tree, SparseArraySegmentBase* newSeg);
};
class SegmentBTreeRoot : public SegmentBTree
{
public:
void Add(Recycler* recycler, SparseArraySegmentBase* newSeg);
void Find(uint itemIndex, SparseArraySegmentBase*& prevOrMatch, SparseArraySegmentBase*& matchOrNext);
Field(SparseArraySegmentBase *) lastUsedSegment;
};
class JavascriptArray : public ArrayObject
{
template <class TPropertyIndex>
friend class ES5ArrayTypeHandlerBase;
public:
static const size_t StackAllocationSize;
private:
static PropertyId const specialPropertyIds[];
protected:
DEFINE_VTABLE_CTOR(JavascriptArray, ArrayObject);
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptArray);
private:
Field(bool) isInitialized;
protected:
Field(SparseArraySegmentBase*) head;
union SegmentUnionType
{
Field(SparseArraySegmentBase*) lastUsedSegment;
Field(SegmentBTreeRoot*) segmentBTreeRoot;
SegmentUnionType() {}
};
Field(SegmentUnionType) segmentUnion;
public:
typedef Var TElement;
static const SparseArraySegmentBase *EmptySegment;
static uint32 const InvalidIndex = 0xFFFFFFFF;
static uint32 const MaxArrayLength = InvalidIndex;
static uint32 const MaxInitialDenseLength=1<<18;
static ushort const MergeSegmentsLengthHeuristics = 128; // If the length is less than MergeSegmentsLengthHeuristics then try to merge the segments
static uint64 const FiftyThirdPowerOfTwoMinusOne = 0x1FFFFFFFFFFFFF; // 2^53-1
static const uint8 AllocationBucketsInfoSize = 3;
// 0th colum in allocationBuckets
static const uint8 AllocationBucketIndex = 0;
// 1st column in allocationBuckets that stores no. of missing elements to initialize for given bucket
static const uint8 MissingElementsCountIndex = 1;
// 2nd column in allocationBuckets that stores allocation size for given bucket
static const uint8 AllocationSizeIndex = 2;
#if defined(_M_X64_OR_ARM64)
static const uint8 AllocationBucketsCount = 3;
#else
static const uint8 AllocationBucketsCount = 2;
#endif
static uint allocationBuckets[AllocationBucketsCount][AllocationBucketsInfoSize];
static const Var MissingItem;
template<typename T> static T GetMissingItem();
SparseArraySegmentBase * GetHead() const { return head; }
SparseArraySegmentBase * GetLastUsedSegment() const;
public:
JavascriptArray(DynamicType * type);
JavascriptArray(uint32 length, uint32 size, DynamicType * type);
JavascriptArray(DynamicType * type, uint32 size);
static Var OP_NewScArray(uint32 argLength, ScriptContext* scriptContext);
static Var OP_NewScArrayWithElements(uint32 argLength, Var *elements, ScriptContext* scriptContext);
static Var OP_NewScArrayWithMissingValues(uint32 argLength, ScriptContext* scriptContext);
static Var OP_NewScIntArray(AuxArray<int32> *ints, ScriptContext* scriptContext);
static Var OP_NewScFltArray(AuxArray<double> *doubles, ScriptContext* scriptContext);
#if ENABLE_PROFILE_INFO
static Var ProfiledNewScArray(uint32 argLength, ScriptContext *scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef);
static Var ProfiledNewScIntArray(AuxArray<int32> *ints, ScriptContext* scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef);
static Var ProfiledNewScFltArray(AuxArray<double> *doubles, ScriptContext* scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef);
static Var ProfiledNewInstanceNoArg(RecyclableObject *function, ScriptContext *scriptContext, ArrayCallSiteInfo *arrayInfo, RecyclerWeakReference<FunctionBody> *weakFuncRef);
#endif
static TypeId OP_SetNativeIntElementC(JavascriptNativeIntArray *arr, uint32 index, Var value, ScriptContext *scriptContext);
static TypeId OP_SetNativeFloatElementC(JavascriptNativeFloatArray *arr, uint32 index, Var value, ScriptContext *scriptContext);
template<typename T> void SetArrayLiteralItem(uint32 index, T value);
void Sort(RecyclableObject* compFn);
template<typename NativeArrayType, typename T> NativeArrayType * ConvertToNativeArrayInPlace(JavascriptArray *varArray);
template <typename T> T GetNativeValue(Var iVal, ScriptContext * scriptContext);
template <> int32 GetNativeValue<int32>(Var iVal, ScriptContext * scriptContext);
template <> double GetNativeValue<double>(Var iVal, ScriptContext * scriptContext);
template<typename T> void ChangeArrayTypeToNativeArray(JavascriptArray * varArray, ScriptContext * scriptContext);
template<> void ChangeArrayTypeToNativeArray<double>(JavascriptArray * varArray, ScriptContext * scriptContext);
template<> void ChangeArrayTypeToNativeArray<int32>(JavascriptArray * varArray, ScriptContext * scriptContext);
template<typename T> inline BOOL DirectGetItemAt(uint32 index, T* outVal);
virtual BOOL DirectGetVarItemAt(uint index, Var* outval, ScriptContext *scriptContext);
virtual BOOL DirectGetItemAtFull(uint index, Var* outVal);
virtual Var DirectGetItem(uint32 index);
Var DirectGetItem(JavascriptString *propName, ScriptContext* scriptContext);
template<typename T> inline void DirectSetItemAt(uint32 itemIndex, T newValue);
template<typename T> inline void DirectSetItemInLastUsedSegmentAt(const uint32 offset, const T newValue);
#if ENABLE_PROFILE_INFO
template<typename T> inline void DirectProfiledSetItemInHeadSegmentAt(const uint32 offset, const T newValue, StElemInfo *const stElemInfo);
#endif
template<typename T> static void CopyValueToSegmentBuferNoCheck(Field(T)* buffer, uint32 length, T value);
template<typename T> void DirectSetItem_Full(uint32 itemIndex, T newValue);
template<typename T> SparseArraySegment<T>* PrepareSegmentForMemOp(uint32 startIndex, uint32 length);
template<typename T> bool DirectSetItemAtRange(uint32 startIndex, uint32 length, T newValue);
template<typename T> bool DirectSetItemAtRangeFull(uint32 startIndex, uint32 length, T newValue);
template<typename T> bool DirectSetItemAtRangeFromArray(uint32 startIndex, uint32 length, JavascriptArray *fromArray, uint32 fromStartIndex);
#if DBG
template <typename T> void VerifyNotNeedMarshal(T value) {};
template <> void VerifyNotNeedMarshal<Var>(Var value) { Assert(value == JavascriptArray::MissingItem || !CrossSite::NeedMarshalVar(value, this->GetScriptContext())); }
#endif
void DirectSetItemIfNotExist(uint32 index, Var newValue);
template<typename T> BOOL DirectDeleteItemAt(uint32 itemIndex);
virtual DescriptorFlags GetItemSetter(uint32 index, Var* setterValue, ScriptContext* requestContext) override
{
Var value = nullptr;
return this->DirectGetItemAt(index, &value) ? WritableData : None;
}
static bool Is(Var aValue);
static bool Is(TypeId typeId);
static JavascriptArray* FromVar(Var aValue);
static bool IsVarArray(Var aValue);
static bool IsVarArray(TypeId typeId);
static JavascriptArray* FromAnyArray(Var aValue);
static bool IsDirectAccessArray(Var aValue);
void SetLength(uint32 newLength);
BOOL SetLength(Var newLength);
virtual void ClearElements(SparseArraySegmentBase *seg, uint32 newSegmentLength);
class EntryInfo
{
public:
static FunctionInfo NewInstance;
static FunctionInfo Concat;
static FunctionInfo Every;
static FunctionInfo Filter;
static FunctionInfo ForEach;
static FunctionInfo IndexOf;
static FunctionInfo Includes;
static FunctionInfo Join;
static FunctionInfo LastIndexOf;
static FunctionInfo Map;
static FunctionInfo Pop;
static FunctionInfo Push;
static FunctionInfo Reduce;
static FunctionInfo ReduceRight;
static FunctionInfo Reverse;
static FunctionInfo Shift;
static FunctionInfo Slice;
static FunctionInfo Some;
static FunctionInfo Sort;
static FunctionInfo Splice;
static FunctionInfo ToString;
static FunctionInfo ToLocaleString;
static FunctionInfo Unshift;
static FunctionInfo IsArray;
static FunctionInfo Find;
static FunctionInfo FindIndex;
static FunctionInfo Entries;
static FunctionInfo Keys;
static FunctionInfo Values;
static FunctionInfo CopyWithin;
static FunctionInfo Fill;
static FunctionInfo From;
static FunctionInfo Of;
static FunctionInfo GetterSymbolSpecies;
};
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
static Var NewInstance(RecyclableObject* function, Arguments args);
static Var ProfiledNewInstance(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryConcat(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryEvery(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFilter(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryForEach(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryIndexOf(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryIncludes(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryJoin(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryLastIndexOf(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryMap(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryPop(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryPush(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryReduce(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryReduceRight(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryReverse(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryShift(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySlice(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySome(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySort(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntrySplice(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToString(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryToLocaleString(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryUnshift(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryIsArray(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFind(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFindIndex(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryEntries(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryKeys(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryValues(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryCopyWithin(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFill(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryFrom(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryOf(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...);
static Var Push(ScriptContext * scriptContext, Var object, Var value);
static Var EntryPushNonJavascriptArray(ScriptContext * scriptContext, Var * args, uint argCount);
static Var EntryPushJavascriptArray(ScriptContext * scriptContext, Var * args, uint argCount);
static Var EntryPushJavascriptArrayNoFastPath(ScriptContext * scriptContext, Var * args, uint argCount);
static Var Pop(ScriptContext * scriptContext, Var object);
static Var EntryPopJavascriptArray(ScriptContext * scriptContext, Var object);
static Var EntryPopNonJavascriptArray(ScriptContext * scriptContext, Var object);
#if DEBUG
static BOOL GetIndex(const char16* propName, uint32 *pIndex);
#endif
uint32 GetNextIndex(uint32 index) const;
template<typename T> uint32 GetNextIndexHelper(uint32 index) const;
#ifdef VALIDATE_ARRAY
virtual void ValidateArray();
void ValidateArrayCommon();
template<typename T> static void ValidateSegment(SparseArraySegment<T>* seg);
static void ValidateVarSegment(SparseArraySegment<Var>* seg);
#endif
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
void CheckForceES5Array();
#endif
virtual PropertyQueryFlags HasPropertyQuery(PropertyId propertyId) override;
virtual BOOL DeleteProperty(PropertyId propertyId, PropertyOperationFlags flags) override;
virtual BOOL DeleteProperty(JavascriptString *propertyNameString, PropertyOperationFlags flags) override;
virtual BOOL IsEnumerable(PropertyId propertyId) override;
virtual BOOL IsConfigurable(PropertyId propertyId) override;
virtual BOOL SetEnumerable(PropertyId propertyId, BOOL value) override;
virtual BOOL SetWritable(PropertyId propertyId, BOOL value) override;
virtual BOOL SetConfigurable(PropertyId propertyId, BOOL value) override;
virtual BOOL SetAttributes(PropertyId propertyId, PropertyAttributes attributes) override;
virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual PropertyQueryFlags GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext);
virtual BOOL SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override;
virtual BOOL SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override;
virtual BOOL SetPropertyWithAttributes(PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags = PropertyOperation_None, SideEffects possibleSideEffects = SideEffects_Any) override;
virtual PropertyQueryFlags HasItemQuery(uint32 index) override;
virtual PropertyQueryFlags GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext) override;
virtual PropertyQueryFlags GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext) override;
virtual BOOL SetItem(uint32 index, Var value, PropertyOperationFlags flags) override;
virtual BOOL DeleteItem(uint32 index, PropertyOperationFlags flags) override;
virtual BOOL SetAccessors(PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags) override;
virtual BOOL PreventExtensions() override;
virtual BOOL Seal() override;
virtual BOOL Freeze() override;
virtual BOOL GetEnumerator(JavascriptStaticEnumerator * enumerator, EnumeratorFlags flags, ScriptContext* requestContext, ForInCache * forInCache = nullptr) override;
virtual BOOL GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
virtual BOOL GetSpecialPropertyName(uint32 index, JavascriptString ** propertyName, ScriptContext * requestContext) override;
virtual uint GetSpecialPropertyCount() const override;
virtual PropertyId const * GetSpecialPropertyIds() const override;
virtual DescriptorFlags GetSetter(PropertyId propertyId, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override;
virtual DescriptorFlags GetSetter(JavascriptString* propertyNameString, Var *setterValue, PropertyValueInfo* info, ScriptContext* requestContext) override;
// objectArray support
virtual BOOL SetItemWithAttributes(uint32 index, Var value, PropertyAttributes attributes) override;
virtual BOOL SetItemAttributes(uint32 index, PropertyAttributes attributes) override;
virtual BOOL SetItemAccessors(uint32 index, Var getter, Var setter) override;
virtual BOOL IsObjectArrayFrozen() override;
virtual JavascriptEnumerator * GetIndexEnumerator(EnumeratorFlags flags, ScriptContext* requestContext) override;
// Get non-index enumerator for SCA
BOOL GetNonIndexEnumerator(JavascriptStaticEnumerator * enumerator, ScriptContext* requestContext);
virtual BOOL IsItemEnumerable(uint32 index);
template<typename Func>
void WalkExisting(Func func)
{
Assert(!JavascriptNativeIntArray::Is(this) && !JavascriptNativeFloatArray::Is(this));
ArrayElementEnumerator e(this, 0);
while(e.MoveNext<Var>())
{
func(e.GetIndex(), e.GetItem<Var>());
}
}
static JavascriptArray* CreateArrayFromConstructor(RecyclableObject* constructor, uint32 length, ScriptContext* scriptContext);
static JavascriptArray* CreateArrayFromConstructorNoArg(RecyclableObject* constructor, ScriptContext* scriptContext);
template<typename unitType, typename className>
static className* New(Recycler* recycler, DynamicType* arrayType);
template<typename unitType, typename className, uint inlineSlots>
static className* New(uint32 length, DynamicType* arrayType, Recycler* recycler);
template<typename unitType, typename className, uint inlineSlots>
static className* NewLiteral(uint32 length, DynamicType* arrayType, Recycler* recycler);
#if ENABLE_COPYONACCESS_ARRAY
template<typename unitType, typename className, uint inlineSlots>
static className* NewCopyOnAccessLiteral(DynamicType* arrayType, ArrayCallSiteInfo *arrayInfo, FunctionBody *functionBody, const Js::AuxArray<int32> *ints, Recycler* recycler);
#endif
static bool HasInlineHeadSegment(uint32 length);
template<class T, uint InlinePropertySlots>
static T *New(void *const stackAllocationPointer, const uint32 length, DynamicType *const arrayType);
template<class T, uint InlinePropertySlots>
static T *NewLiteral(void *const stackAllocationPointer, const uint32 length, DynamicType *const arrayType);
static JavascriptArray *EnsureNonNativeArray(JavascriptArray *arr);
#if ENABLE_PROFILE_INFO
virtual JavascriptArray *FillFromArgs(uint length, uint start, Var *args, ArrayCallSiteInfo *info = nullptr, bool dontCreateNewArray = false);
#else
virtual JavascriptArray *FillFromArgs(uint length, uint start, Var *args, bool dontCreateNewArray = false);
#endif
protected:
// Use static New methods to create array.
JavascriptArray(uint32 length, DynamicType * type);
// For BoxStackInstance
JavascriptArray(JavascriptArray * instance, bool boxHead);
template<typename T> inline void LinkSegments(SparseArraySegment<T>* prev, SparseArraySegment<T>* current);
template<typename T> inline SparseArraySegment<T>* ReallocNonLeafSegment(SparseArraySegment<T>* seg, SparseArraySegmentBase* nextSeg, bool forceNonLeaf = false);
void TryAddToSegmentMap(Recycler* recycler, SparseArraySegmentBase* seg);
private:
DynamicObjectFlags GetFlags() const;
DynamicObjectFlags GetFlags_Unchecked() const; // do not use except in extreme circumstances
void SetFlags(const DynamicObjectFlags flags);
void LinkSegmentsCommon(SparseArraySegmentBase* prev, SparseArraySegmentBase* current);
public:
static JavascriptArray *GetArrayForArrayOrObjectWithArray(const Var var);
static JavascriptArray *GetArrayForArrayOrObjectWithArray(const Var var, bool *const isObjectWithArrayRef, TypeId *const arrayTypeIdRef);
static const SparseArraySegmentBase *Jit_GetArrayHeadSegmentForArrayOrObjectWithArray(const Var var);
static uint32 Jit_GetArrayHeadSegmentLength(const SparseArraySegmentBase *const headSegment);
static bool Jit_OperationInvalidatedArrayHeadSegment(const SparseArraySegmentBase *const headSegmentBeforeOperation, const uint32 headSegmentLengthBeforeOperation, const Var varAfterOperation);
static uint32 Jit_GetArrayLength(const Var var);
static bool Jit_OperationInvalidatedArrayLength(const uint32 lengthBeforeOperation, const Var varAfterOperation);
static DynamicObjectFlags Jit_GetArrayFlagsForArrayOrObjectWithArray(const Var var);
static bool Jit_OperationCreatedFirstMissingValue(const DynamicObjectFlags flagsBeforeOperation, const Var varAfterOperation);
public:
bool HasNoMissingValues() const; // if true, the head segment has no missing values
bool HasNoMissingValues_Unchecked() const; // do not use except in extreme circumstances
void SetHasNoMissingValues(const bool hasNoMissingValues = true);
template<typename T>
bool IsMissingItemAt(uint32 index) const;
bool IsMissingItem(uint32 index);
virtual bool IsMissingHeadSegmentItem(const uint32 index) const;
static VTableValue VtableHelper()
{
return VTableValue::VtableJavascriptArray;
}
static LibraryValue InitialTypeHelper()
{
return LibraryValue::ValueJavascriptArrayType;
}
static DynamicType * GetInitialType(ScriptContext * scriptContext);
public:
static uint32 defaultSmallSegmentAlignedSize;
template<typename unitType, typename classname>
inline BOOL TryGrowHeadSegmentAndSetItem(uint32 indexInt, unitType iValue);
static int64 GetIndexFromVar(Js::Var arg, int64 length, ScriptContext* scriptContext);
template <typename T>
static Var MapHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
template <typename T>
static Var MapObjectHelper(RecyclableObject* obj, T length, T start, RecyclableObject* newObj, JavascriptArray* newArr,
bool isBuiltinArrayCtor, RecyclableObject* callBackFn, Var thisArg, ScriptContext* scriptContext);
static Var FillHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext);
static Var CopyWithinHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext);
template <typename T>
static BOOL GetParamForIndexOf(T length, Arguments const & args, Var& search, T& fromIndex, ScriptContext * scriptContext);
static BOOL GetParamForLastIndexOf(int64 length, Arguments const & args, Var& search, int64& fromIndex, ScriptContext * scriptContext);
template <bool includesAlgorithm, typename T, typename P = uint32>
static Var TemplatedIndexOfHelper(T* pArr, Var search, P fromIndex, P toIndex, ScriptContext * scriptContext);
template <typename T>
static Var LastIndexOfHelper(T* pArr, Var search, int64 fromIndex, ScriptContext * scriptContext);
template <typename T>
static BOOL TemplatedGetItem(T *pArr, uint32 index, Var * element, ScriptContext * scriptContext, bool checkHasItem = true);
template <typename T>
static BOOL TemplatedGetItem(T *pArr, uint64 index, Var * element, ScriptContext * scriptContext, bool checkHasItem = true);
template <typename T = uint32>
static Var ReverseHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, ScriptContext* scriptContext);
template <typename T = uint32>
static Var SliceHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
static Var SliceObjectHelper(RecyclableObject* obj, uint32 sliceStart, uint32 start, JavascriptArray* newArr, RecyclableObject* newObj, uint32 newLen, ScriptContext* scriptContext);
template <typename T = uint32>
static Var EveryHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
template <typename T = uint32>
static Var EveryObjectHelper(RecyclableObject* obj, T length, T start, RecyclableObject* callBackFn, Var thisArg, ScriptContext* scriptContext);
template <typename T = uint32>
static Var SomeHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
template <typename T = uint32>
static Var SomeObjectHelper(RecyclableObject* obj, T length, T start, RecyclableObject* callBackFn, Var thisArg, ScriptContext* scriptContext);
template <bool findIndex>
static Var FindHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, int64 length, Arguments& args, ScriptContext* scriptContext);
template <bool findIndex>
static Var FindObjectHelper(RecyclableObject* obj, int64 length, int64 start, RecyclableObject* callBackFn, Var thisArg, ScriptContext* scriptContext);
template <typename T = uint32>
static Var ReduceHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
template <typename T>
static Var ReduceObjectHelper(RecyclableObject* obj, T length, T start, RecyclableObject* callBackFn, Var accumulator, ScriptContext* scriptContext);
template <typename T>
static Var FilterHelper(JavascriptArray* pArr, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
template <typename T>
static Var FilterObjectHelper(RecyclableObject* obj, T length, T start, JavascriptArray* newArr, RecyclableObject* newObj, T newStart,
RecyclableObject* callBackFn, Var thisArg, ScriptContext* scriptContext);
template <typename T = uint32>
static Var ReduceRightHelper(JavascriptArray* pArr, Js::TypedArrayBase* typedArrayBase, RecyclableObject* obj, T length, Arguments& args, ScriptContext* scriptContext);
template <typename T>
static Var ReduceRightObjectHelper(RecyclableObject* obj, T length, T start, RecyclableObject* callBackFn, Var accumulator, ScriptContext* scriptContext);
static Var OfHelper(bool isTypedArrayEntryPoint, Arguments& args, ScriptContext* scriptContext);
template<typename T>
static T GetFromIndex(Var arg, T length, ScriptContext *scriptContext, bool addWithLength = true)
{
T fromIndex = 0;
double value = TaggedInt::Is(arg) ? (double)TaggedInt::ToInt64(arg) : JavascriptConversion::ToInteger(arg, scriptContext);
if (value < 0)
{
fromIndex = addWithLength ? (T)max(0i64, (int64)(value + length)) : 0;
}
else
{
fromIndex = (T)min(value, (double)length);
}
return fromIndex;
}
protected:
template<class T> bool IsMissingHeadSegmentItemImpl(const uint32 index) const;
SegmentBTreeRoot * GetSegmentMap() const;
void SetHeadAndLastUsedSegment(SparseArraySegmentBase * segment);
void SetLastUsedSegment(SparseArraySegmentBase * segment);
bool HasSegmentMap() const;
private:
void SetSegmentMap(SegmentBTreeRoot * segmentMap);
void ClearSegmentMap();
template <typename Fn> SparseArraySegmentBase * ForEachSegment(Fn fn) const;
template <typename Fn> static SparseArraySegmentBase * ForEachSegment(SparseArraySegmentBase * segment, Fn fn);
template<typename T> bool NeedScanForMissingValuesUponSetItem(SparseArraySegment<T> *const segment, const uint32 offset) const;
template<typename T> void ScanForMissingValues(const uint startIndex = 0);
template<typename T> bool ScanForMissingValues(const uint startIndex, const uint endIndex);
template<typename T, uint InlinePropertySlots> static SparseArraySegment<typename T::TElement> *InitArrayAndHeadSegment(T *const array, const uint32 length, const uint32 size, const bool wasZeroAllocated);
template<typename T> static void SliceHelper(JavascriptArray*pArr, JavascriptArray* pNewArr, uint32 start, uint32 newLen);
template<typename T>
static void ShiftHelper(JavascriptArray* pArr, ScriptContext * scriptContext);
template<typename T>
static void UnshiftHelper(JavascriptArray* pArr, uint32 unshiftElements, Js::Var * elements);
template<typename T>
static void GrowArrayHeadHelperForUnshift(JavascriptArray* pArr, uint32 unshiftElements, ScriptContext * scriptContext);
static int64 GetFromLastIndex(Var arg, int64 length, ScriptContext *scriptContext);
static JavascriptString* JoinToString(Var value, ScriptContext* scriptContext);
static JavascriptString* JoinHelper(Var thisArg, JavascriptString* separatorStr, ScriptContext* scriptContext);
template <typename T>
static JavascriptString* JoinArrayHelper(T * arr, JavascriptString* separatorStr, ScriptContext* scriptContext);
static JavascriptString* JoinOtherHelper(RecyclableObject *object, JavascriptString* separatorStr, ScriptContext* scriptContext);
template <bool includesAlgorithm>
static Var IndexOfHelper(Arguments const & args, ScriptContext *scriptContext);
virtual int32 HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext);
template<typename T>
static void CopyHeadIfInlinedHeadSegment(JavascriptArray *array, Recycler *recycler);
template<typename T>
static void ReallocateNonLeafLastSegmentIfLeaf(JavascriptArray * arr, Recycler * recycler);
template<typename T>
static void ArraySpliceHelper(JavascriptArray* pNewArr, JavascriptArray* pArr, uint32 start, uint32 deleteLen,
Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext);
template<typename T>
static void ArraySegmentSpliceHelper(JavascriptArray *pnewArr, SparseArraySegment<T> *seg, SparseArraySegment<T> **prev, uint32 start, uint32 deleteLen,
Var* insertArgs, uint32 insertLen, Recycler *recycler);
template<typename T>
static RecyclableObject* ObjectSpliceHelper(RecyclableObject* pObj, T len, T start, T deleteLen,
Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext, RecyclableObject* pNewObj = nullptr);
static JavascriptString* ToLocaleStringHelper(Var value, ScriptContext* scriptContext);
static Js::JavascriptArray* CreateNewArrayHelper(uint32 len, bool isIntArray, bool isFloatArray, Js::JavascriptArray *baseArray, ScriptContext* scriptContext);
static Var TryArraySplice(JavascriptArray* pArr, uint32 start, uint32 len, uint32 deleteLen,
Var* insertArgs, uint32 insertLen, ScriptContext *scriptContext);
void FillFromPrototypes(uint32 startIndex, uint32 endIndex);
bool IsFillFromPrototypes();
void GetArrayTypeAndConvert(bool* isIntArray, bool* isFloatArray);
template<typename T> void EnsureHeadStartsFromZero(Recycler * recycler);
SparseArraySegmentBase * GetBeginLookupSegment(uint32 index, const bool useSegmentMap = true) const;
SegmentBTreeRoot * BuildSegmentMap();
void InvalidateLastUsedSegment();
inline BOOL IsFullArray() const; // no missing elements till array length
inline BOOL IsSingleSegmentArray() const;
template<typename T> void AllocateHead();
template<typename T> void EnsureHead();
uint32 sort(__inout_ecount(*length) Field(Var) *orig, uint32 *length, ScriptContext *scriptContext);
BOOL GetPropertyBuiltIns(PropertyId propertyId, Var* value);
bool GetSetterBuiltIns(PropertyId propertyId, PropertyValueInfo* info, DescriptorFlags* descriptorFlags);
private:
struct Element
{
Field(Var) Value;
Field(JavascriptString*) StringValue;
};
static int __cdecl CompareElements(void* context, const void* elem1, const void* elem2);
void SortElements(Element* elements, uint32 left, uint32 right);
template <typename Fn>
static void ForEachOwnMissingArrayIndexOfObject(JavascriptArray *baseArr, JavascriptArray *destArray, RecyclableObject* obj, uint32 startIndex, uint32 limitIndex, uint32 destIndex, Fn fn);
// NativeArrays may change it's content type, but not others
template <typename T> static bool MayChangeType() { return false; }
template<typename T, typename P>
static BOOL TryTemplatedGetItem(T *arr, P index, Var *element, ScriptContext *scriptContext, bool checkHasItem = true)
{
return T::Is(arr) ? JavascriptArray::TemplatedGetItem(arr, index, element, scriptContext, checkHasItem) :
JavascriptOperators::GetItem(arr, index, element, scriptContext);
}
template <bool hasSideEffect, typename T, typename Fn>
static void TemplatedForEachItemInRange(T * arr, uint32 startIndex, uint32 limitIndex, Var missingItem, ScriptContext * scriptContext, Fn fn)
{
for (uint32 i = startIndex; i < limitIndex; i++)
{
Var element;
fn(i, TryTemplatedGetItem(arr, i, &element, scriptContext) ? element : missingItem);
if (hasSideEffect && MayChangeType<T>() && !T::Is(arr))
{
// The function has changed, go to another ForEachItemInRange. It is possible that the array might have changed to
// an ES5Array, in such cases we don't need to call the JavascriptArray specific implementation.
if (JavascriptArray::Is(arr))
{
JavascriptArray::FromVar(arr)->template ForEachItemInRange<true>(i + 1, limitIndex, missingItem, scriptContext, fn);
return;
}
else
{
AssertOrFailFastMsg(ES5Array::Is(arr), "The array should have been converted to an ES5Array");
}
}
}
}
template <bool hasSideEffect, typename T, typename P, typename Fn>
static void TemplatedForEachItemInRange(T * arr, P startIndex, P limitIndex, ScriptContext * scriptContext, Fn fn)
{
for (P i = startIndex; i < limitIndex; i++)
{
Var element;
if (TryTemplatedGetItem(arr, i, &element, scriptContext))
{
fn(i, element);
if (hasSideEffect && MayChangeType<T>() && !T::Is(arr))
{
// The function has changed, go to another ForEachItemInRange. It is possible that the array might have changed to
// an ES5Array, in such cases we don't need to call the JavascriptArray specific implementation.
if (JavascriptArray::Is(arr))
{
JavascriptArray::FromVar(arr)->template ForEachItemInRange<true>(i + 1, limitIndex, scriptContext, fn);
return;
}
else
{
AssertOrFailFastMsg(ES5Array::Is(arr), "The array should have been converted to an ES5Array");
}
}
}
}
}
public:
template <bool hasSideEffect, typename Fn>
void ForEachItemInRange(uint64 startIndex, uint64 limitIndex, ScriptContext * scriptContext, Fn fn)
{
Assert(false);
Throw::InternalError();
}
template <bool hasSideEffect, typename Fn>
void ForEachItemInRange(uint32 startIndex, uint32 limitIndex, ScriptContext * scriptContext, Fn fn)
{
switch (this->GetTypeId())
{
case TypeIds_Array:
TemplatedForEachItemInRange<hasSideEffect>(this, startIndex, limitIndex, scriptContext, fn);
break;
case TypeIds_NativeIntArray:
TemplatedForEachItemInRange<hasSideEffect>(JavascriptNativeIntArray::FromVar(this), startIndex, limitIndex, scriptContext, fn);
break;
case TypeIds_NativeFloatArray:
TemplatedForEachItemInRange<hasSideEffect>(JavascriptNativeFloatArray::FromVar(this), startIndex, limitIndex, scriptContext, fn);
break;
default:
Assert(false);
break;
}
}
template <bool hasSideEffect, typename Fn>
void ForEachItemInRange(uint32 startIndex, uint32 limitIndex, Var missingItem, ScriptContext * scriptContext, Fn fn)
{
switch (this->GetTypeId())
{
case TypeIds_Array:
TemplatedForEachItemInRange<hasSideEffect>(this, startIndex, limitIndex, missingItem, scriptContext, fn);
break;
case TypeIds_NativeIntArray:
TemplatedForEachItemInRange<hasSideEffect>(JavascriptNativeIntArray::FromVar(this), startIndex, limitIndex, missingItem, scriptContext, fn);
break;
case TypeIds_NativeFloatArray:
TemplatedForEachItemInRange<hasSideEffect>(JavascriptNativeFloatArray::FromVar(this), startIndex, limitIndex, missingItem, scriptContext, fn);
break;
default:
Assert(false);
break;
}
}
// ArrayElementEnumerator walks an array's segments and enumerates the elements in order.
class ArrayElementEnumerator
{
private:
SparseArraySegmentBase* seg;
uint32 index, endIndex;
const uint32 start, end;
public:
ArrayElementEnumerator(JavascriptArray* arr, uint32 start = 0, uint32 end = MaxArrayLength);
template<typename T> bool MoveNext();
uint32 GetIndex() const;
template<typename T> T GetItem() const;
private:
void Init(JavascriptArray* arr);
};
template <typename T>
class IndexTrace
{
public:
static Var ToNumber(const T& index, ScriptContext* scriptContext);
// index on JavascriptArray
static BOOL GetItem(JavascriptArray* arr, const T& index, Var* outVal);
static BOOL SetItem(JavascriptArray* arr, const T& index, Var newValue);
static void SetItemIfNotExist(JavascriptArray* arr, const T& index, Var newValue);
static BOOL DeleteItem(JavascriptArray* arr, const T& index);
// index on RecyclableObject
static BOOL SetItem(RecyclableObject* obj, const T& index, Var newValue, PropertyOperationFlags flags = PropertyOperation_None);
static BOOL DeleteItem(RecyclableObject* obj, const T& index, PropertyOperationFlags flags = PropertyOperation_None);
};
// BigIndex represents a general index which may grow larger than uint32.
class BigIndex
{
private:
uint32 index;
uint64 bigIndex;
typedef IndexTrace<uint32> small_index;
public:
BigIndex(uint32 initIndex = 0);
BigIndex(uint64 initIndex);
bool IsSmallIndex() const;
bool IsUint32Max() const;
uint32 GetSmallIndex() const;
uint64 GetBigIndex() const;
Var ToNumber(ScriptContext* scriptContext) const;
const BigIndex& operator++();
const BigIndex& operator--();
BigIndex operator+(const BigIndex& delta) const;
BigIndex operator+(uint32 delta) const;
bool operator==(const BigIndex& rhs) const;
bool operator> (const BigIndex& rhs) const;
bool operator< (const BigIndex& rhs) const;
bool operator<=(const BigIndex& rhs) const;
bool operator>=(const BigIndex& rhs) const;
BOOL GetItem(JavascriptArray* arr, Var* outVal) const;
BOOL SetItem(JavascriptArray* arr, Var newValue) const;
void SetItemIfNotExist(JavascriptArray* arr, Var newValue) const;
BOOL DeleteItem(JavascriptArray* arr) const;
BOOL SetItem(RecyclableObject* obj, Var newValue, PropertyOperationFlags flags = PropertyOperation_None) const;
BOOL DeleteItem(RecyclableObject* obj, PropertyOperationFlags flags = PropertyOperation_None) const;
};
void GenericDirectSetItemAt(const BigIndex& index, Var newValue) { index.SetItem(this, newValue); }
void GenericDirectSetItemAt(const uint32 index, Var newValue) { this->DirectSetItemAt(index, newValue); }
void DirectSetItemIfNotExist(const BigIndex& index, Var newValue) { index.SetItemIfNotExist(this, newValue); }
void DirectAppendItem(Var newValue) { BigIndex(this->GetLength()).SetItem(this, newValue); }
void TruncateToProperties(const BigIndex& index, uint32 start);
static void InternalCopyArrayElements(JavascriptArray* dstArray, uint32 dstIndex, JavascriptArray* srcArray, uint32 start, uint32 end);
static void InternalCopyNativeFloatArrayElements(JavascriptArray* dstArray, const uint32 dstIndex, JavascriptNativeFloatArray* srcArray, uint32 start, uint32 end);
static void InternalCopyNativeIntArrayElements(JavascriptArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray* srcArray, uint32 start, uint32 end);
static void InternalFillFromPrototype(JavascriptArray *dstArray, const uint32 dstIndex, JavascriptArray *srcArray, uint32 start, uint32 end, uint32 count);
static void CopyArrayElements(JavascriptArray* dstArray, uint32 dstIndex, JavascriptArray* srcArray, uint32 start = 0, uint32 end = MaxArrayLength);
template <typename T>
static void CopyAnyArrayElementsToVar(JavascriptArray* dstArray, T dstIndex, JavascriptArray* srcArray, uint32 start = 0, uint32 end = MaxArrayLength);
static bool CopyNativeIntArrayElements(JavascriptNativeIntArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray *srcArray, uint32 start = 0, uint32 end = MaxArrayLength);
static bool CopyNativeIntArrayElementsToFloat(JavascriptNativeFloatArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray *srcArray, uint32 start = 0, uint32 end = MaxArrayLength);
static void CopyNativeIntArrayElementsToVar(JavascriptArray* dstArray, uint32 dstIndex, JavascriptNativeIntArray *srcArray, uint32 start = 0, uint32 end = MaxArrayLength);
static bool CopyNativeFloatArrayElements(JavascriptNativeFloatArray* dstArray, uint32 dstIndex, JavascriptNativeFloatArray *srcArray, uint32 start = 0, uint32 end = MaxArrayLength);
static void CopyNativeFloatArrayElementsToVar(JavascriptArray* dstArray, uint32 dstIndex, JavascriptNativeFloatArray *srcArray, uint32 start = 0, uint32 end = MaxArrayLength);
static bool BoxConcatItem(Var aItem, uint idxArg, ScriptContext *scriptContext);
template<typename T>
static void SetConcatItem(Var aItem, uint idxArg, JavascriptArray* pDestArray, RecyclableObject* pDestObj, T idxDest, ScriptContext *scriptContext);
template<typename T>
static void ConcatArgs(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, uint start, BigIndex startIdxDest, BOOL firstPromotedItemIsSpreadable, BigIndex firstPromotedItemLength, bool spreadableCheckedAndTrue = false);
template<typename T>
static void ConcatArgs(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, uint start = 0, uint startIdxDest = 0u, BOOL FirstPromotedItemIsSpreadable = false, BigIndex FirstPromotedItemLength = 0u, bool spreadableCheckedAndTrue = false);
static JavascriptArray* ConcatIntArgs(JavascriptNativeIntArray* pDestArray, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext);
static bool PromoteToBigIndex(BigIndex lhs, BigIndex rhs);
static bool PromoteToBigIndex(BigIndex lhs, uint32 rhs);
static JavascriptArray* ConcatFloatArgs(JavascriptNativeFloatArray* pDestArray, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext);
private:
template<typename T=uint32>
static RecyclableObject* ArraySpeciesCreate(Var pThisArray, T length, ScriptContext* scriptContext, bool *pIsIntArray = nullptr, bool *pIsFloatArray = nullptr, bool *pIsBuiltinArrayCtor = nullptr);
template <typename T, typename R> static R ConvertToIndex(T idxDest, ScriptContext* scriptContext) { Throw::InternalError(); return 0; }
static BOOL SetArrayLikeObjects(RecyclableObject* pDestObj, uint32 idxDest, Var aItem);
static BOOL SetArrayLikeObjects(RecyclableObject* pDestObj, BigIndex idxDest, Var aItem);
static void ConcatArgsCallingHelper(RecyclableObject* pDestObj, TypeId* remoteTypeIds, Js::Arguments& args, ScriptContext* scriptContext, ::Math::RecordOverflowPolicy &destLengthOverflow);
static void ThrowErrorOnFailure(BOOL succeeded, ScriptContext* scriptContext, uint32 index);
static void ThrowErrorOnFailure(BOOL succeeded, ScriptContext* scriptContext, BigIndex index);
template<typename T>
static void TryGetArrayAndLength(Var arg, ScriptContext *scriptContext, PCWSTR methodName, __out JavascriptArray** array, __out RecyclableObject** obj, __out T * length);
static uint64 OP_GetLength(Var obj, ScriptContext *scriptContext);
public:
template<typename T, typename P = uint32>
static void Unshift(RecyclableObject* obj, const T& toIndex, P start, P end, ScriptContext* scriptContext);
template <typename T>
class ItemTrace
{
public:
static uint32 GetLength(T* obj, ScriptContext* scriptContext);
static BOOL GetItem(T* obj, uint32 index, Var* outVal, ScriptContext* scriptContext);
};
template <typename T>
static JavascriptString* ToLocaleString(T* obj, ScriptContext* scriptContext);
static JavascriptString* GetLocaleSeparator(ScriptContext* scriptContext);
public:
static uint32 GetOffsetOfArrayFlags() { return offsetof(JavascriptArray, arrayFlags); }
static uint32 GetOffsetOfHead() { return offsetof(JavascriptArray, head); }
static uint32 GetOffsetOfLastUsedSegmentOrSegmentMap() { return offsetof(JavascriptArray, segmentUnion.lastUsedSegment); }
static Var SpreadArrayArgs(Var arrayToSpread, const Js::AuxArray<uint32> *spreadIndices, ScriptContext *scriptContext);
static uint32 GetSpreadArgLen(Var spreadArg, ScriptContext *scriptContext);
static JavascriptArray * BoxStackInstance(JavascriptArray * instance);
protected:
template <typename T> void InitBoxedInlineHeadSegment(SparseArraySegment<T> * dst, SparseArraySegment<T> * src);
template <typename T> static T * BoxStackInstance(T * instance);
public:
template<class T, uint InlinePropertySlots> static size_t DetermineAllocationSize(const uint inlineElementSlots, size_t *const allocationPlusSizeRef = nullptr, uint *const alignedInlineElementSlotsRef = nullptr);
template<class ArrayType, uint InlinePropertySlots> static size_t DetermineAllocationSizeForArrayObjects(const uint inlineElementSlots, size_t *const allocationPlusSizeRef = nullptr, uint *const alignedInlineElementSlotsRef = nullptr);
template<class ArrayType> static void EnsureCalculationOfAllocationBuckets();
template<class T, uint InlinePropertySlots> static uint DetermineAvailableInlineElementSlots(const size_t allocationSize, bool *const isSufficientSpaceForInlinePropertySlotsRef);
template<class T, uint ConstInlinePropertySlots, bool UseDynamicInlinePropertySlots> static SparseArraySegment<typename T::TElement> *DetermineInlineHeadSegmentPointer(T *const array);
#if ENABLE_TTD
public:
virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override;
virtual void ProcessCorePaths() override;
virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
#endif
public:
virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
{
return VtableHelper();
}
};
// Ideally we would propagate the throw flag setting of true from the array operations down to the [[Delete]]/[[Put]]/... methods. But that is a big change
// so we are checking for failure on DeleteProperty/DeleteItem/... etc instead. This helper makes that checking a little less intrusive.
class ThrowTypeErrorOnFailureHelper
{
ScriptContext *m_scriptContext;
PCWSTR m_functionName;
public:
ThrowTypeErrorOnFailureHelper(ScriptContext *scriptContext, PCWSTR functionName) : m_scriptContext(scriptContext), m_functionName(functionName) {}
inline void ThrowTypeErrorOnFailure(BOOL operationSucceeded);
inline void ThrowTypeErrorOnFailure();
inline BOOL IsThrowTypeError(BOOL operationSucceeded);
};
class JavascriptNativeArray : public JavascriptArray
{
friend class JavascriptArray;
protected:
DEFINE_VTABLE_CTOR(JavascriptNativeArray, JavascriptArray);
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptNativeArray);
public:
JavascriptNativeArray(DynamicType * type) :
JavascriptArray(type), weakRefToFuncBody(nullptr)
{
}
protected:
JavascriptNativeArray(uint32 length, DynamicType * type) :
JavascriptArray(length, type), weakRefToFuncBody(nullptr) {}
// For BoxStackInstance
JavascriptNativeArray(JavascriptNativeArray * instance);
Field(RecyclerWeakReference<FunctionBody> *) weakRefToFuncBody;
public:
static bool Is(Var aValue);
static bool Is(TypeId typeId);
static JavascriptNativeArray* FromVar(Var aValue);
void SetArrayCallSite(ProfileId index, RecyclerWeakReference<FunctionBody> *weakRef)
{
Assert(weakRef);
Assert(!weakRefToFuncBody);
SetArrayCallSiteIndex(index);
weakRefToFuncBody = weakRef;
}
void ClearArrayCallSiteIndex()
{
weakRefToFuncBody = nullptr;
}
#if ENABLE_PROFILE_INFO
ArrayCallSiteInfo *GetArrayCallSiteInfo();
#endif
static uint32 GetOffsetOfArrayCallSiteIndex() { return offsetof(JavascriptNativeArray, arrayCallSiteIndex); }
static uint32 GetOffsetOfWeakFuncRef() { return offsetof(JavascriptNativeArray, weakRefToFuncBody); }
#if ENABLE_PROFILE_INFO
void SetArrayProfileInfo(RecyclerWeakReference<FunctionBody> *weakRef, ArrayCallSiteInfo *arrayInfo);
void CopyArrayProfileInfo(Js::JavascriptNativeArray* baseArray);
#endif
Var FindMinOrMax(Js::ScriptContext * scriptContext, bool findMax);
template<typename T, bool checkNaNAndNegZero> Var FindMinOrMax(Js::ScriptContext * scriptContext, bool findMax); // NativeInt arrays can't have NaNs or -0
static void PopWithNoDst(Var nativeArray);
};
class JavascriptNativeFloatArray;
class JavascriptNativeIntArray : public JavascriptNativeArray
{
friend class JavascriptArray;
public:
static const size_t StackAllocationSize;
protected:
DEFINE_VTABLE_CTOR(JavascriptNativeIntArray, JavascriptNativeArray);
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptNativeIntArray);
public:
JavascriptNativeIntArray(DynamicType * type);
JavascriptNativeIntArray(uint32 length, uint32 size, DynamicType * type);
JavascriptNativeIntArray(DynamicType * type, uint32 size);
protected:
JavascriptNativeIntArray(uint32 length, DynamicType * type) :
JavascriptNativeArray(length, type) {}
// For BoxStackInstance
JavascriptNativeIntArray(JavascriptNativeIntArray * instance, bool boxHead);
public:
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
static Var NewInstance(RecyclableObject* function, Arguments args);
static bool Is(Var aValue);
static bool Is(TypeId typeId);
static JavascriptNativeIntArray* FromVar(Var aValue);
static bool IsNonCrossSite(Var aValue);
typedef int32 TElement;
static const uint8 AllocationBucketsCount = 3;
static uint allocationBuckets[AllocationBucketsCount][AllocationBucketsInfoSize];
static const int32 MissingItem;
virtual PropertyQueryFlags HasItemQuery(uint32 index) override;
virtual PropertyQueryFlags GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext) override;
virtual PropertyQueryFlags GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext) override;
virtual BOOL DirectGetVarItemAt(uint index, Var* outval, ScriptContext *scriptContext);
virtual BOOL DirectGetItemAtFull(uint index, Var* outVal);
virtual Var DirectGetItem(uint32 index);
virtual DescriptorFlags GetItemSetter(uint32 index, Var* setterValue, ScriptContext* requestContext) override;
virtual BOOL SetItem(uint32 index, Var value, PropertyOperationFlags flags) override;
virtual BOOL DeleteItem(uint32 index, PropertyOperationFlags flags) override;
#ifdef VALIDATE_ARRAY
virtual void ValidateArray() override;
#endif
BOOL SetItem(uint32 index, int32 iValue);
static JavascriptNativeFloatArray * ToNativeFloatArray(JavascriptNativeIntArray *intArray);
static JavascriptArray * ToVarArray(JavascriptNativeIntArray *intArray);
static JavascriptArray * ConvertToVarArray(JavascriptNativeIntArray *intArray);
static Var Push(ScriptContext * scriptContext, Var array, int value);
static int32 Pop(ScriptContext * scriptContext, Var nativeIntArray);
#if ENABLE_PROFILE_INFO
virtual JavascriptArray *FillFromArgs(uint length, uint start, Var *args, ArrayCallSiteInfo *info = nullptr, bool dontCreateNewArray = false) override;
#else
virtual JavascriptArray *FillFromArgs(uint length, uint start, Var *args, bool dontCreateNewArray = false) override;
#endif
virtual void ClearElements(SparseArraySegmentBase *seg, uint32 newSegmentLength) override;
virtual void SetIsPrototype() override;
TypeId TrySetNativeIntArrayItem(Var value, int32 *iValue, double *dValue);
virtual bool IsMissingHeadSegmentItem(const uint32 index) const override;
static VTableValue VtableHelper()
{
return VTableValue::VtableNativeIntArray;
}
static LibraryValue InitialTypeHelper()
{
return LibraryValue::ValueNativeIntArrayType;
}
static DynamicType * GetInitialType(ScriptContext * scriptContext);
static JavascriptNativeIntArray * BoxStackInstance(JavascriptNativeIntArray * instance);
private:
virtual int32 HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext) override;
#if ENABLE_TTD
public:
virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override
{
return;
}
virtual void ProcessCorePaths() override
{
return;
}
virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
#endif
public:
virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
{
return VtableHelper();
}
};
#if ENABLE_COPYONACCESS_ARRAY
class JavascriptCopyOnAccessNativeIntArray : public JavascriptNativeIntArray
{
friend class JavascriptArray;
public:
static const size_t StackAllocationSize;
protected:
DEFINE_VTABLE_CTOR(JavascriptCopyOnAccessNativeIntArray, JavascriptNativeIntArray);
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptCopyOnAccessNativeIntArray);
public:
JavascriptCopyOnAccessNativeIntArray(uint32 length, DynamicType * type) :
JavascriptNativeIntArray(length, type) {}
virtual BOOL IsCopyOnAccessArray() { return TRUE; }
static bool Is(Var aValue);
static bool Is(TypeId typeId);
static JavascriptCopyOnAccessNativeIntArray* FromVar(Var aValue);
static DynamicType * GetInitialType(ScriptContext * scriptContext);
void ConvertCopyOnAccessSegment();
uint32 GetNextIndex(uint32 index) const;
BOOL DirectGetItemAt(uint32 index, int* outVal);
static VTableValue VtableHelper()
{
return VTableValue::VtableCopyOnAccessNativeIntArray;
}
#if ENABLE_TTD
public:
virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override
{
return;
}
virtual void ProcessCorePaths() override
{
return;
}
virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
#endif
public:
virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
{
return VtableHelper();
}
};
#endif
class JavascriptNativeFloatArray : public JavascriptNativeArray
{
friend class JavascriptArray;
public:
static const size_t StackAllocationSize;
protected:
DEFINE_VTABLE_CTOR(JavascriptNativeFloatArray, JavascriptNativeArray);
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptNativeFloatArray);
public:
JavascriptNativeFloatArray(DynamicType * type);
JavascriptNativeFloatArray(uint32 length, uint32 size, DynamicType * type);
JavascriptNativeFloatArray(DynamicType * type, uint32 size);
private:
JavascriptNativeFloatArray(uint32 length, DynamicType * type) :
JavascriptNativeArray(length, type) {}
// For BoxStackInstance
JavascriptNativeFloatArray(JavascriptNativeFloatArray * instance, bool boxHead);
public:
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
static Var NewInstance(RecyclableObject* function, Arguments args);
static bool Is(Var aValue);
static bool Is(TypeId typeId);
static JavascriptNativeFloatArray* FromVar(Var aValue);
static bool IsNonCrossSite(Var aValue);
typedef double TElement;
static const uint8 AllocationBucketsCount = 3;
static uint allocationBuckets[AllocationBucketsCount][AllocationBucketsInfoSize];
static const double MissingItem;
virtual PropertyQueryFlags HasItemQuery(uint32 index) override;
virtual PropertyQueryFlags GetItemQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext) override;
virtual PropertyQueryFlags GetItemReferenceQuery(Var originalInstance, uint32 index, Var* value, ScriptContext * requestContext) override;
virtual BOOL DirectGetVarItemAt(uint index, Var* outval, ScriptContext *scriptContext);
virtual BOOL DirectGetItemAtFull(uint index, Var* outVal);
virtual Var DirectGetItem(uint32 index);
virtual DescriptorFlags GetItemSetter(uint32 index, Var* setterValue, ScriptContext* requestContext) override
{
double value = 0;
return this->DirectGetItemAt(index, &value) ? WritableData : None;
}
virtual BOOL SetItem(uint32 index, Var value, PropertyOperationFlags flags) override;
virtual BOOL DeleteItem(uint32 index, PropertyOperationFlags flags) override;
#ifdef VALIDATE_ARRAY
virtual void ValidateArray() override;
#endif
BOOL SetItem(uint32 index, double dValue);
static JavascriptArray * ToVarArray(JavascriptNativeFloatArray *fArray);
static JavascriptArray * ConvertToVarArray(JavascriptNativeFloatArray *fArray);
#if ENABLE_PROFILE_INFO
virtual JavascriptArray *FillFromArgs(uint length, uint start, Var *args, ArrayCallSiteInfo *info = nullptr, bool dontCreateNewArray = false) override;
#else
virtual JavascriptArray *FillFromArgs(uint length, uint start, Var *args, bool dontCreateNewArray = false) override;
#endif
virtual void ClearElements(SparseArraySegmentBase *seg, uint32 newSegmentLength) override;
virtual void SetIsPrototype() override;
TypeId TrySetNativeFloatArrayItem(Var value, double *dValue);
virtual bool IsMissingHeadSegmentItem(const uint32 index) const override;
static VTableValue VtableHelper()
{
return VTableValue::VtableNativeFloatArray;
}
static LibraryValue InitialTypeHelper()
{
return LibraryValue::ValueNativeFloatArrayType;
}
static DynamicType * GetInitialType(ScriptContext * scriptContext);
static Var Push(ScriptContext * scriptContext, Var * nativeFloatArray, double value);
static JavascriptNativeFloatArray * BoxStackInstance(JavascriptNativeFloatArray * instance);
static double Pop(ScriptContext * scriptContext, Var nativeFloatArray);
private:
virtual int32 HeadSegmentIndexOfHelper(Var search, uint32 &fromIndex, uint32 toIndex, bool includesAlgorithm, ScriptContext * scriptContext) override;
#if ENABLE_TTD
public:
virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override
{
return;
}
virtual void ProcessCorePaths() override
{
return;
}
virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
#endif
public:
virtual VTableValue DummyVirtualFunctionToHinderLinkerICF()
{
return VtableHelper();
}
};
template <>
inline bool JavascriptArray::MayChangeType<JavascriptNativeIntArray>() { return true; }
template <>
inline bool JavascriptArray::MayChangeType<JavascriptNativeFloatArray>() { return true; }
template <>
inline uint32 JavascriptArray::ConvertToIndex<uint32, uint32>(uint32 idxDest, ScriptContext* scriptContext) { return idxDest; }
} // namespace Js