• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024-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_PLUGINS_ETS_RUNTIME_NAPI_ETS_SCOPED_OBJECTS_FIX_H
17 #define PANDA_PLUGINS_ETS_RUNTIME_NAPI_ETS_SCOPED_OBJECTS_FIX_H
18 
19 #include "plugins/ets/runtime/types/ets_method.h"
20 #include "runtime/include/exceptions.h"
21 #include "runtime/include/mem/panda_smart_pointers.h"
22 #include "plugins/ets/runtime/ets_napi_env.h"
23 #include "plugins/ets/runtime/napi/ets_napi.h"
24 #include "runtime/include/managed_thread.h"
25 #include "plugins/ets/runtime/ets_coroutine.h"
26 #include "plugins/ets/runtime/ets_exceptions.h"
27 #include "plugins/ets/runtime/ets_panda_file_items.h"
28 #include "plugins/ets/runtime/mem/ets_reference.h"
29 
30 namespace ark::ets::napi {
31 enum class EtsNapiException {
32     ARRAY_INDEX_OUT_OF_BOUNDS,
33     ARRAY_STORE,
34     ILLEGAL_MONITOR_STATE,
35     INSTANTIATION,
36     NO_CLASS_DEF_FOUND,
37     NO_SUCH_FIELD,
38     NO_SUCH_METHOD,
39     NULL_POINTER_ERROR,
40     OUT_OF_MEMORY,
41     STRING_INDEX_OUT_OF_BOUNDS
42 };
43 
44 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
45 #define RETURN_NULL_IF_NULL(ptr) \
46     {                            \
47         if ((ptr) == nullptr) {  \
48             return nullptr;      \
49         }                        \
50     }
51 
52 class ManagedCodeAccessor {
53 public:
ManagedCodeAccessor(PandaEtsNapiEnv * env)54     explicit ManagedCodeAccessor(PandaEtsNapiEnv *env) : env_(env) {}
55     ~ManagedCodeAccessor() = default;
56 
ToInternalType(ets_class cls)57     EtsClass *ToInternalType(ets_class cls)
58     {
59         RETURN_NULL_IF_NULL(cls);
60         return reinterpret_cast<EtsClass *>(GetInternalType(env_, reinterpret_cast<ets_object>(cls)));
61     }
62 
ToInternalType(ets_object obj)63     EtsObject *ToInternalType(ets_object obj)
64     {
65         RETURN_NULL_IF_NULL(obj);
66         return GetInternalType(env_, obj);
67     }
68 
ToInternalType(ets_string str)69     EtsString *ToInternalType(ets_string str)
70     {
71         RETURN_NULL_IF_NULL(str);
72         return reinterpret_cast<EtsString *>(GetInternalType(env_, reinterpret_cast<ets_object>(str)));
73     }
74 
ToInternalType(ets_array arr)75     EtsArray *ToInternalType(ets_array arr)
76     {
77         RETURN_NULL_IF_NULL(arr);
78         return reinterpret_cast<EtsArray *>(GetInternalType(env_, reinterpret_cast<ets_object>(arr)));
79     }
80 
ToInternalType(ets_objectArray arr)81     EtsObjectArray *ToInternalType(ets_objectArray arr)
82     {
83         RETURN_NULL_IF_NULL(arr);
84         return reinterpret_cast<EtsObjectArray *>(ToInternalType(reinterpret_cast<ets_object>(arr)));
85     }
86 
ToInternalType(ets_deferred deferred)87     EtsPromise *ToInternalType(ets_deferred deferred)
88     {
89         RETURN_NULL_IF_NULL(deferred);
90         return reinterpret_cast<EtsPromise *>(ToInternalType(reinterpret_cast<ets_object>(deferred)));
91     }
92 
93     template <typename T>
Convert(ets_object obj)94     T *Convert(ets_object obj)
95     {
96         return Convert<T>(env_, obj);
97     }
98 
99     template <typename T>
Convert(PandaEtsNapiEnv * env,ets_object obj)100     static T *Convert(PandaEtsNapiEnv *env, ets_object obj)
101     {
102         return reinterpret_cast<T *>(GetInternalType(env, obj));
103     }
104 
Coroutine()105     EtsCoroutine *Coroutine()
106     {
107         return env_->GetEtsCoroutine();
108     }
109 
EtsNapiEnv()110     PandaEtsNapiEnv *EtsNapiEnv()
111     {
112         return env_;
113     }
114 
AddGlobalRef(EtsObject * obj)115     ets_object AddGlobalRef(EtsObject *obj)
116     {
117         EtsReference *ref = GetEtsReferenceStorage()->NewEtsRef(obj, EtsReference::EtsObjectType::GLOBAL);
118         return EtsRefToEtsObject(ref);
119     }
120 
AddLocalRef(EtsObject * obj)121     ets_object AddLocalRef(EtsObject *obj)
122     {
123         return AddLocalRef(env_, obj);
124     }
125 
AddLocalRef(PandaEtsNapiEnv * env,EtsObject * obj)126     static ets_object AddLocalRef(PandaEtsNapiEnv *env, EtsObject *obj)
127     {
128         ASSERT_MANAGED_CODE();
129         EtsReference *ref = GetEtsReferenceStorage(env)->NewEtsRef(obj, EtsReference::EtsObjectType::LOCAL);
130         return EtsRefToEtsObject(ref);
131     }
132 
AddWeakGlobalRef(EtsObject * obj)133     ets_object AddWeakGlobalRef(EtsObject *obj)
134     {
135         return AddWeakGlobalRef(env_, obj);
136     }
137 
AddWeakGlobalRef(PandaEtsNapiEnv * env,EtsObject * obj)138     static ets_object AddWeakGlobalRef(PandaEtsNapiEnv *env, EtsObject *obj)
139     {
140         ASSERT_MANAGED_CODE();
141         RETURN_NULL_IF_NULL(obj);
142         EtsReferenceStorage *refStorage = GetEtsReferenceStorage(env);
143         EtsReference *ref = refStorage->NewEtsRef(obj, EtsReference::EtsObjectType::WEAK);
144         return EtsRefToEtsNapiWeak(ref);
145     }
146 
DelLocalRef(ets_object obj)147     void DelLocalRef(ets_object obj)
148     {
149         DelLocalRef(env_, obj);
150     }
151 
DelLocalRef(PandaEtsNapiEnv * env,ets_object obj)152     static void DelLocalRef(PandaEtsNapiEnv *env, ets_object obj)
153     {
154         ASSERT_MANAGED_CODE();
155         if (obj == nullptr) {
156             return;
157         }
158         EtsReference *ref = EtsObjectToEtsRef(obj);
159         if (!ref->IsLocal()) {
160             LOG(WARNING, RUNTIME) << "Try to remove non-local ref: " << std::hex << ref;
161             return;
162         }
163         env->GetEtsReferenceStorage()->RemoveEtsRef(ref);
164     }
165 
IsValidRef(ets_object obj)166     bool IsValidRef(ets_object obj)
167     {
168         EtsReference *ref = EtsObjectToEtsRef(obj);
169         return GetEtsReferenceStorage()->IsValidEtsRef(ref);
170     }
171 
TraverseReferenceTable()172     void TraverseReferenceTable()
173     {
174         mem::ReferenceStorage *refStorage = GetEtsReferenceStorage()->GetAsReferenceStorage();
175         PandaVector<ObjectHeader *> table = refStorage->GetAllObjects();
176         for (const auto *obj : table) {
177             std::cout << "object classname = ";
178             std::cout << obj->ClassAddr<Class>()->GetName() << std::endl;
179         }
180     }
181 
GetEtsCoroutine()182     EtsCoroutine *GetEtsCoroutine() const
183     {
184         return env_->GetEtsCoroutine();
185     }
186 
187     NO_COPY_SEMANTIC(ManagedCodeAccessor);
188     NO_MOVE_SEMANTIC(ManagedCodeAccessor);
189 
190 protected:
GetEtsReferenceStorage()191     EtsReferenceStorage *GetEtsReferenceStorage()
192     {
193         return env_->GetEtsReferenceStorage();
194     }
195 
GetEtsReferenceStorage(PandaEtsNapiEnv * env)196     static EtsReferenceStorage *GetEtsReferenceStorage(PandaEtsNapiEnv *env)
197     {
198         return env->GetEtsReferenceStorage();
199     }
200 
GetInternalType(PandaEtsNapiEnv * env,ets_object obj)201     static EtsObject *GetInternalType(PandaEtsNapiEnv *env, ets_object obj)
202     {
203         if (obj == nullptr) {
204             return nullptr;
205         }
206         EtsReference *ref = EtsObjectToEtsRef(obj);
207         return GetEtsReferenceStorage(env)->GetEtsObject(ref);
208     }
209 
210 private:
211     PandaEtsNapiEnv *env_;
212 };
213 
214 class ScopedManagedCodeFix : public ManagedCodeAccessor {
215     class ExceptionData {
216     public:
ExceptionData(const char * name,const char * message)217         ExceptionData(const char *name, const char *message) : className_(name)
218         {
219             if (message != nullptr) {
220                 message_ = message;
221             }
222         }
223 
GetClassName()224         const char *GetClassName() const
225         {
226             return className_.c_str();
227         }
228 
GetMessage()229         const char *GetMessage() const
230         {
231             return message_ ? message_.value().c_str() : nullptr;
232         }
233 
234     private:
235         PandaString className_;
236         std::optional<PandaString> message_;
237     };
238 
239 public:
ScopedManagedCodeFix(PandaEtsNapiEnv * env)240     explicit ScopedManagedCodeFix(PandaEtsNapiEnv *env)
241         : ManagedCodeAccessor(env), alreadyInManaged_(ManagedThread::IsManagedScope())
242     {
243         ASSERT(env == PandaEtsNapiEnv::GetCurrent());
244 
245         if (alreadyInManaged_ && IsAccessFromManagedAllowed()) {
246             return;
247         }
248 
249         Coroutine()->ManagedCodeBegin();
250     }
251 
~ScopedManagedCodeFix()252     ~ScopedManagedCodeFix()
253     {
254         if (exceptionData_) {
255             ThrowEtsException(Coroutine(), exceptionData_->GetClassName(), exceptionData_->GetMessage());
256         }
257 
258         if (!alreadyInManaged_) {
259             Coroutine()->ManagedCodeEnd();
260         }
261     }
262 
ThrowNewException(EtsNapiException kind,const char * message)263     void ThrowNewException(EtsNapiException kind, const char *message)
264     {
265         ASSERT(!exceptionData_);
266 
267         std::string_view className {};
268         switch (kind) {
269             case EtsNapiException::ARRAY_INDEX_OUT_OF_BOUNDS:
270                 className = panda_file_items::class_descriptors::ARRAY_INDEX_OUT_OF_BOUNDS_ERROR;
271                 break;
272             case EtsNapiException::ARRAY_STORE:
273                 className = panda_file_items::class_descriptors::ARRAY_STORE_ERROR;
274                 break;
275             case EtsNapiException::ILLEGAL_MONITOR_STATE:
276                 className = panda_file_items::class_descriptors::ILLEGAL_MONITOR_STATE_ERROR;
277                 break;
278             case EtsNapiException::INSTANTIATION:
279                 className = panda_file_items::class_descriptors::INSTANTIATION_ERROR;
280                 break;
281             case EtsNapiException::NO_CLASS_DEF_FOUND:
282                 className = panda_file_items::class_descriptors::LINKER_UNRESOLVED_CLASS_ERROR;
283                 break;
284             case EtsNapiException::NO_SUCH_FIELD:
285                 className = panda_file_items::class_descriptors::LINKER_UNRESOLVED_FIELD_ERROR;
286                 break;
287             case EtsNapiException::NO_SUCH_METHOD:
288                 className = panda_file_items::class_descriptors::LINKER_UNRESOLVED_FIELD_ERROR;
289                 break;
290             case EtsNapiException::NULL_POINTER_ERROR:
291                 className = panda_file_items::class_descriptors::NULL_POINTER_ERROR;
292                 break;
293             case EtsNapiException::OUT_OF_MEMORY:
294                 className = panda_file_items::class_descriptors::OUT_OF_MEMORY_ERROR;
295                 break;
296             case EtsNapiException::STRING_INDEX_OUT_OF_BOUNDS:
297                 className = panda_file_items::class_descriptors::STRING_INDEX_OUT_OF_BOUNDS_ERROR;
298                 break;
299             default:
300                 UNREACHABLE();
301         }
302 
303         exceptionData_ = MakePandaUnique<ExceptionData>(className.data(), message);
304     }
305 
HasPendingException()306     bool HasPendingException()
307     {
308         return exceptionData_ || Coroutine()->HasPendingException();
309     }
310 
311     NO_COPY_SEMANTIC(ScopedManagedCodeFix);
312     NO_MOVE_SEMANTIC(ScopedManagedCodeFix);
313 
314 private:
IsAccessFromManagedAllowed()315     bool IsAccessFromManagedAllowed()
316     {
317 #ifndef NDEBUG
318         auto stack = StackWalker::Create(GetEtsCoroutine());
319         auto method = EtsMethod::FromRuntimeMethod(stack.GetMethod());
320         ASSERT(method != nullptr);
321         return method->IsFastNative();
322 #else
323         return true;
324 #endif  // NDEBUG
325     }
326 
327     PandaUniquePtr<ExceptionData> exceptionData_;
328     bool alreadyInManaged_;
329 };
330 
331 class ScopedManagedCodeFastNative : public ManagedCodeAccessor {
332 public:
ScopedManagedCodeFastNative(PandaEtsNapiEnv * env)333     explicit ScopedManagedCodeFastNative(PandaEtsNapiEnv *env) : ManagedCodeAccessor(env)
334     {
335         ASSERT(Coroutine()->IsManagedCode());
336     }
337 
~ScopedManagedCodeFastNative()338     ~ScopedManagedCodeFastNative()
339     {
340         ASSERT(Coroutine()->IsManagedCode());
341     }
342 
343     NO_COPY_SEMANTIC(ScopedManagedCodeFastNative);
344     NO_MOVE_SEMANTIC(ScopedManagedCodeFastNative);
345 };
346 }  // namespace ark::ets::napi
347 
348 #endif  // PANDA_PLUGINS_ETS_RUNTIME_NAPI_ETS_SCOPED_OBJECTS_FIX_H
349