• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 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_ANI_SCOPED_OBJECTS_FIX_H
17 #define PANDA_PLUGINS_ETS_RUNTIME_ANI_SCOPED_OBJECTS_FIX_H
18 
19 #include "plugins/ets/runtime/ani/ani.h"
20 #include "plugins/ets/runtime/ani/ani_checkers.h"
21 #include "plugins/ets/runtime/ets_coroutine.h"
22 #include "plugins/ets/runtime/ets_napi_env.h"
23 #include "plugins/ets/runtime/types/ets_arraybuffer.h"
24 #include "plugins/ets/runtime/types/ets_method.h"
25 #include "plugins/ets/runtime/types/ets_module.h"
26 #include "plugins/ets/runtime/types/ets_namespace.h"
27 #include "plugins/ets/runtime/types/ets_promise.h"
28 
29 namespace ark::ets::ani {
30 
31 class ManagedCodeAccessor {
32 public:
ManagedCodeAccessor(PandaEnv * env)33     explicit ManagedCodeAccessor(PandaEnv *env) : env_(env) {}
34     ~ManagedCodeAccessor() = default;
35 
GetCoroutine()36     EtsCoroutine *GetCoroutine() const
37     {
38         return env_->GetEtsCoroutine();
39     }
40 
GetPandaEnv()41     PandaEnv *GetPandaEnv()
42     {
43         return env_;
44     }
45 
IsUndefined(ani_ref ref)46     static bool IsUndefined(ani_ref ref)
47     {
48         return ref == nullptr;
49     }
50 
IsUndefined(ani_wref ref)51     static bool IsUndefined(ani_wref ref)
52     {
53         return ref == nullptr;
54     }
55 
IsUndefined(EtsObject * object)56     static bool IsUndefined(EtsObject *object)
57     {
58         return object == nullptr;
59     }
60 
GetUndefinedObject()61     static EtsObject *GetUndefinedObject()
62     {
63         return nullptr;
64     }
65 
GetUndefinedWRef(ani_wref * result)66     static ani_status GetUndefinedWRef(ani_wref *result)
67     {
68         *result = nullptr;
69         return ANI_OK;
70     }
71 
GetUndefinedRef(ani_ref * result)72     static ani_status GetUndefinedRef(ani_ref *result)
73     {
74         *result = nullptr;
75         return ANI_OK;
76     }
77 
IsNull(ani_ref ref)78     bool IsNull(ani_ref ref)
79     {
80         ASSERT(!IsUndefined(ref));
81         return ToInternalType(ref)->GetCoreType() == GetCoroutine()->GetNullValue();
82     }
83 
IsNullishValue(ani_ref ref)84     bool IsNullishValue(ani_ref ref)
85     {
86         return IsUndefined(ref) || IsNull(ref);
87     }
88 
GetNullRef(ani_ref * result)89     ani_status GetNullRef(ani_ref *result)
90     {
91         EtsObject *nullObject = EtsObject::FromCoreType(GetCoroutine()->GetNullValue());
92         return AddLocalRef(nullObject, result);
93     }
94 
ToInternalType(ani_array array)95     EtsArray *ToInternalType(ani_array array)
96     {
97         ASSERT(!IsNullishValue(array));
98         return reinterpret_cast<EtsArray *>(GetInternalType(array));
99     }
100 
ToInternalType(ani_array_ref array)101     EtsObjectArray *ToInternalType(ani_array_ref array)
102     {
103         ASSERT(!IsNullishValue(array));
104         return reinterpret_cast<EtsObjectArray *>(GetInternalType(array));
105     }
106 
ToInternalType(ani_arraybuffer arraybuffer)107     EtsEscompatArrayBuffer *ToInternalType(ani_arraybuffer arraybuffer)
108     {
109         ASSERT(!IsNullishValue(arraybuffer));
110         return reinterpret_cast<EtsEscompatArrayBuffer *>(GetInternalType(arraybuffer));
111     }
112 
ToInternalType(ani_ref ref)113     EtsObject *ToInternalType(ani_ref ref)
114     {
115         if (IsUndefined(ref)) {
116             return GetUndefinedObject();
117         }
118         return GetInternalType(ref);
119     }
120 
ToInternalType(ani_object obj)121     EtsObject *ToInternalType(ani_object obj)
122     {
123         ASSERT(!IsNullishValue(obj));
124         return GetInternalType(obj);
125     }
126 
ToInternalType(ani_class cls)127     EtsClass *ToInternalType(ani_class cls)
128     {
129         ASSERT(!IsNullishValue(cls));
130         return reinterpret_cast<EtsClass *>(GetInternalType(cls));
131     }
132 
ToInternalType(ani_namespace ns)133     EtsNamespace *ToInternalType(ani_namespace ns)
134     {
135         ASSERT(!IsNullishValue(ns));
136         return reinterpret_cast<EtsNamespace *>(GetInternalType(ns));
137     }
138 
ToInternalType(ani_module module)139     EtsModule *ToInternalType(ani_module module)
140     {
141         ASSERT(!IsNullishValue(module));
142         return reinterpret_cast<EtsModule *>(GetInternalType(module));
143     }
144 
ToInternalType(ani_string str)145     EtsString *ToInternalType(ani_string str)
146     {
147         ASSERT(!IsNullishValue(str));
148         return reinterpret_cast<EtsString *>(GetInternalType(str));
149     }
150 
ToInternalType(ani_type type)151     EtsClass *ToInternalType(ani_type type)
152     {
153         ASSERT(!IsNullishValue(type));
154         return reinterpret_cast<EtsClass *>(GetInternalType(type));
155     }
156 
ToInternalType(ani_resolver resolver)157     EtsPromise *ToInternalType(ani_resolver resolver)
158     {
159         auto resolverRef = reinterpret_cast<ani_ref>(resolver);
160         EtsObject *obj = IsUndefined(resolverRef) ? GetUndefinedObject() : GetInternalType(resolverRef);
161         return reinterpret_cast<EtsPromise *>(obj);
162     }
163 
AddGlobalRef(ani_ref ref,ani_ref * result)164     ani_status AddGlobalRef(ani_ref ref, ani_ref *result)
165     {
166         if (IsUndefined(ref)) {
167             return GetUndefinedRef(result);
168         }
169         EtsObject *obj = ToInternalType(ref);
170         EtsReference *gref = GetRefStorage()->NewEtsRef(obj, EtsReference::EtsObjectType::GLOBAL);
171         ANI_CHECK_RETURN_IF_EQ(gref, nullptr, ANI_OUT_OF_REF);
172         *result = EtsRefToAniRef(gref);
173         return ANI_OK;
174     }
175 
DelGlobalRef(ani_ref gref)176     ani_status DelGlobalRef(ani_ref gref)
177     {
178         if (IsUndefined(gref)) {
179             return ANI_OK;
180         }
181         EtsReference *etsGRef = AniRefToEtsRef(gref);
182         ANI_CHECK_RETURN_IF_EQ(etsGRef->IsGlobal(), false, ANI_INCORRECT_REF);
183         GetRefStorage()->RemoveEtsRef(etsGRef);
184         return ANI_OK;
185     }
186 
AddWeakRef(ani_ref ref,ani_wref * result)187     ani_status AddWeakRef(ani_ref ref, ani_wref *result)
188     {
189         if (IsUndefined(ref)) {
190             return GetUndefinedWRef(result);
191         }
192         EtsObject *obj = ToInternalType(ref);
193         EtsReference *etsRef = GetRefStorage()->NewEtsRef(obj, EtsReference::EtsObjectType::WEAK);
194         ANI_CHECK_RETURN_IF_EQ(etsRef, nullptr, ANI_OUT_OF_REF);
195         *result = EtsRefToAniWRef(etsRef);
196         return ANI_OK;
197     }
198 
DelWeakRef(ani_wref wref)199     ani_status DelWeakRef(ani_wref wref)
200     {
201         if (IsUndefined(wref)) {
202             return ANI_OK;
203         }
204         EtsReference *etsGRef = AniWRefToEtsRef(wref);
205         ASSERT(etsGRef->IsWeak());
206         GetRefStorage()->RemoveEtsRef(etsGRef);
207         return ANI_OK;
208     }
209 
GetLocalRef(ani_wref wref,ani_boolean * wasReleasedResult,ani_ref * result)210     ani_status GetLocalRef(ani_wref wref, ani_boolean *wasReleasedResult, ani_ref *result)
211     {
212         if (IsUndefined(wref)) {
213             return GetUndefinedRef(result);
214         }
215         EtsReference *etsGRef = AniWRefToEtsRef(wref);
216         ASSERT(etsGRef->IsWeak());
217         EtsObject *obj = GetRefStorage()->GetEtsObject(etsGRef);
218         if (obj == nullptr) {
219             // Reference was freed.
220             *wasReleasedResult = ANI_TRUE;
221             *result = nullptr;
222             return ANI_OK;
223         }
224         *wasReleasedResult = ANI_FALSE;
225         return AddLocalRef(obj, result);
226     }
227 
AddGlobalRef(EtsObject * obj,ani_ref * result)228     ani_status AddGlobalRef(EtsObject *obj, ani_ref *result)
229     {
230         if (IsUndefined(obj)) {
231             return GetUndefinedRef(result);
232         }
233         EtsReference *gref = GetRefStorage()->NewEtsRef(obj, EtsReference::EtsObjectType::GLOBAL);
234         ANI_CHECK_RETURN_IF_EQ(gref, nullptr, ANI_OUT_OF_REF);
235         *result = EtsRefToAniRef(gref);
236         return ANI_OK;
237     }
238 
AddLocalRef(EtsObject * obj,ani_ref * result)239     ani_status AddLocalRef(EtsObject *obj, ani_ref *result)
240     {
241         if (IsUndefined(obj)) {
242             return GetUndefinedRef(result);
243         }
244         EtsReference *etsRef = GetRefStorage()->NewEtsRef(obj, EtsReference::EtsObjectType::LOCAL);
245         ANI_CHECK_RETURN_IF_EQ(etsRef, nullptr, ANI_OUT_OF_REF);
246         *result = EtsRefToAniRef(etsRef);
247         return ANI_OK;
248     }
249 
DelLocalRef(ani_ref ref)250     ani_status DelLocalRef(ani_ref ref)
251     {
252         if (IsUndefined(ref)) {
253             return ANI_OK;
254         }
255         EtsReference *etsRef = AniRefToEtsRef(ref);
256         ANI_CHECK_RETURN_IF_EQ(etsRef->IsLocal(), false, ANI_INCORRECT_REF);
257         GetRefStorage()->RemoveEtsRef(etsRef);
258         return ANI_OK;
259     }
260 
EnsureLocalEnoughRefs(ani_size nrRefs)261     ani_status EnsureLocalEnoughRefs(ani_size nrRefs)
262     {
263         ANI_CHECK_RETURN_IF_EQ(nrRefs, 0, ANI_INVALID_ARGS);
264         ANI_CHECK_RETURN_IF_GT(nrRefs, std::numeric_limits<uint32_t>::max(), ANI_OUT_OF_MEMORY);
265 
266         auto etsNrRefs = static_cast<uint32_t>(nrRefs);
267         bool ret = GetRefStorage()->EnsureLocalEtsCapacity(etsNrRefs);
268         ANI_CHECK_RETURN_IF_EQ(ret, false, ANI_OUT_OF_MEMORY);
269 
270         return ANI_OK;
271     }
272 
CreateLocalScope(ani_size nrRefs)273     ani_status CreateLocalScope(ani_size nrRefs)
274     {
275         ANI_CHECK_RETURN_IF_EQ(nrRefs, 0, ANI_INVALID_ARGS);
276         ANI_CHECK_RETURN_IF_GT(nrRefs, std::numeric_limits<uint32_t>::max(), ANI_OUT_OF_MEMORY);
277 
278         auto nrRefsU32 = static_cast<uint32_t>(nrRefs);
279         bool ret = GetRefStorage()->PushLocalEtsFrame(nrRefsU32);
280         ANI_CHECK_RETURN_IF_EQ(ret, false, ANI_OUT_OF_MEMORY);
281         return ANI_OK;
282     }
283 
DestroyLocalScope()284     ani_status DestroyLocalScope()
285     {
286         GetRefStorage()->PopLocalEtsFrame(nullptr);
287         return ANI_OK;
288     }
289 
CreateEscapeLocalScope(ani_size nrRefs)290     ani_status CreateEscapeLocalScope(ani_size nrRefs)
291     {
292         ANI_CHECK_RETURN_IF_EQ(nrRefs, 0, ANI_INVALID_ARGS);
293         ANI_CHECK_RETURN_IF_GT(nrRefs, std::numeric_limits<uint32_t>::max(), ANI_OUT_OF_MEMORY);
294 
295         auto nrRefsU32 = static_cast<uint32_t>(nrRefs);
296         bool ret = GetRefStorage()->PushLocalEtsFrame(nrRefsU32);
297         ANI_CHECK_RETURN_IF_EQ(ret, false, ANI_OUT_OF_MEMORY);
298         return ANI_OK;
299     }
300 
DestroyEscapeLocalScope(ani_ref ref,ani_ref * result)301     ani_status DestroyEscapeLocalScope(ani_ref ref, ani_ref *result)
302     {
303         if (IsUndefined(ref)) {
304             GetRefStorage()->PopLocalEtsFrame(nullptr);
305             return GetUndefinedRef(result);
306         }
307         EtsReference *resultEtsRef = AniRefToEtsRef(ref);
308         EtsReference *etsRef = GetRefStorage()->PopLocalEtsFrame(resultEtsRef);
309         ANI_CHECK_RETURN_IF_EQ(etsRef, nullptr, ANI_OUT_OF_REF);
310         *result = EtsRefToAniRef(etsRef);
311         return ANI_OK;
312     }
313 
314     NO_COPY_SEMANTIC(ManagedCodeAccessor);
315     NO_MOVE_SEMANTIC(ManagedCodeAccessor);
316 
317 private:
GetRefStorage()318     EtsReferenceStorage *GetRefStorage()
319     {
320         return env_->GetEtsReferenceStorage();
321     }
322 
GetInternalType(ani_ref ref)323     EtsObject *GetInternalType(ani_ref ref)
324     {
325         ASSERT(!IsUndefined(ref));
326         EtsReference *etsRef = AniRefToEtsRef(ref);
327         return GetRefStorage()->GetEtsObject(etsRef);
328     }
329 
AniRefToEtsRef(ani_ref ref)330     static EtsReference *AniRefToEtsRef(ani_ref ref)
331     {
332         auto etsRef = reinterpret_cast<EtsReference *>(ref);
333         ASSERT(etsRef != nullptr);
334         ASSERT(!etsRef->IsWeak());
335         return etsRef;
336     }
337 
EtsRefToAniRef(EtsReference * etsRef)338     static ani_ref EtsRefToAniRef(EtsReference *etsRef)
339     {
340         ASSERT(etsRef != nullptr);
341         ASSERT(!etsRef->IsWeak());
342         return reinterpret_cast<ani_ref>(etsRef);
343     }
344 
AniWRefToEtsRef(ani_wref wref)345     static EtsReference *AniWRefToEtsRef(ani_wref wref)
346     {
347         auto etsRef = reinterpret_cast<EtsReference *>(wref);
348         ASSERT(etsRef != nullptr);
349         ASSERT(etsRef->IsWeak());
350         return etsRef;
351     }
352 
EtsRefToAniWRef(EtsReference * etsRef)353     static ani_wref EtsRefToAniWRef(EtsReference *etsRef)
354     {
355         ASSERT(etsRef != nullptr);
356         ASSERT(etsRef->IsWeak());
357         return reinterpret_cast<ani_wref>(etsRef);
358     }
359 
360     PandaEnv *env_;
361 };
362 
363 class ScopedManagedCodeFix : public ManagedCodeAccessor {
364     class ExceptionData {
365     public:
ExceptionData(const char * name,const char * message)366         ExceptionData(const char *name, const char *message) : className_(name)
367         {
368             if (message != nullptr) {
369                 message_ = message;
370             }
371         }
372 
GetClassName()373         const char *GetClassName() const
374         {
375             return className_.c_str();
376         }
377 
GetMessage()378         const char *GetMessage() const
379         {
380             return message_ ? message_.value().c_str() : nullptr;
381         }
382 
383     private:
384         PandaString className_;
385         std::optional<PandaString> message_;
386     };
387 
388 public:
ScopedManagedCodeFix(PandaEnv * env)389     explicit ScopedManagedCodeFix(PandaEnv *env)
390         : ManagedCodeAccessor(env), alreadyInManaged_(ManagedThread::IsManagedScope())
391     {
392         ASSERT(env == PandaEnv::GetCurrent());
393 
394         if (alreadyInManaged_ && IsAccessFromManagedAllowed()) {
395             return;
396         }
397 
398         GetCoroutine()->ManagedCodeBegin();
399     }
ScopedManagedCodeFix(ani_env * env)400     explicit ScopedManagedCodeFix(ani_env *env) : ScopedManagedCodeFix(PandaEnv::FromAniEnv(env)) {}
401 
~ScopedManagedCodeFix()402     ~ScopedManagedCodeFix()
403     {
404         if (exceptionData_) {
405             // NOTE: Throw exception
406         }
407 
408         if (!alreadyInManaged_) {
409             GetCoroutine()->ManagedCodeEnd();
410         }
411     }
412 
HasPendingException()413     bool HasPendingException()
414     {
415         return exceptionData_ || GetCoroutine()->HasPendingException();
416     }
417 
418     NO_COPY_SEMANTIC(ScopedManagedCodeFix);
419     NO_MOVE_SEMANTIC(ScopedManagedCodeFix);
420 
421 private:
IsAccessFromManagedAllowed()422     bool IsAccessFromManagedAllowed()
423     {
424 #ifndef NDEBUG
425         auto stack = StackWalker::Create(GetCoroutine());
426         auto method = EtsMethod::FromRuntimeMethod(stack.GetMethod());
427         ASSERT(method != nullptr);
428         return method->IsFastNative();
429 #else
430         return true;
431 #endif  // NDEBUG
432     }
433 
434     PandaUniquePtr<ExceptionData> exceptionData_;
435     bool alreadyInManaged_;
436 };
437 
438 }  // namespace ark::ets::ani
439 
440 #endif  // PANDA_PLUGINS_ETS_RUNTIME_ANI_SCOPED_OBJECTS_FIX_H
441