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 #ifndef PANDA_PLUGINS_ETS_RUNTIME_FFI_CLASSES_ETS_ARRAY_H_ 16 #define PANDA_PLUGINS_ETS_RUNTIME_FFI_CLASSES_ETS_ARRAY_H_ 17 18 #include "libpandabase/macros.h" 19 #include "libpandabase/mem/space.h" 20 #include "runtime/include/coretypes/array.h" 21 #include "plugins/ets/runtime/types/ets_class.h" 22 #include "plugins/ets/runtime/types/ets_primitives.h" 23 #include "plugins/ets/runtime/types/ets_box_primitive.h" 24 #include "plugins/ets/runtime/types/ets_object.h" 25 #include "plugins/ets/runtime/ets_class_root.h" 26 #include "plugins/ets/runtime/ets_vm.h" 27 28 namespace ark::ets { 29 30 // Private inheritance, because need to disallow implicit conversion to core type 31 class EtsArray : private coretypes::Array { 32 public: 33 EtsArray() = delete; 34 ~EtsArray() = delete; 35 GetLength()36 PANDA_PUBLIC_API size_t GetLength() 37 { 38 return GetCoreType()->GetLength(); 39 } 40 GetElementSize()41 size_t GetElementSize() 42 { 43 return GetCoreType()->ClassAddr<Class>()->GetComponentSize(); 44 } 45 ObjectSize()46 size_t ObjectSize() 47 { 48 return GetCoreType()->ObjectSize(GetElementSize()); 49 } 50 51 template <class T> GetData()52 T *GetData() 53 { 54 return reinterpret_cast<T *>(GetCoreType()->GetData()); 55 } 56 GetClass()57 EtsClass *GetClass() 58 { 59 return EtsClass::FromRuntimeClass(GetCoreType()->ClassAddr<Class>()); 60 } 61 IsPrimitive()62 bool IsPrimitive() 63 { 64 auto componentType = GetCoreType()->ClassAddr<Class>()->GetComponentType(); 65 ASSERT(componentType != nullptr); 66 return componentType->IsPrimitive(); 67 } 68 GetDataOffset()69 static constexpr uint32_t GetDataOffset() 70 { 71 return coretypes::Array::GetDataOffset(); 72 } 73 AsObject()74 EtsObject *AsObject() 75 { 76 return reinterpret_cast<EtsObject *>(this); 77 } 78 AsObject()79 const EtsObject *AsObject() const 80 { 81 return reinterpret_cast<const EtsObject *>(this); 82 } 83 GetCoreType()84 coretypes::Array *GetCoreType() 85 { 86 return reinterpret_cast<coretypes::Array *>(this); 87 } 88 89 NO_COPY_SEMANTIC(EtsArray); 90 NO_MOVE_SEMANTIC(EtsArray); 91 92 protected: 93 // Use type alias to allow using into derived classes 94 using ObjectHeader = ::ark::ObjectHeader; 95 96 template <class T> 97 static T *Create(EtsClass *arrayClass, uint32_t length, SpaceType spaceType = SpaceType::SPACE_TYPE_OBJECT, 98 bool pinned = false) 99 { 100 return reinterpret_cast<T *>( 101 coretypes::Array::Create(arrayClass->GetRuntimeClass(), length, spaceType, pinned)); 102 } 103 104 template <class T> SetImpl(uint32_t idx,T elem)105 void SetImpl(uint32_t idx, T elem) 106 { 107 GetCoreType()->Set(idx, elem); 108 } 109 110 template <class T> GetImpl(uint32_t idx)111 T GetImpl(uint32_t idx) 112 { 113 return GetCoreType()->Get<T>(idx); 114 } 115 }; 116 117 template <typename Component> 118 class EtsTypedObjectArray : public EtsArray { 119 public: 120 using ValueType = Component *; 121 122 static EtsTypedObjectArray *Create(EtsClass *objectClass, uint32_t length, 123 ark::SpaceType spaceType = ark::SpaceType::SPACE_TYPE_OBJECT) 124 { 125 ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS(); 126 // Generate Array class name "[L<object_class>;" 127 EtsClassLinker *classLinker = PandaEtsVM::GetCurrent()->GetClassLinker(); 128 PandaString arrayClassName = PandaString("[") + objectClass->GetDescriptor(); 129 EtsClass *arrayClass = classLinker->GetClass(arrayClassName.c_str(), true, objectClass->GetLoadContext()); 130 if (arrayClass == nullptr) { 131 return nullptr; 132 } 133 return EtsArray::Create<EtsTypedObjectArray>(arrayClass, length, spaceType); 134 } 135 Set(uint32_t index,Component * element)136 void Set(uint32_t index, Component *element) 137 { 138 if (element == nullptr) { 139 SetImpl<ObjectHeader *>(index, nullptr); 140 } else { 141 SetImpl(index, element->GetCoreType()); 142 } 143 } 144 Get(uint32_t index)145 PANDA_PUBLIC_API Component *Get(uint32_t index) 146 { 147 return reinterpret_cast<Component *>( 148 GetImpl<std::invoke_result_t<decltype(&Component::GetCoreType), Component>>(index)); 149 } 150 Set(uint32_t index,Component * element,std::memory_order memoryOrder)151 void Set(uint32_t index, Component *element, std::memory_order memoryOrder) 152 { 153 auto offset = index * sizeof(ObjectPointerType); 154 GetCoreType()->SetObject(offset, element == nullptr ? nullptr : element->GetCoreType(), memoryOrder); 155 } 156 Get(uint32_t index,std::memory_order memoryOrder)157 Component *Get(uint32_t index, std::memory_order memoryOrder) 158 { 159 auto offset = index * sizeof(ObjectPointerType); 160 return Component::FromCoreType(GetCoreType()->GetObject(offset, memoryOrder)); 161 } 162 FromCoreType(ObjectHeader * objectHeader)163 static EtsTypedObjectArray *FromCoreType(ObjectHeader *objectHeader) 164 { 165 return reinterpret_cast<EtsTypedObjectArray *>(objectHeader); 166 } 167 CopyDataTo(EtsTypedObjectArray * dst)168 void CopyDataTo(EtsTypedObjectArray *dst) 169 { 170 ASSERT(dst != nullptr); 171 ASSERT(GetLength() <= dst->GetLength()); 172 173 if (std::size_t count = GetLength() * OBJECT_POINTER_SIZE) { 174 Span<uint8_t> srcSpan(GetData<uint8_t>(), count); 175 Span<uint8_t> dstSpan(dst->GetData<uint8_t>(), count); 176 CopyData(srcSpan, dstSpan); 177 // Call barriers. 178 // PreBarrier isn't needed as links inside the source object arn't changed. 179 auto *barrierSet = ManagedThread::GetCurrent()->GetBarrierSet(); 180 if (!mem::IsEmptyBarrier(barrierSet->GetPostType())) { 181 barrierSet->PostBarrier(dst, GetDataOffset(), count); 182 } 183 } 184 } 185 186 EtsTypedObjectArray() = delete; 187 ~EtsTypedObjectArray() = delete; 188 189 private: 190 NO_COPY_SEMANTIC(EtsTypedObjectArray); 191 NO_MOVE_SEMANTIC(EtsTypedObjectArray); 192 193 using WordType = uintptr_t; 194 using AtomicWord = std::atomic<WordType>; 195 using AtomicRef = std::atomic<ark::ObjectPointerType>; 196 197 static constexpr const std::size_t WORD_SIZE = sizeof(WordType); 198 CopyData(Span<uint8_t> & src,Span<uint8_t> & dst)199 void CopyData(Span<uint8_t> &src, Span<uint8_t> &dst) 200 { 201 ASSERT((src.Size() % OBJECT_POINTER_SIZE) == 0); 202 ASSERT(src.Size() <= dst.Size()); 203 204 // WORDs and REFERENCEs must be loaded/stored atomically 205 constexpr const std::memory_order ORDER = std::memory_order_relaxed; 206 // 1. copy by words if any 207 std::size_t i = 0; 208 std::size_t stop = (src.Size() / WORD_SIZE) * WORD_SIZE; 209 for (; i < stop; i += WORD_SIZE) { 210 // Atomic with parameterized order reason: memory order defined as constexpr 211 reinterpret_cast<AtomicWord *>(&dst[i])->store(reinterpret_cast<AtomicWord *>(&src[i])->load(ORDER), ORDER); 212 } 213 // 2. copy by references if any 214 stop = src.Size(); 215 for (; i < stop; i += OBJECT_POINTER_SIZE) { 216 // Atomic with parameterized order reason: memory order defined as constexpr 217 reinterpret_cast<AtomicRef *>(&dst[i])->store(reinterpret_cast<AtomicRef *>(&src[i])->load(ORDER), ORDER); 218 } 219 } 220 }; 221 222 using EtsObjectArray = EtsTypedObjectArray<EtsObject>; 223 224 template <class ClassType, EtsClassRoot ETS_CLASS_ROOT> 225 class EtsPrimitiveArray : public EtsArray { 226 public: 227 using ValueType = ClassType; 228 229 static EtsPrimitiveArray *Create(uint32_t length, SpaceType spaceType = SpaceType::SPACE_TYPE_OBJECT, 230 bool pinned = false) 231 { 232 ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS(); 233 return EtsArray::Create<EtsPrimitiveArray>(GetComponentClass(), length, spaceType, pinned); 234 } Set(uint32_t index,ClassType element)235 void Set(uint32_t index, ClassType element) 236 { 237 SetImpl(index, element); 238 } Get(uint32_t index)239 ClassType Get(uint32_t index) 240 { 241 return GetImpl<ClassType>(index); 242 } GetComponentClass()243 static EtsClass *GetComponentClass() 244 { 245 return PandaEtsVM::GetCurrent()->GetClassLinker()->GetClassRoot(ETS_CLASS_ROOT); 246 } 247 248 EtsPrimitiveArray() = delete; 249 ~EtsPrimitiveArray() = delete; 250 251 private: 252 NO_COPY_SEMANTIC(EtsPrimitiveArray); 253 NO_MOVE_SEMANTIC(EtsPrimitiveArray); 254 }; 255 256 using EtsBooleanArray = EtsPrimitiveArray<EtsBoolean, EtsClassRoot::BOOLEAN_ARRAY>; 257 using EtsByteArray = EtsPrimitiveArray<EtsByte, EtsClassRoot::BYTE_ARRAY>; 258 using EtsCharArray = EtsPrimitiveArray<EtsChar, EtsClassRoot::CHAR_ARRAY>; 259 using EtsShortArray = EtsPrimitiveArray<EtsShort, EtsClassRoot::SHORT_ARRAY>; 260 using EtsUintArray = EtsPrimitiveArray<EtsUint, EtsClassRoot::UINT_ARRAY>; 261 using EtsIntArray = EtsPrimitiveArray<EtsInt, EtsClassRoot::INT_ARRAY>; 262 using EtsUlongArray = EtsPrimitiveArray<EtsUlong, EtsClassRoot::ULONG_ARRAY>; 263 using EtsLongArray = EtsPrimitiveArray<EtsLong, EtsClassRoot::LONG_ARRAY>; 264 using EtsFloatArray = EtsPrimitiveArray<EtsFloat, EtsClassRoot::FLOAT_ARRAY>; 265 using EtsDoubleArray = EtsPrimitiveArray<EtsDouble, EtsClassRoot::DOUBLE_ARRAY>; 266 267 namespace test { 268 template <class ElementType> 269 class EtsArrayObjectMembers; 270 } // namespace test 271 272 // Mirror class for Array<T> from ETS stdlib 273 template <class ElementType> 274 class EtsArrayObject : public EtsObject { 275 public: 276 EtsArrayObject() = delete; 277 ~EtsArrayObject() = delete; 278 279 NO_COPY_SEMANTIC(EtsArrayObject); 280 NO_MOVE_SEMANTIC(EtsArrayObject); 281 FromEtsObject(EtsObject * etsObj)282 static EtsArrayObject *FromEtsObject(EtsObject *etsObj) 283 { 284 return reinterpret_cast<EtsArrayObject *>(etsObj); 285 } 286 GetData()287 EtsTypedObjectArray<ElementType> *GetData() 288 { 289 return reinterpret_cast<EtsTypedObjectArray<ElementType> *>(GetFieldObject(GetBufferOffset())); 290 } 291 GetActualLength()292 uint32_t GetActualLength() 293 { 294 return helpers::ToUnsigned(GetFieldPrimitive<EtsInt>(GetActualLengthOffset())); 295 } 296 GetBufferOffset()297 static constexpr size_t GetBufferOffset() 298 { 299 return MEMBER_OFFSET(EtsArrayObject, buffer_); 300 } 301 GetActualLengthOffset()302 static constexpr size_t GetActualLengthOffset() 303 { 304 return MEMBER_OFFSET(EtsArrayObject, actualLength_); 305 } 306 307 private: 308 ObjectPointer<EtsTypedObjectArray<ElementType>> buffer_; 309 EtsInt actualLength_; 310 311 friend class test::EtsArrayObjectMembers<ElementType>; 312 }; 313 314 using EtsBoxedBooleanArray = EtsArrayObject<EtsBoxPrimitive<EtsBoolean>>; 315 using EtsBoxedByteArray = EtsArrayObject<EtsBoxPrimitive<EtsByte>>; 316 using EtsBoxedCharArray = EtsArrayObject<EtsBoxPrimitive<EtsChar>>; 317 using EtsBoxedShortArray = EtsArrayObject<EtsBoxPrimitive<EtsShort>>; 318 using EtsBoxedIntArray = EtsArrayObject<EtsBoxPrimitive<EtsInt>>; 319 using EtsBoxedLongArray = EtsArrayObject<EtsBoxPrimitive<EtsLong>>; 320 using EtsBoxedFloatArray = EtsArrayObject<EtsBoxPrimitive<EtsFloat>>; 321 using EtsBoxedDoubleArray = EtsArrayObject<EtsBoxPrimitive<EtsDouble>>; 322 } // namespace ark::ets 323 324 #endif // PANDA_PLUGINS_ETS_RUNTIME_FFI_CLASSES_ETS_ARRAY_H_ 325