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