• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_OBJECT_H_
17 #define PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_OBJECT_H_
18 
19 #include <cstdint>
20 #include "plugins/ets/runtime/ets_mark_word.h"
21 #include "mark_word.h"
22 #include "mem/mem.h"
23 #include "runtime/include/object_header-inl.h"
24 #include "plugins/ets/runtime/types/ets_class.h"
25 #include "plugins/ets/runtime/types/ets_field.h"
26 
27 namespace ark::ets {
28 
29 class EtsCoroutine;
30 
31 // Private inheritance, because need to disallow implicit conversion to core type
32 class EtsObject : private ObjectHeader {
33 public:
34     PANDA_PUBLIC_API static EtsObject *Create(EtsCoroutine *etsCoroutine, EtsClass *klass);
35     PANDA_PUBLIC_API static EtsObject *CreateNonMovable(EtsClass *klass);
36 
37     PANDA_PUBLIC_API static EtsObject *Create(EtsClass *klass);
38 
GetClass()39     PANDA_PUBLIC_API EtsClass *GetClass() const
40     {
41         return EtsClass::FromRuntimeClass(GetCoreType()->ClassAddr<Class>());
42     }
43 
SetClass(EtsClass * cls)44     void SetClass(EtsClass *cls)
45     {
46         GetCoreType()->SetClass(UNLIKELY(cls == nullptr) ? nullptr : cls->GetRuntimeClass());
47     }
48 
IsInstanceOf(EtsClass * klass)49     bool IsInstanceOf(EtsClass *klass) const
50     {
51         ASSERT(klass != nullptr);
52         return klass->IsAssignableFrom(GetClass());
53     }
54 
HasField(EtsField * field)55     bool HasField(EtsField *field) const
56     {
57         ASSERT(field != nullptr);
58         return field->GetDeclaringClass()->IsAssignableFrom(field->GetDeclaringClass());
59     }
60 
GetAndSetFieldObject(size_t offset,EtsObject * value,std::memory_order memoryOrder)61     EtsObject *GetAndSetFieldObject(size_t offset, EtsObject *value, std::memory_order memoryOrder)
62     {
63         ASSERT(value != nullptr);
64         return FromCoreType(GetCoreType()->GetAndSetFieldObject(offset, value->GetCoreType(), memoryOrder));
65     }
66 
67     template <class T>
GetFieldPrimitive(EtsField * field)68     T GetFieldPrimitive(EtsField *field)
69     {
70         ASSERT(field != nullptr);
71         ASSERT(field->GetEtsType() == GetEtsTypeByPrimitive<T>());
72         ASSERT(HasField(field));
73         return GetCoreType()->GetFieldPrimitive<T>(*field->GetRuntimeField());
74     }
75 
76     template <class T, bool IS_VOLATILE = false>
GetFieldPrimitive(size_t offset)77     T GetFieldPrimitive(size_t offset)
78     {
79         return GetCoreType()->GetFieldPrimitive<T, IS_VOLATILE>(offset);
80     }
81 
82     template <class T>
GetFieldPrimitive(int32_t fieldOffset,bool isVolatile)83     T GetFieldPrimitive(int32_t fieldOffset, bool isVolatile)
84     {
85         if (isVolatile) {
86             return GetCoreType()->GetFieldPrimitive<T, true>(fieldOffset);
87         }
88         return GetCoreType()->GetFieldPrimitive<T, false>(fieldOffset);
89     }
90 
91     template <class T>
SetFieldPrimitive(EtsField * field,T value)92     void SetFieldPrimitive(EtsField *field, T value)
93     {
94         ASSERT(field != nullptr);
95         ASSERT(field->GetEtsType() == GetEtsTypeByPrimitive<T>());
96         ASSERT(HasField(field));
97         GetCoreType()->SetFieldPrimitive<T>(*field->GetRuntimeField(), value);
98     }
99 
100     template <class T>
SetFieldPrimitive(int32_t fieldOffset,bool isVolatile,T value)101     void SetFieldPrimitive(int32_t fieldOffset, bool isVolatile, T value)
102     {
103         if (isVolatile) {
104             GetCoreType()->SetFieldPrimitive<T, true>(fieldOffset, value);
105         }
106         GetCoreType()->SetFieldPrimitive<T, false>(fieldOffset, value);
107     }
108 
109     template <class T, bool IS_VOLATILE = false>
SetFieldPrimitive(size_t offset,T value)110     void SetFieldPrimitive(size_t offset, T value)
111     {
112         GetCoreType()->SetFieldPrimitive<T, IS_VOLATILE>(offset, value);
113     }
114 
115     template <bool NEED_READ_BARRIER = true>
GetFieldObject(EtsField * field)116     PANDA_PUBLIC_API EtsObject *GetFieldObject(EtsField *field) const
117     {
118         ASSERT(field != nullptr);
119         ASSERT(field->GetEtsType() == EtsType::OBJECT);
120         ASSERT(HasField(field));
121         return reinterpret_cast<EtsObject *>(
122             GetCoreType()->GetFieldObject<NEED_READ_BARRIER>(*field->GetRuntimeField()));
123     }
124 
GetFieldObject(int32_t fieldOffset,bool isVolatile)125     EtsObject *GetFieldObject(int32_t fieldOffset, bool isVolatile) const
126     {
127         if (isVolatile) {
128             return reinterpret_cast<EtsObject *>(GetCoreType()->GetFieldObject<true>(fieldOffset));
129         }
130         return reinterpret_cast<EtsObject *>(GetCoreType()->GetFieldObject<false>(fieldOffset));
131     }
132 
133     template <bool IS_VOLATILE = false>
GetFieldObject(size_t offset)134     EtsObject *GetFieldObject(size_t offset)
135     {
136         return reinterpret_cast<EtsObject *>(GetCoreType()->GetFieldObject<IS_VOLATILE>(offset));
137     }
138 
139     template <bool NEED_WRITE_BARRIER = true>
SetFieldObject(EtsField * field,EtsObject * value)140     void SetFieldObject(EtsField *field, EtsObject *value)
141     {
142         ASSERT(field != nullptr);
143         ASSERT(field->GetEtsType() == EtsType::OBJECT);
144         ASSERT(HasField(field));
145         GetCoreType()->SetFieldObject<NEED_WRITE_BARRIER>(*field->GetRuntimeField(),
146                                                           reinterpret_cast<ObjectHeader *>(value));
147     }
148 
149     template <bool NEED_WRITE_BARRIER = true>
SetFieldObject(int32_t fieldOffset,bool isVolatile,EtsObject * value)150     void SetFieldObject(int32_t fieldOffset, bool isVolatile, EtsObject *value)
151     {
152         if (isVolatile) {
153             GetCoreType()->SetFieldObject<true, NEED_WRITE_BARRIER>(fieldOffset,
154                                                                     reinterpret_cast<ObjectHeader *>(value));
155         } else {
156             GetCoreType()->SetFieldObject<false, NEED_WRITE_BARRIER>(fieldOffset,
157                                                                      reinterpret_cast<ObjectHeader *>(value));
158         }
159     }
160 
161     template <bool IS_VOLATILE = false>
SetFieldObject(size_t offset,EtsObject * value)162     void SetFieldObject(size_t offset, EtsObject *value)
163     {
164         GetCoreType()->SetFieldObject<IS_VOLATILE>(offset, reinterpret_cast<ObjectHeader *>(value));
165     }
166 
SetFieldObject(size_t offset,EtsObject * value,std::memory_order memoryOrder)167     void SetFieldObject(size_t offset, EtsObject *value, std::memory_order memoryOrder)
168     {
169         GetCoreType()->SetFieldObject(offset, value->GetCoreType(), memoryOrder);
170     }
171 
172     template <typename T>
CompareAndSetFieldPrimitive(size_t offset,T oldValue,T newValue,std::memory_order memoryOrder,bool strong)173     bool CompareAndSetFieldPrimitive(size_t offset, T oldValue, T newValue, std::memory_order memoryOrder, bool strong)
174     {
175         return GetCoreType()->CompareAndSetFieldPrimitive(offset, oldValue, newValue, memoryOrder, strong);
176     }
177 
CompareAndSetFieldObject(size_t offset,EtsObject * oldValue,EtsObject * newValue,std::memory_order memoryOrder,bool strong)178     bool CompareAndSetFieldObject(size_t offset, EtsObject *oldValue, EtsObject *newValue,
179                                   std::memory_order memoryOrder, bool strong)
180     {
181         return GetCoreType()->CompareAndSetFieldObject(offset, reinterpret_cast<ObjectHeader *>(oldValue),
182                                                        reinterpret_cast<ObjectHeader *>(newValue), memoryOrder, strong);
183     }
184 
Clone()185     EtsObject *Clone() const
186     {
187         return FromCoreType(ObjectHeader::Clone(GetCoreType()));
188     }
189 
GetCoreType()190     ObjectHeader *GetCoreType() const
191     {
192         return static_cast<ObjectHeader *>(const_cast<EtsObject *>(this));
193     }
194 
FromCoreType(ObjectHeader * objectHeader)195     static constexpr EtsObject *FromCoreType(ObjectHeader *objectHeader)
196     {
197         return static_cast<EtsObject *>(objectHeader);
198     }
199 
FromCoreType(const ObjectHeader * objectHeader)200     static constexpr const EtsObject *FromCoreType(const ObjectHeader *objectHeader)
201     {
202         return static_cast<const EtsObject *>(objectHeader);
203     }
204 
IsStringClass()205     PANDA_PUBLIC_API bool IsStringClass()
206     {
207         return GetClass()->IsStringClass();
208     }
209 
IsArrayClass()210     PANDA_PUBLIC_API bool IsArrayClass()
211     {
212         return GetClass()->IsArrayClass();
213     }
214 
215     // NOTE(ipetrov, #20886): Support separated Interop and Hash states
216     /**
217      * Get hash code if object has been already hashed,
218      * or generate and set hash code in the object header and return it
219      * @return hash code for the object
220      */
221     uint32_t GetHashCode();
222 
223     /**
224      * @brief Get interop index of object. It should be setted. You can check if it's setted by
225      * HasInteropIndexed method.
226      * This method is thread safe to GetHashCode, HasInteropIndexed methods. Also it's thread safe to itself.
227      * @see HasInteropIndexed, GetHashCode.
228      * @return interop index of object.
229      */
230     uint32_t GetInteropIndex() const;
231 
232     /**
233      * @brief This method sets interop index of the object. Object must not have it. You can check if it's setted by
234      * HasInteropIndexed method.
235      * This method is thread safe to GetHashCode, HasInteropIndexed methods. IT IN NOT THREAD SAFE TO ITSELF!
236      * @see HasInteropIndexed, GetHashCode.
237      */
238     void SetInteropIndex(uint32_t index);
239 
240     /**
241      * @brief This method drops interop index of the object. Object must have it. You can check if it's setted by
242      * HasInteropIndexed method. If object is in USE_INFO state, it will no be changed until Deflate method is called.
243      * This method is thread safe to GetHashCode, HasInteropIndexed methods. IT IN NOT THREAD SAFE TO ITSELF!
244      * @see HasInteropIndexed, GetHashCode, Deflate
245      */
246     void DropInteropIndex();
247 
HasInteropIndex()248     bool HasInteropIndex() const
249     {
250         bool hasInteropIndex = false;
251         while (!TryCheckIfHasInteropIndex(&hasInteropIndex)) {
252         }
253         return hasInteropIndex;
254     }
255 
IsHashed()256     bool IsHashed() const
257     {
258         auto mark = AtomicGetMark(std::memory_order_relaxed);
259         auto markState = mark.GetState();
260         return markState == EtsMarkWord::STATE_USE_INFO || markState == EtsMarkWord::STATE_HASHED;
261     }
262 
IsUsedInfo()263     bool IsUsedInfo() const
264     {
265         auto mark = AtomicGetMark(std::memory_order_relaxed);
266         auto markState = mark.GetState();
267         return markState == EtsMarkWord::STATE_USE_INFO;
268     }
269 
SetMark(EtsMarkWord word)270     ALWAYS_INLINE void SetMark(EtsMarkWord word)
271     {
272         ObjectHeader::SetMark(word.ToMark());
273     }
274 
275     ALWAYS_INLINE EtsMarkWord AtomicGetMark(std::memory_order memoryOrder = std::memory_order_seq_cst) const
276     {
277         return EtsMarkWord::FromMarkWord(ObjectHeader::AtomicGetMark(memoryOrder));
278     }
279 
GetMark()280     ALWAYS_INLINE EtsMarkWord GetMark() const
281     {
282         return EtsMarkWord::FromMarkWord(ObjectHeader::GetMark());
283     }
284 
285     template <bool STRONG = true>
286     ALWAYS_INLINE bool AtomicSetMark(EtsMarkWord &oldMarkWord, EtsMarkWord newMarkWord,
287                                      std::memory_order memoryOrder = std::memory_order_seq_cst)
288     {
289         return ObjectHeader::AtomicSetMark<STRONG>(oldMarkWord, newMarkWord.ToMark(), memoryOrder);
290     }
291 
292     EtsObject() = delete;
293     ~EtsObject() = delete;
294 
295 protected:
296     // Use type alias to allow using into derived classes
297     using ObjectHeader = ::ark::ObjectHeader;
298 
299 private:
300     static inline uint32_t GenerateHashCode();
301 
302     [[nodiscard]] bool TryGetHashCode(uint32_t *hash);
303     [[nodiscard]] bool TryGetInteropIndex(uint32_t *index) const;
304     [[nodiscard]] bool TrySetInteropIndex(uint32_t index);
305     [[nodiscard]] bool TryDropInteropIndex();
306     [[nodiscard]] bool TryDeflate();
307     [[nodiscard]] PANDA_PUBLIC_API bool TryCheckIfHasInteropIndex(bool *hasInteropIndexed) const;
308 
309     NO_COPY_SEMANTIC(EtsObject);
310     NO_MOVE_SEMANTIC(EtsObject);
311 };
312 
313 // Size of EtsObject must be equal size of ObjectHeader
314 static_assert(sizeof(EtsObject) == sizeof(ObjectHeader));
315 
316 }  // namespace ark::ets
317 
318 #endif  // PANDA_RUNTIME_ETS_FFI_CLASSES_ETS_OBJECT_H_
319