1 /* 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef ECMASCRIPT_TAGGED_ARRAY_H 17 #define ECMASCRIPT_TAGGED_ARRAY_H 18 19 #include "ecmascript/ecma_macros.h" 20 #include "ecmascript/js_handle.h" 21 #include "ecmascript/js_tagged_value.h" 22 #include "ecmascript/mem/barriers.h" 23 #include "ecmascript/mem/visitor.h" 24 25 namespace panda::ecmascript { 26 class ObjectFactory; 27 class JSThread; 28 29 class TaggedArray : public TaggedObject { 30 public: 31 static constexpr uint32_t MAX_ARRAY_INDEX = std::numeric_limits<uint32_t>::max(); 32 static constexpr uint32_t MAX_END_UNUSED = 4; 33 static constexpr uint32_t EXTEND_PADDING_LENGTH = 16U; 34 35 CAST_CHECK(TaggedArray, IsTaggedArray); 36 37 template <RBMode mode = RBMode::DEFAULT_RB> Get(const JSThread * thread,uint32_t idx)38 JSTaggedValue Get(const JSThread *thread, uint32_t idx) const 39 { 40 ASSERT(idx < GetLength()); 41 // Note: Here we can't statically decide the element type is a primitive or heap object, especially for 42 // dynamically-typed languages like JavaScript. So we simply skip the read-barrier. 43 size_t offset = JSTaggedValue::TaggedTypeSize() * idx; 44 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) 45 return JSTaggedValue(Barriers::GetTaggedValue<mode>(thread, reinterpret_cast<JSTaggedType *>(ToUintPtr(this)), 46 DATA_OFFSET + offset)); 47 } 48 GetPrimitive(uint32_t idx)49 JSTaggedValue GetPrimitive(uint32_t idx) const 50 { 51 ASSERT(idx < GetLength()); 52 size_t offset = JSTaggedValue::TaggedTypeSize() * idx; 53 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) 54 return JSTaggedValue(Barriers::GetPrimitive<JSTaggedType>(reinterpret_cast<JSTaggedType *>(ToUintPtr(this)), 55 DATA_OFFSET + offset)); 56 } 57 58 uint32_t GetIdx(const JSThread *thread, const JSTaggedValue &value) const; 59 JSTaggedValue GetBit(const JSThread *thread, uint32_t idx, uint32_t bitOffset) const; 60 61 template<bool needBarrier = true, typename T = JSTaggedValue> 62 inline void Set(const JSThread *thread, uint32_t idx, const T &value); 63 64 void SetBit(const JSThread* thread, uint32_t idx, uint32_t bitOffset, const JSTaggedValue &value); 65 66 template <bool needBarrier = true, bool maybeOverlap = false> 67 inline void Copy(const JSThread* thread, uint32_t dstStart, uint32_t srcStart, 68 const TaggedArray *srcArray, uint32_t count); 69 70 static JSHandle<TaggedArray> Append(const JSThread *thread, const JSHandle<TaggedArray> &first, 71 const JSHandle<TaggedArray> &second); 72 static JSHandle<TaggedArray> AppendSkipHole(const JSThread *thread, const JSHandle<TaggedArray> &first, 73 const JSHandle<TaggedArray> &second, uint32_t copyLength); 74 ExtendCapacityWithPadding(uint32_t oldCapacity)75 static inline uint32_t ExtendCapacityWithPadding(uint32_t oldCapacity) 76 { 77 return oldCapacity + (oldCapacity >> 1U) + EXTEND_PADDING_LENGTH; 78 } 79 ComputeSize(size_t elemSize,uint32_t length)80 static size_t ComputeSize(size_t elemSize, uint32_t length) 81 { 82 ASSERT(elemSize != 0); 83 size_t size = DATA_OFFSET + elemSize * length; 84 return size; 85 } 86 GetData()87 JSTaggedType *GetData() const 88 { 89 return reinterpret_cast<JSTaggedType *>(ToUintPtr(this) + DATA_OFFSET); 90 } 91 GetThis()92 JSTaggedType *GetThis() const 93 { 94 return reinterpret_cast<JSTaggedType *>(ToUintPtr(this)); 95 } 96 97 bool IsDictionaryMode() const; 98 99 bool HasDuplicateEntry(const JSThread *thread) const; 100 101 bool IsYoungAndNotMarking(const JSThread *thread); 102 103 static JSHandle<TaggedArray> SetCapacity(const JSThread *thread, const JSHandle<TaggedArray> &array, 104 uint32_t capa); 105 106 static JSHandle<TaggedArray> SetCapacityInOldSpace(const JSThread *thread, const JSHandle<TaggedArray> &array, 107 uint32_t capa); 108 109 static void RemoveElementByIndex(const JSThread *thread, JSHandle<TaggedArray> &srcArray, 110 uint32_t index, uint32_t effectiveLength, bool noNeedBarrier = false); 111 static void InsertElementByIndex(const JSThread *thread, JSHandle<TaggedArray> &srcArray, 112 const JSHandle<JSTaggedValue> &value, uint32_t index, uint32_t effectiveLength); 113 static void CopyTaggedArrayElement(const JSThread *thread, JSHandle<TaggedArray> &srcElements, 114 JSHandle<TaggedArray> &dstElements, uint32_t effectiveLength); 115 116 void InitializeWithSpecialValue(JSTaggedValue initValue, uint32_t length, uint32_t extraLength = 0); 117 void FillRangeWithSpecialValue(JSTaggedValue initValue, uint32_t start, uint32_t end); 118 ShouldTrim(uint32_t oldLength,uint32_t newLength)119 static bool ShouldTrim(uint32_t oldLength, uint32_t newLength) 120 { 121 ASSERT(oldLength >= newLength); 122 return (oldLength - newLength > MAX_END_UNUSED); 123 } 124 void Trim(const JSThread *thread, uint32_t newLength); 125 126 static constexpr size_t LENGTH_OFFSET = TaggedObjectSize(); 127 ACCESSORS_PRIMITIVE_FIELD(Length, uint32_t, LENGTH_OFFSET, EXTRA_LENGTH_OFFSET) 128 ACCESSORS_PRIMITIVE_FIELD(ExtraLength, uint32_t, EXTRA_LENGTH_OFFSET, LAST_OFFSET) 129 DEFINE_ALIGN_SIZE(LAST_OFFSET); 130 static constexpr size_t DATA_OFFSET = SIZE; // DATA_OFFSET equal to Empty Array size 131 132 DECL_VISIT_ARRAY(DATA_OFFSET, GetLength(), GetLength()); 133 DECL_DUMP() 134 135 private: 136 friend class ObjectFactory; 137 }; 138 139 static_assert(TaggedArray::LENGTH_OFFSET == sizeof(TaggedObject)); 140 static_assert((TaggedArray::DATA_OFFSET % static_cast<uint8_t>(MemAlignment::MEM_ALIGN_OBJECT)) == 0); 141 142 // Copy On Write TaggedArray is shared in the nonmovable space. 143 class COWTaggedArray : public TaggedArray { 144 public: 145 CAST_CHECK(COWTaggedArray, IsCOWArray) 146 DECL_DUMP() 147 private: 148 friend class ObjectFactory; 149 }; 150 151 // A Mutant of TaggedArray which has numbers directly stored in Data section. 152 // Used by JSArrays with specified elementsKind. 153 class MutantTaggedArray : public TaggedArray { 154 public: 155 void InitializeWithSpecialValue(JSTaggedType initValue, uint32_t length, uint32_t extraLength = 0); 156 157 DECL_VISIT_ARRAY(DATA_OFFSET, 0, GetLength()); 158 CAST_CHECK(MutantTaggedArray, IsMutantTaggedArray) 159 DECL_DUMP() 160 private: 161 friend class ObjectFactory; 162 }; 163 164 // Copy On Write MutantTaggedArray is shared in the nonmovable space. 165 // With raw numbers stored in Data section. 166 class COWMutantTaggedArray : public MutantTaggedArray { 167 public: 168 169 DECL_VISIT_ARRAY(DATA_OFFSET, 0, GetLength()); 170 CAST_CHECK(COWMutantTaggedArray, IsCOWArray) 171 DECL_DUMP() 172 private: 173 friend class ObjectFactory; 174 }; 175 176 } // namespace panda::ecmascript 177 #endif // ECMASCRIPT_TAGGED_ARRAY_H 178