/* * Copyright (c) 2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef FOUNDATION_ACE_NAPI_NATIVE_ENGINE_NATIVE_ASYNC_HOOK_CONTEXT_H #define FOUNDATION_ACE_NAPI_NATIVE_ENGINE_NATIVE_ASYNC_HOOK_CONTEXT_H #include "callback_scope_manager/native_callback_scope_manager.h" #include "ecmascript/napi/include/jsnapi.h" #include "native_value.h" #include "native_engine.h" #include "utils/log.h" class NativeAsyncHookContext; static panda::JSValueRef* InternalMakeCallback(NativeEngine* engine, panda::FunctionRef* funRef, panda::JSValueRef* obj, panda::JSValueRef *const argv[], size_t argc, NativeAsyncHookContext* asyncContext); enum CallbackScopeCode { CALLBACK_SCOPE_OK = 0, CALLBACK_SCOPE_INVALID_ARG, CALLBACK_SCOPE_MISMATCH, }; class NativeAsyncHookContext { public: NativeAsyncHookContext(NativeEngine* env, panda::Local resourceObject, const panda::Local resourceName, bool isExternalResource) : env_(env), resource_(env->GetEcmaVm(), resourceObject) { asyncId_ = env->NewAsyncId(); triggerAsyncId_ = env->GetDefaultTriggerAsyncId(); lostReference_ = false; if (isExternalResource) { resource_.SetWeak(); resource_.SetWeakCallback(reinterpret_cast(this), FreeGlobalCallBack, NativeFinalizeCallBack); } NativeAsyncWrap::EmitAsyncInit(env, resourceObject, resourceName, asyncId_, triggerAsyncId_); } ~NativeAsyncHookContext() { resource_.FreeGlobalHandleAddr(); lostReference_ = true; NativeAsyncWrap::EmitDestroy(env_, asyncId_); } static void FreeGlobalCallBack(void* ref) { auto that = reinterpret_cast(ref); that->resource_.FreeGlobalHandleAddr(); that->lostReference_ = true; } static void NativeFinalizeCallBack(void* ref) {} inline AsyncIdInfo GetAsyncIdInfo() { return {asyncId_, triggerAsyncId_}; } inline NativeCallbackScope* OpenCallbackScope() { EnsureReference(); auto callbackScopeManager = env_->GetCallbackScopeManager(); if (callbackScopeManager == nullptr) { return nullptr; } auto callbackScope = callbackScopeManager->Open(env_, resource_.ToLocal(), GetAsyncIdInfo()); callbackScopeManager->IncrementOpenCallbackScopes(); return callbackScope; } inline void EnsureReference() { if (lostReference_) { auto ecmaVm = env_->GetEcmaVm(); panda::Global resource(ecmaVm, panda::ObjectRef::New(ecmaVm)); resource_ = resource; lostReference_ = false; } } static CallbackScopeCode CloseCallbackScope(NativeEngine* env, NativeCallbackScope* scope) { auto callbackScopeManager = env->GetCallbackScopeManager(); if (callbackScopeManager == nullptr) { return CALLBACK_SCOPE_INVALID_ARG; } if (callbackScopeManager->GetOpenCallbackScopes() > 0) { callbackScopeManager->DecrementOpenCallbackScopes(); callbackScopeManager->Close(scope); return CALLBACK_SCOPE_OK; } else { return CALLBACK_SCOPE_MISMATCH; } } panda::JSValueRef* MakeCallback(panda::FunctionRef* funRef, panda::JSValueRef* obj, panda::JSValueRef *const argv[], size_t argc) { EnsureReference(); panda::JSValueRef* rst = InternalMakeCallback(env_, funRef, obj, argv, argc, this); return rst; } private: NativeEngine* env_ {nullptr}; double asyncId_ = 0; double triggerAsyncId_ = 0; panda::Global resource_; bool lostReference_ = false; }; static panda::FunctionRef* AsyncHooksCallbackTrampoline() { return nullptr; // not support async_hook yet, and the actions need to be improved in the future } static void CloseScope(NativeAsyncHookContext* nativeAsyncContext, NativeCallbackScopeManager* callbackScopeMgr, NativeCallbackScope* callbackScope, NativeEngine* engine) { if (nativeAsyncContext != nullptr) { nativeAsyncContext->CloseCallbackScope(engine, callbackScope); } else { if (callbackScopeMgr != nullptr) { callbackScopeMgr->Close(callbackScope); } } } static panda::JSValueRef* InternalMakeCallback(NativeEngine* engine, panda::FunctionRef* funRef, panda::JSValueRef* obj, panda::JSValueRef* const argv[], size_t argc, NativeAsyncHookContext* asyncContext) { auto vm = engine->GetEcmaVm(); if (funRef == nullptr) { HILOG_ERROR("funRef is nullptr!"); return *panda::JSValueRef::Undefined(vm); } bool useAsyncHooksTrampoline = false; panda::FunctionRef* rstFunRef = AsyncHooksCallbackTrampoline(); if (rstFunRef != nullptr) { useAsyncHooksTrampoline = true; } NativeCallbackScopeManager* callbackScopeMgr = nullptr; NativeCallbackScope* callbackScope; if (asyncContext == nullptr) { callbackScopeMgr = engine->GetCallbackScopeManager(); callbackScope = callbackScopeMgr->Open(engine, panda::Local(reinterpret_cast(obj)), {0, 0}); } else { callbackScope = asyncContext->OpenCallbackScope(); } panda::JSValueRef* callBackRst; if (useAsyncHooksTrampoline) { callBackRst = nullptr; // not support async_hook yet, and the actions need to be improved in the future } else { callBackRst = funRef->CallForNapi(vm, obj, argv, argc); } if (callbackScope != nullptr) { if (callBackRst == nullptr) { callbackScope->MarkAsFailed(); } callbackScope->Close(); } CloseScope(asyncContext, callbackScopeMgr, callbackScope, engine); return callBackRst; } panda::JSValueRef* MakeCallback(NativeEngine* engine, panda::FunctionRef* funRef, panda::JSValueRef* obj, size_t argc, panda::JSValueRef* const argv[], NativeAsyncHookContext* asyncContext) { auto vm = engine->GetEcmaVm(); panda::JSValueRef* rst = InternalMakeCallback(engine, funRef, obj, argv, argc, asyncContext); NativeCallbackScopeManager* callbackScopeMgr = engine->GetCallbackScopeManager(); size_t depth = callbackScopeMgr->GetAsyncCallbackScopeDepth(); if (rst == nullptr && depth == 0) { return *panda::JSValueRef::Undefined(vm); } return rst; } #endif /* FOUNDATION_ACE_NAPI_NATIVE_ENGINE_NATIVE_ASYNC_HOOK_CONTEXT_H */