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