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_JSARRAY_H 17 #define ECMASCRIPT_JSARRAY_H 18 19 #include "ecmascript/js_object.h" 20 #include "ecmascript/js_tagged_value-inl.h" 21 #include "ecmascript/js_tagged_value.h" 22 #include "ecmascript/js_thread.h" 23 #include "ecmascript/tagged_array.h" 24 25 namespace panda::ecmascript { 26 enum class ArrayMode : uint8_t { UNDEFINED = 0, DICTIONARY, LITERAL }; 27 // ecma6 9.4.2 Array Exotic Object 28 class JSArray final : public JSObject { 29 public: 30 // array instance property: 31 static constexpr int LENGTH_INLINE_PROPERTY_INDEX = 0; 32 // array prototype property: 33 static constexpr int CONSTRUCTOR_INLINE_PROPERTY_INDEX = 1; 34 // array constructor property: 35 static constexpr int ARRAY_FUNCTION_INLINE_PROPERTY_NUM = 7; 36 static constexpr int ARRAY_FUNCTION_SPECIES_INDEX = 0; 37 CAST_CHECK(JSArray, IsJSArray); 38 39 static JSTaggedValue CheckStableArrayAndGet(JSThread *thread, const JSHandle<JSObject> &thisObjHandle, 40 uint32_t index); 41 static void CheckStableArrayAndSet(JSThread *thread, const JSHandle<JSObject> &thisObjHandle, 42 uint32_t index, JSMutableHandle<JSTaggedValue> &value); 43 static PUBLIC_API JSHandle<JSTaggedValue> ArrayCreate(JSThread *thread, JSTaggedNumber length, 44 ArrayMode mode = ArrayMode::UNDEFINED); 45 static JSHandle<JSTaggedValue> ArrayCreate(JSThread *thread, JSTaggedNumber length, 46 const JSHandle<JSTaggedValue> &newTarget, 47 ArrayMode mode = ArrayMode::UNDEFINED); 48 static JSTaggedValue GetConstructorOrSpeciesInlinedProp(JSTaggedValue object, uint32_t inlinePropIndex); 49 static JSTaggedValue ArraySpeciesCreate(JSThread *thread, const JSHandle<JSObject> &originalArray, 50 JSTaggedNumber length); 51 static bool ArraySetLength(JSThread *thread, const JSHandle<JSObject> &array, const PropertyDescriptor &desc); 52 static bool DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array, const JSHandle<JSTaggedValue> &key, 53 const PropertyDescriptor &desc); 54 static bool DefineOwnProperty(JSThread *thread, const JSHandle<JSObject> &array, uint32_t index, 55 const PropertyDescriptor &desc); 56 57 static bool IsLengthString(JSThread *thread, const JSHandle<JSTaggedValue> &key); 58 static bool IsProtoNotChangeJSArray(JSThread *thread, const JSHandle<JSObject> &obj); 59 static bool IsProtoNotModifiedDictionaryJSArray(JSThread *thread, const JSHandle<JSObject> &obj); 60 // ecma6 7.3 Operations on Objects 61 static JSHandle<JSArray> CreateArrayFromList(JSThread *thread, const JSHandle<TaggedArray> &elements); 62 static JSHandle<JSArray> CreateArrayFromList(JSThread *thread, const JSHandle<JSTaggedValue> &newtarget, 63 const JSHandle<TaggedArray> &elements); 64 static JSTaggedValue FastConcatDictionaryArray(JSThread *thread, JSHandle<JSObject> obj, 65 JSHandle<JSObject> &newArrayHandle, JSMutableHandle<JSTaggedValue> &fromValHandle, 66 JSMutableHandle<JSTaggedValue> &toKey, int64_t &n); 67 68 // use first inlined property slot for array length GetArrayLength()69 inline uint32_t GetArrayLength() const 70 { 71 return GetLength(); 72 } 73 SetArrayLength(const JSThread * thread,uint32_t length)74 inline void SetArrayLength([[maybe_unused]]const JSThread *thread, uint32_t length) 75 { 76 SetLength(length); 77 } 78 GetHintLength()79 inline uint32_t GetHintLength() const 80 { 81 auto trackInfo = GetTrackInfo(); 82 if (trackInfo.IsInt()) { 83 int hint = trackInfo.GetInt(); 84 return hint > 0 ? hint : 0; 85 } 86 return 0; 87 } 88 89 static constexpr size_t LENGTH_OFFSET = JSObject::SIZE; 90 ACCESSORS_PRIMITIVE_FIELD(Length, uint32_t, LENGTH_OFFSET, TRACE_INDEX_OFFSET) 91 ACCESSORS_PRIMITIVE_FIELD(TraceIndex, uint32_t, TRACE_INDEX_OFFSET, TRACK_INFO_OFFSET) 92 ACCESSORS(TrackInfo, TRACK_INFO_OFFSET, SIZE) 93 94 DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, TRACK_INFO_OFFSET, SIZE) 95 96 static const uint32_t MAX_ARRAY_INDEX = MAX_ELEMENT_INDEX; DECL_DUMP()97 DECL_DUMP() 98 99 static uint32_t GetInlinedPropertyOffset(uint32_t index) 100 { 101 return JSArray::SIZE + index * JSTaggedValue::TaggedTypeSize(); 102 } 103 GetArrayLengthOffset()104 static int32_t GetArrayLengthOffset() 105 { 106 return LENGTH_OFFSET; 107 } 108 109 static bool PropertyKeyToArrayIndex(JSThread *thread, const JSHandle<JSTaggedValue> &key, uint32_t *output); 110 111 static JSTaggedValue LengthGetter(JSThread *thread, const JSHandle<JSObject> &self); 112 113 static bool LengthSetter(JSThread *thread, const JSHandle<JSObject> &self, const JSHandle<JSTaggedValue> &value, 114 bool mayThrow = false); 115 116 static JSHandle<JSTaggedValue> FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 117 uint32_t index); 118 119 static JSHandle<JSTaggedValue> FastGetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 120 const JSHandle<JSTaggedValue> &key); 121 122 static bool FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, uint32_t index, 123 const JSHandle<JSTaggedValue> &value); 124 125 static bool FastSetPropertyByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 126 const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value); 127 128 static bool TryFastCreateDataProperty(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t index, 129 const JSHandle<JSTaggedValue> &value, 130 SCheckMode sCheckMode = SCheckMode::CHECK); 131 132 static JSTaggedValue Sort(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &fn); 133 static JSTaggedValue CopySortedListToReceiver(JSThread *thread, const JSHandle<JSTaggedValue> &thisObjVal, 134 JSHandle<TaggedArray> sortedList, uint32_t len); 135 static bool IncludeInSortedValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj, 136 const JSHandle<JSTaggedValue> &value); 137 static JSHandle<TaggedArray> ToTaggedArray(JSThread *thread, const JSHandle<JSTaggedValue> &obj); 138 static void PUBLIC_API CheckAndCopyArray(const JSThread *thread, JSHandle<JSArray> obj); 139 static void CheckAndSetPrototypeModified(JSThread *thread, const JSHandle<JSObject> &newArrayHandle); 140 static void SetCapacity(JSThread *thread, const JSHandle<JSObject> &array, uint32_t oldLen, uint32_t newLen, 141 bool isNew = false); 142 static void TransformElementsKindAfterSetCapacity(JSThread *thread, const JSHandle<JSObject> &array, 143 [[maybe_unused]] uint32_t oldLen, uint32_t newLen, 144 [[maybe_unused]] bool isNew); 145 static void SortElements(JSThread *thread, const JSHandle<TaggedArray> &elements, 146 const JSHandle<JSTaggedValue> &fn); 147 static void SortElementsByObject(JSThread *thread, const JSHandle<JSObject> &thisObjHandle, 148 const JSHandle<JSTaggedValue> &fn); 149 static void SortElementsByInsertionSort(JSThread *thread, const JSHandle<TaggedArray> &elements, uint32_t len, 150 const JSHandle<JSTaggedValue> &fn); 151 static void SortElementsByMergeSort(JSThread *thread, const JSHandle<TaggedArray> &elements, 152 const JSHandle<JSTaggedValue> &fn, int64_t startIdx, int64_t endIdx); 153 static void MergeSortedElements(JSThread *thread, const JSHandle<TaggedArray> &elements, 154 const JSHandle<JSTaggedValue> &fn, int64_t startIdx, int64_t middleIdx, int64_t endIdx); 155 156 static JSHandle<JSHClass> CreateJSArrayPrototypeClass(const JSThread *thread, ObjectFactory *factory, 157 JSHandle<JSTaggedValue> proto, uint32_t inlinedProps); 158 159 static JSHandle<JSHClass> CreateJSArrayFunctionClass(const JSThread *thread, ObjectFactory *factory, 160 const JSHandle<GlobalEnv> &env); 161 162 template <class Callback> ArrayCreateWithInit(JSThread * thread,uint32_t length,const Callback & cb)163 static JSTaggedValue ArrayCreateWithInit(JSThread *thread, uint32_t length, const Callback &cb) 164 { 165 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 166 JSHandle<TaggedArray> newElements(factory->NewTaggedArray(length)); 167 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 168 JSHandle<JSTaggedValue> array(JSArray::CreateArrayFromList(thread, newElements)); 169 cb(newElements, length); 170 return array.GetTaggedValue(); 171 } 172 173 void UpdateTrackInfo(const JSThread *thread); 174 }; 175 176 class TrackInfo : public TaggedObject { 177 public: Cast(TaggedObject * object)178 static TrackInfo *Cast(TaggedObject *object) 179 { 180 ASSERT(JSTaggedValue(object).IsTrackInfoObject()); 181 return static_cast<TrackInfo *>(object); 182 } 183 184 static constexpr size_t CACHED_HCLASS_OFFSET = TaggedObjectSize(); 185 ACCESSORS(CachedHClass, CACHED_HCLASS_OFFSET, CACHED_FUNC_OFFSET); 186 ACCESSORS(CachedFunc, CACHED_FUNC_OFFSET, BIT_FIELD_OFFSET); 187 ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, ARRAY_LENGTH_OFFSET); 188 ACCESSORS_PRIMITIVE_FIELD(ArrayLength, uint32_t, ARRAY_LENGTH_OFFSET, LAST_OFFSET) 189 DEFINE_ALIGN_SIZE(LAST_OFFSET); 190 191 // define BitField 192 static constexpr size_t ELEMENTS_KIND_BITS = 8; 193 static constexpr size_t SPACE_FALG_BITS = 8; 194 FIRST_BIT_FIELD(BitField, ElementsKind, ElementsKind, ELEMENTS_KIND_BITS); 195 NEXT_BIT_FIELD(BitField, SpaceFlag, RegionSpaceFlag, SPACE_FALG_BITS, ElementsKind); 196 197 DECL_DUMP() 198 199 DECL_VISIT_OBJECT(CACHED_HCLASS_OFFSET, BIT_FIELD_OFFSET); 200 }; 201 } // namespace panda::ecmascript 202 203 #endif // ECMASCRIPT_JSARRAY_H 204