• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "include/language_context.h"
17 #include "include/mem/panda_containers.h"
18 #include "libpandabase/utils/utf.h"
19 #include "macros.h"
20 #include "napi/ets_napi.h"
21 #include "runtime/include/runtime.h"
22 #include "plugins/ets/runtime/types/ets_array.h"
23 #include "plugins/ets/runtime/types/ets_object.h"
24 #include "plugins/ets/runtime/types/ets_field.h"
25 #include "plugins/ets/runtime/types/ets_method.h"
26 #include "plugins/ets/runtime/types/ets_method_signature.h"
27 #include "plugins/ets/runtime/types/ets_string.h"
28 #include "plugins/ets/runtime/types/ets_value.h"
29 #include "plugins/ets/runtime/types/ets_class.h"
30 
31 namespace ark::ets {
32 
GetFieldsNumber()33 uint32_t EtsClass::GetFieldsNumber()
34 {
35     uint32_t fnumber = 0;
36     EnumerateBaseClasses([&](EtsClass *c) {
37         fnumber += c->GetRuntimeClass()->GetFields().Size();
38         return false;
39     });
40     return fnumber;
41 }
42 
43 // Without inherited fields
GetOwnFieldsNumber()44 uint32_t EtsClass::GetOwnFieldsNumber()
45 {
46     return GetRuntimeClass()->GetFields().Size();
47 }
48 
GetFields()49 PandaVector<EtsField *> EtsClass::GetFields()
50 {
51     auto etsFields = PandaVector<EtsField *>(Runtime::GetCurrent()->GetInternalAllocator()->Adapter());
52     EnumerateBaseClasses([&](EtsClass *c) {
53         auto fields = c->GetRuntimeClass()->GetFields();
54         auto fnum = fields.Size();
55         for (uint32_t i = 0; i < fnum; i++) {
56             etsFields.push_back(EtsField::FromRuntimeField(&fields[i]));
57         }
58         return false;
59     });
60     return etsFields;
61 }
62 
GetFieldByIndex(uint32_t i)63 EtsField *EtsClass::GetFieldByIndex(uint32_t i)
64 {
65     EtsField *res = nullptr;
66     EnumerateBaseClasses([&](EtsClass *c) {
67         auto fields = c->GetRuntimeClass()->GetFields();
68         auto fnum = fields.Size();
69         if (i >= fnum) {
70             i -= fnum;
71             return false;
72         }
73         res = EtsField::FromRuntimeField(&fields[i]);
74         return true;
75     });
76     return res;
77 }
78 
GetOwnFieldByIndex(uint32_t i)79 EtsField *EtsClass::GetOwnFieldByIndex(uint32_t i)
80 {
81     return EtsField::FromRuntimeField(&GetRuntimeClass()->GetFields()[i]);
82 }
83 
GetDirectMethod(const char * name,const char * signature)84 EtsMethod *EtsClass::GetDirectMethod(const char *name, const char *signature)
85 {
86     auto coreName = reinterpret_cast<const uint8_t *>(name);
87     return GetDirectMethod(coreName, signature);
88 }
89 
GetDirectMethod(const char * name)90 EtsMethod *EtsClass::GetDirectMethod(const char *name)
91 {
92     const uint8_t *mutf8Name = utf::CStringAsMutf8(name);
93     Method *rtMethod = GetRuntimeClass()->GetDirectMethod(mutf8Name);
94     return EtsMethod::FromRuntimeMethod(rtMethod);
95 }
96 
GetDirectMethod(const uint8_t * name,const char * signature)97 EtsMethod *EtsClass::GetDirectMethod(const uint8_t *name, const char *signature)
98 {
99     EtsMethodSignature methodSignature(signature);
100     if (!methodSignature.IsValid()) {
101         LOG(ERROR, ETS_NAPI) << "Wrong method signature: " << signature;
102         return nullptr;
103     }
104 
105     auto coreMethod = GetRuntimeClass()->GetDirectMethod(name, methodSignature.GetProto());
106     return reinterpret_cast<EtsMethod *>(coreMethod);
107 }
108 
GetDirectMethod(const char * name,const Method::Proto & proto) const109 EtsMethod *EtsClass::GetDirectMethod(const char *name, const Method::Proto &proto) const
110 {
111     Method *method = klass_.GetDirectMethod(utf::CStringAsMutf8(name), proto);
112     return EtsMethod::FromRuntimeMethod(method);
113 }
114 
GetMethodsNum()115 uint32_t EtsClass::GetMethodsNum()
116 {
117     return GetMethods().size();
118 }
119 
GetMethodByIndex(uint32_t ind)120 EtsMethod *EtsClass::GetMethodByIndex(uint32_t ind)
121 {
122     EtsMethod *res = nullptr;
123     auto methods = GetMethods();
124     ASSERT(ind < methods.size());
125     res = methods[ind];
126     return res;
127 }
128 
GetMethod(const char * name)129 EtsMethod *EtsClass::GetMethod(const char *name)
130 {
131     auto coreName = reinterpret_cast<const uint8_t *>(name);
132 
133     Method *coreMethod = nullptr;
134     auto *runtimeClass = GetRuntimeClass();
135     if (IsInterface()) {
136         coreMethod = runtimeClass->GetInterfaceMethod(coreName);
137     } else {
138         coreMethod = runtimeClass->GetClassMethod(coreName);
139     }
140     return reinterpret_cast<EtsMethod *>(coreMethod);
141 }
142 
GetMethod(const char * name,const char * signature)143 EtsMethod *EtsClass::GetMethod(const char *name, const char *signature)
144 {
145     EtsMethodSignature methodSignature(signature);
146     if (!methodSignature.IsValid()) {
147         LOG(ERROR, ETS_NAPI) << "Wrong method signature:" << signature;
148         return nullptr;
149     }
150 
151     auto coreName = reinterpret_cast<const uint8_t *>(name);
152 
153     Method *coreMethod = nullptr;
154     auto *runtimeClass = GetRuntimeClass();
155     if (IsInterface()) {
156         coreMethod = runtimeClass->GetInterfaceMethod(coreName, methodSignature.GetProto());
157     } else {
158         coreMethod = runtimeClass->GetClassMethod(coreName, methodSignature.GetProto());
159     }
160     return reinterpret_cast<EtsMethod *>(coreMethod);
161 }
162 
163 // NOTE(kirill-mitkin): Cache in EtsClass field later
GetMethods()164 PandaVector<EtsMethod *> EtsClass::GetMethods()
165 {
166     PandaUnorderedMap<PandaString, EtsMethod *> uniqueMethods;
167 
168     auto addDirectMethods = [&](const EtsClass *c) {
169         auto directMethods = c->GetRuntimeClass()->GetMethods();
170         for (auto &method : directMethods) {
171             PandaString methodFullName = utf::Mutf8AsCString(method.GetName().data);
172             methodFullName += method.GetProto().GetSignature();
173             if (uniqueMethods.find(methodFullName) == uniqueMethods.end()) {
174                 uniqueMethods[methodFullName] = EtsMethod::FromRuntimeMethod(&method);
175             }
176         }
177     };
178 
179     auto addDirectMethodsForBaseClass = [&uniqueMethods](EtsClass *c) {
180         auto directMethods = c->GetRuntimeClass()->GetMethods();
181         auto fnum = directMethods.Size();
182         for (uint32_t i = 0; i < fnum; i++) {
183             Method *method = &directMethods[i];
184             // Skip constructors
185             if (method->IsConstructor()) {
186                 continue;
187             }
188 
189             PandaString methodFullName = utf::Mutf8AsCString((method->GetName().data));
190             methodFullName += method->GetProto().GetSignature();
191 
192             uniqueMethods[methodFullName] = EtsMethod::FromRuntimeMethod(method);
193         }
194     };
195 
196     if (IsInterface()) {
197         addDirectMethods(this);
198         EnumerateInterfaces([&](const EtsClass *c) {
199             addDirectMethods(c);
200             return false;
201         });
202     } else {
203         EnumerateBaseClasses([&](EtsClass *c) {
204             addDirectMethodsForBaseClass(c);
205             return false;
206         });
207     }
208     auto etsMethods = PandaVector<EtsMethod *>();
209     for (auto &iter : uniqueMethods) {
210         etsMethods.push_back(iter.second);
211     }
212     return etsMethods;
213 }
214 
GetConstructors()215 PandaVector<EtsMethod *> EtsClass::GetConstructors()
216 {
217     auto constructors = PandaVector<EtsMethod *>();
218     auto methods = GetRuntimeClass()->GetMethods();
219     // NOTE(kirill-mitkin): cache in ets_class field
220     for (auto &method : methods) {
221         // Skip constructors
222         if (method.IsInstanceConstructor()) {
223             constructors.emplace_back(EtsMethod::FromRuntimeMethod(&method));
224         }
225     }
226     return constructors;
227 }
228 
ResolveVirtualMethod(const EtsMethod * method) const229 EtsMethod *EtsClass::ResolveVirtualMethod(const EtsMethod *method) const
230 {
231     return reinterpret_cast<EtsMethod *>(GetRuntimeClass()->ResolveVirtualMethod(method->GetPandaMethod()));
232 }
233 
GetNameAndClassRoot(char hash)234 static std::pair<char const *, EtsClassRoot> GetNameAndClassRoot(char hash)
235 {
236     const char *primitiveName = nullptr;
237     EtsClassRoot classRoot;
238 
239     switch (hash) {
240         case 'v':
241             primitiveName = "void";
242             classRoot = EtsClassRoot::VOID;
243             break;
244         case 'b':
245             primitiveName = "boolean";
246             classRoot = EtsClassRoot::BOOLEAN;
247             break;
248         case 'B':
249             primitiveName = "byte";
250             classRoot = EtsClassRoot::BYTE;
251             break;
252         case 'c':
253             primitiveName = "char";
254             classRoot = EtsClassRoot::CHAR;
255             break;
256         case 's':
257             primitiveName = "short";
258             classRoot = EtsClassRoot::SHORT;
259             break;
260         case 'i':
261             primitiveName = "int";
262             classRoot = EtsClassRoot::INT;
263             break;
264         case 'l':
265             primitiveName = "long";
266             classRoot = EtsClassRoot::LONG;
267             break;
268         case 'f':
269             primitiveName = "float";
270             classRoot = EtsClassRoot::FLOAT;
271             break;
272         case 'd':
273             primitiveName = "double";
274             classRoot = EtsClassRoot::DOUBLE;
275             break;
276         default:
277             break;
278     }
279 
280     return {primitiveName, classRoot};
281 }
282 
283 /* static */
GetPrimitiveClass(EtsString * name)284 EtsClass *EtsClass::GetPrimitiveClass(EtsString *name)
285 {
286     if (name == nullptr || name->GetMUtf8Length() < 2) {  // MUtf8Length must be >= 2
287         return nullptr;
288     }
289     // StringIndexOutOfBoundsException is not thrown by At method, because index (0, 1) < length (>= 2)
290     // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader)
291     char hash = name->At(0) ^ ((name->At(1) & 0x10) << 1);  // NOLINT(hicpp-signed-bitwise, readability-magic-numbers)
292     auto [primitiveName, classRoot] = GetNameAndClassRoot(hash);
293 
294     if (primitiveName != nullptr && name->IsEqual(primitiveName)) {  // SUPPRESS_CSA(alpha.core.WasteObjHeader)
295         return PandaEtsVM::GetCurrent()->GetClassLinker()->GetClassRoot(classRoot);
296     }
297 
298     return nullptr;
299 }
300 
CreateEtsClassName(const char * descriptor)301 EtsString *EtsClass::CreateEtsClassName([[maybe_unused]] const char *descriptor)
302 {
303     ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
304 
305     if (*descriptor == 'L') {
306         std::string_view tmpName(descriptor);
307         tmpName.remove_prefix(1);
308         tmpName.remove_suffix(1);
309         PandaString etsName(tmpName);
310         std::replace(etsName.begin(), etsName.end(), '/', '.');
311         return EtsString::CreateFromMUtf8(etsName.data(), etsName.length());
312     }
313     if (*descriptor == '[') {
314         PandaString etsName(descriptor);
315         std::replace(etsName.begin(), etsName.end(), '/', '.');
316         return EtsString::CreateFromMUtf8(etsName.data(), etsName.length());
317     }
318 
319     switch (*descriptor) {
320         case 'Z':
321             return EtsString::CreateFromMUtf8("boolean");
322         case 'B':
323             return EtsString::CreateFromMUtf8("byte");
324         case 'C':
325             return EtsString::CreateFromMUtf8("char");
326         case 'S':
327             return EtsString::CreateFromMUtf8("short");
328         case 'I':
329             return EtsString::CreateFromMUtf8("int");
330         case 'J':
331             return EtsString::CreateFromMUtf8("long");
332         case 'F':
333             return EtsString::CreateFromMUtf8("float");
334         case 'D':
335             return EtsString::CreateFromMUtf8("double");
336         case 'V':
337             return EtsString::CreateFromMUtf8("void");
338         default:
339             LOG(FATAL, RUNTIME) << "Incorrect primitive name" << descriptor;
340             UNREACHABLE();
341     }
342 }
343 
GetName()344 EtsString *EtsClass::GetName()
345 {
346     ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
347 
348     EtsString *name = nullptr;
349     bool success = false;
350 
351     do {
352         name = reinterpret_cast<EtsString *>(GetObjectHeader()->GetFieldObject(GetNameOffset()));
353         if (name != nullptr) {
354             return name;
355         }
356 
357         name = CreateEtsClassName(GetDescriptor());
358         if (name == nullptr) {
359             return nullptr;
360         }
361         success = CompareAndSetName(nullptr, name);
362     } while (!success);
363     return name;
364 }
365 
IsInSamePackage(std::string_view className1,std::string_view className2)366 bool EtsClass::IsInSamePackage(std::string_view className1, std::string_view className2)
367 {
368     size_t i = 0;
369     size_t minLength = std::min(className1.size(), className2.size());
370     while (i < minLength && className1[i] == className2[i]) {
371         ++i;
372     }
373     return className1.find('/', i) == std::string::npos && className2.find('/', i) == std::string::npos;
374 }
375 
IsInSamePackage(EtsClass * that)376 bool EtsClass::IsInSamePackage(EtsClass *that)
377 {
378     if (this == that) {
379         return true;
380     }
381 
382     EtsClass *klass1 = this;
383     EtsClass *klass2 = that;
384     while (klass1->IsArrayClass()) {
385         klass1 = klass1->GetComponentType();
386     }
387     while (klass2->IsArrayClass()) {
388         klass2 = klass2->GetComponentType();
389     }
390     if (klass1 == klass2) {
391         return true;
392     }
393 
394     // Compare the package part of the descriptor string.
395     return IsInSamePackage(klass1->GetDescriptor(), klass2->GetDescriptor());
396 }
397 
SetWeakReference()398 void EtsClass::SetWeakReference()
399 {
400     flags_ = flags_ | IS_WEAK_REFERENCE;
401     ASSERT(IsWeakReference() && IsReference());
402 }
SetFinalizeReference()403 void EtsClass::SetFinalizeReference()
404 {
405     flags_ = flags_ | IS_FINALIZE_REFERENCE;
406     ASSERT(IsFinalizerReference() && IsReference());
407 }
408 
SetValueTyped()409 void EtsClass::SetValueTyped()
410 {
411     flags_ = flags_ | IS_VALUE_TYPED;
412     ASSERT(IsValueTyped());
413 }
SetUndefined()414 void EtsClass::SetUndefined()
415 {
416     flags_ = flags_ | IS_UNDEFINED;
417     ASSERT(IsUndefined());
418 }
SetBoxed()419 void EtsClass::SetBoxed()
420 {
421     flags_ = flags_ | IS_BOXED;
422     ASSERT(IsBoxed());
423 }
SetFunction()424 void EtsClass::SetFunction()
425 {
426     flags_ = flags_ | IS_FUNCTION;
427     ASSERT(IsFunction());
428 }
SetBigInt()429 void EtsClass::SetBigInt()
430 {
431     flags_ = flags_ | IS_BIGINT;
432     ASSERT(IsBigInt());
433 }
434 
HasFunctionTypeInSuperClasses(EtsClass * cls)435 static bool HasFunctionTypeInSuperClasses(EtsClass *cls)
436 {
437     if (EtsClass *base = cls->GetBase(); base != nullptr) {
438         if (UNLIKELY(base->IsFunction())) {
439             return true;
440         }
441     }
442     for (Class *iface : cls->GetRuntimeClass()->GetInterfaces()) {
443         if (UNLIKELY(EtsClass::FromRuntimeClass(iface)->IsFunction())) {
444             return true;
445         }
446     }
447     return false;
448 }
449 
Initialize(EtsClass * superClass,uint16_t accessFlags,bool isPrimitiveType)450 void EtsClass::Initialize(EtsClass *superClass, uint16_t accessFlags, bool isPrimitiveType)
451 {
452     ASSERT_HAVE_ACCESS_TO_MANAGED_OBJECTS();
453 
454     SetName(nullptr);
455     SetSuperClass(superClass);
456 
457     uint32_t flags = accessFlags;
458     if (isPrimitiveType) {
459         flags |= ETS_ACC_PRIMITIVE;
460     }
461 
462     if (superClass != nullptr) {
463         static constexpr uint32_t COPIED_MASK = IS_WEAK_REFERENCE | IS_FINALIZE_REFERENCE;
464         flags |= superClass->GetFlags() & COPIED_MASK;
465         ASSERT(!superClass->IsValueTyped());
466     }
467     if (UNLIKELY(HasFunctionTypeInSuperClasses(this))) {
468         flags |= IS_FUNCTION;
469     }
470     SetFlags(flags);
471 }
472 
SetComponentType(EtsClass * componentType)473 void EtsClass::SetComponentType(EtsClass *componentType)
474 {
475     if (componentType == nullptr) {
476         GetRuntimeClass()->SetComponentType(nullptr);
477         return;
478     }
479     GetRuntimeClass()->SetComponentType(componentType->GetRuntimeClass());
480 }
481 
GetComponentType() const482 EtsClass *EtsClass::GetComponentType() const
483 {
484     ark::Class *componentType = GetRuntimeClass()->GetComponentType();
485     if (componentType == nullptr) {
486         return nullptr;
487     }
488     return FromRuntimeClass(componentType);
489 }
490 
SetName(EtsString * name)491 void EtsClass::SetName(EtsString *name)
492 {
493     GetObjectHeader()->SetFieldObject(GetNameOffset(), reinterpret_cast<ObjectHeader *>(name));
494 }
495 
CompareAndSetName(EtsString * oldName,EtsString * newName)496 bool EtsClass::CompareAndSetName(EtsString *oldName, EtsString *newName)
497 {
498     return GetObjectHeader()->CompareAndSetFieldObject(GetNameOffset(), reinterpret_cast<ObjectHeader *>(oldName),
499                                                        reinterpret_cast<ObjectHeader *>(newName),
500                                                        std::memory_order::memory_order_seq_cst, true);
501 }
502 
GetFieldIDByName(const char * name,const char * sig)503 EtsField *EtsClass::GetFieldIDByName(const char *name, const char *sig)
504 {
505     auto u8name = reinterpret_cast<const uint8_t *>(name);
506     auto *field = reinterpret_cast<EtsField *>(GetRuntimeClass()->GetInstanceFieldByName(u8name));
507 
508     if (sig != nullptr && field != nullptr) {
509         if (strcmp(field->GetTypeDescriptor(), sig) != 0) {
510             return nullptr;
511         }
512     }
513 
514     return field;
515 }
516 
GetFieldIndexByName(const char * name)517 uint32_t EtsClass::GetFieldIndexByName(const char *name)
518 {
519     auto u8name = reinterpret_cast<const uint8_t *>(name);
520     auto fields = GetRuntimeClass()->GetFields();
521     panda_file::File::StringData sd = {static_cast<uint32_t>(ark::utf::MUtf8ToUtf16Size(u8name)), u8name};
522     for (uint32_t i = 0; i < GetFieldsNumber(); i++) {
523         if (fields[i].GetName() == sd) {
524             return i;
525         }
526     }
527     return -1;
528 }
529 
GetStaticFieldIDByName(const char * name,const char * sig)530 EtsField *EtsClass::GetStaticFieldIDByName(const char *name, const char *sig)
531 {
532     auto u8name = reinterpret_cast<const uint8_t *>(name);
533     auto *field = reinterpret_cast<EtsField *>(GetRuntimeClass()->GetStaticFieldByName(u8name));
534 
535     if (sig != nullptr && field != nullptr) {
536         if (strcmp(field->GetTypeDescriptor(), sig) != 0) {
537             return nullptr;
538         }
539     }
540 
541     return field;
542 }
543 
GetDeclaredFieldIDByName(const char * name)544 EtsField *EtsClass::GetDeclaredFieldIDByName(const char *name)
545 {
546     return reinterpret_cast<EtsField *>(GetRuntimeClass()->FindDeclaredField([name](const ark::Field &field) -> bool {
547         auto *efield = EtsField::FromRuntimeField(&field);
548         return ::strcmp(efield->GetName(), name) == 0;
549     }));
550 }
551 
GetFieldIDByOffset(uint32_t fieldOffset)552 EtsField *EtsClass::GetFieldIDByOffset(uint32_t fieldOffset)
553 {
554     auto pred = [fieldOffset](const ark::Field &f) { return f.GetOffset() == fieldOffset; };
555     return reinterpret_cast<EtsField *>(GetRuntimeClass()->FindInstanceField(pred));
556 }
557 
GetStaticFieldIDByOffset(uint32_t fieldOffset)558 EtsField *EtsClass::GetStaticFieldIDByOffset(uint32_t fieldOffset)
559 {
560     auto pred = [fieldOffset](const ark::Field &f) { return f.GetOffset() == fieldOffset; };
561     return reinterpret_cast<EtsField *>(GetRuntimeClass()->FindStaticField(pred));
562 }
563 
GetBase()564 EtsClass *EtsClass::GetBase()
565 {
566     if (IsInterface()) {
567         return nullptr;
568     }
569     auto *base = GetRuntimeClass()->GetBase();
570     if (base == nullptr) {
571         return nullptr;
572     }
573     return FromRuntimeClass(base);
574 }
575 
GetInterfaces(PandaUnorderedSet<EtsClass * > & ifaces,EtsClass * iface)576 void EtsClass::GetInterfaces(PandaUnorderedSet<EtsClass *> &ifaces, EtsClass *iface)
577 {
578     ifaces.insert(iface);
579     EnumerateDirectInterfaces([&](EtsClass *runtimeInterface) {
580         if (ifaces.find(runtimeInterface) == ifaces.end()) {
581             runtimeInterface->GetInterfaces(ifaces, runtimeInterface);
582         }
583         return false;
584     });
585 }
586 
GetStaticFieldObject(EtsField * field)587 EtsObject *EtsClass::GetStaticFieldObject(EtsField *field)
588 {
589     return reinterpret_cast<EtsObject *>(GetRuntimeClass()->GetFieldObject(*field->GetRuntimeField()));
590 }
591 
GetStaticFieldObject(int32_t fieldOffset,bool isVolatile)592 EtsObject *EtsClass::GetStaticFieldObject(int32_t fieldOffset, bool isVolatile)
593 {
594     if (isVolatile) {
595         return reinterpret_cast<EtsObject *>(GetRuntimeClass()->GetFieldObject<true>(fieldOffset));
596     }
597     return reinterpret_cast<EtsObject *>(GetRuntimeClass()->GetFieldObject<false>(fieldOffset));
598 }
599 
SetStaticFieldObject(EtsField * field,EtsObject * value)600 void EtsClass::SetStaticFieldObject(EtsField *field, EtsObject *value)
601 {
602     GetRuntimeClass()->SetFieldObject(*field->GetRuntimeField(), reinterpret_cast<ObjectHeader *>(value));
603 }
604 
SetStaticFieldObject(int32_t fieldOffset,bool isVolatile,EtsObject * value)605 void EtsClass::SetStaticFieldObject(int32_t fieldOffset, bool isVolatile, EtsObject *value)
606 {
607     if (isVolatile) {
608         GetRuntimeClass()->SetFieldObject<true>(fieldOffset, reinterpret_cast<ObjectHeader *>(value));
609     }
610     GetRuntimeClass()->SetFieldObject<false>(fieldOffset, reinterpret_cast<ObjectHeader *>(value));
611 }
612 
613 }  // namespace ark::ets
614