1/* 2 * Copyright (c) 2024 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 FOUNDATION_ACE_NAPI_NATIVE_ENGINE_NATIVE_ASYNC_HOOK_CONTEXT_H 17#define FOUNDATION_ACE_NAPI_NATIVE_ENGINE_NATIVE_ASYNC_HOOK_CONTEXT_H 18 19#include "callback_scope_manager/native_callback_scope_manager.h" 20#include "ecmascript/napi/include/jsnapi.h" 21#include "native_value.h" 22#include "native_engine.h" 23#include "utils/log.h" 24 25class NativeAsyncHookContext; 26static panda::JSValueRef* InternalMakeCallback(NativeEngine* engine, panda::FunctionRef* funRef, 27 panda::JSValueRef* obj, panda::JSValueRef *const argv[], 28 size_t argc, NativeAsyncHookContext* asyncContext); 29 30enum CallbackScopeCode { 31 CALLBACK_SCOPE_OK = 0, 32 CALLBACK_SCOPE_INVALID_ARG, 33 CALLBACK_SCOPE_MISMATCH, 34}; 35 36class NativeAsyncHookContext { 37public: 38 NativeAsyncHookContext(NativeEngine* env, 39 panda::Local<panda::ObjectRef> resourceObject, 40 const panda::Local<panda::StringRef> resourceName, 41 bool isExternalResource) : env_(env), resource_(env->GetEcmaVm(), resourceObject) 42 { 43 asyncId_ = env->NewAsyncId(); 44 triggerAsyncId_ = env->GetDefaultTriggerAsyncId(); 45 lostReference_ = false; 46 if (isExternalResource) { 47 resource_.SetWeak(); 48 resource_.SetWeakCallback(reinterpret_cast<void*>(this), FreeGlobalCallBack, NativeFinalizeCallBack); 49 } 50 51 NativeAsyncWrap::EmitAsyncInit(env, 52 resourceObject, 53 resourceName, 54 asyncId_, 55 triggerAsyncId_); 56 } 57 58 ~NativeAsyncHookContext() 59 { 60 resource_.FreeGlobalHandleAddr(); 61 lostReference_ = true; 62 NativeAsyncWrap::EmitDestroy(env_, asyncId_); 63 } 64 65 static void FreeGlobalCallBack(void* ref) 66 { 67 auto that = reinterpret_cast<NativeAsyncHookContext*>(ref); 68 that->resource_.FreeGlobalHandleAddr(); 69 that->lostReference_ = true; 70 } 71 72 static void NativeFinalizeCallBack(void* ref) {} 73 74 inline AsyncIdInfo GetAsyncIdInfo() 75 { 76 return {asyncId_, triggerAsyncId_}; 77 } 78 79 inline NativeCallbackScope* OpenCallbackScope() 80 { 81 EnsureReference(); 82 auto callbackScopeManager = env_->GetCallbackScopeManager(); 83 if (callbackScopeManager == nullptr) { 84 return nullptr; 85 } 86 87 auto callbackScope = callbackScopeManager->Open(env_, resource_.ToLocal(), GetAsyncIdInfo()); 88 callbackScopeManager->IncrementOpenCallbackScopes(); 89 90 return callbackScope; 91 } 92 93 inline void EnsureReference() 94 { 95 if (lostReference_) { 96 auto ecmaVm = env_->GetEcmaVm(); 97 panda::Global<panda::ObjectRef> resource(ecmaVm, panda::ObjectRef::New(ecmaVm)); 98 resource_ = resource; 99 lostReference_ = false; 100 } 101 } 102 103 static CallbackScopeCode CloseCallbackScope(NativeEngine* env, NativeCallbackScope* scope) 104 { 105 auto callbackScopeManager = env->GetCallbackScopeManager(); 106 if (callbackScopeManager == nullptr) { 107 return CALLBACK_SCOPE_INVALID_ARG; 108 } 109 if (callbackScopeManager->GetOpenCallbackScopes() > 0) { 110 callbackScopeManager->DecrementOpenCallbackScopes(); 111 callbackScopeManager->Close(scope); 112 return CALLBACK_SCOPE_OK; 113 } else { 114 return CALLBACK_SCOPE_MISMATCH; 115 } 116 } 117 118 panda::JSValueRef* MakeCallback(panda::FunctionRef* funRef, panda::JSValueRef* obj, 119 panda::JSValueRef *const argv[], size_t argc) 120 { 121 EnsureReference(); 122 panda::JSValueRef* rst = InternalMakeCallback(env_, funRef, obj, argv, argc, this); 123 return rst; 124 } 125private: 126 NativeEngine* env_ {nullptr}; 127 double asyncId_ = 0; 128 double triggerAsyncId_ = 0; 129 panda::Global<panda::ObjectRef> resource_; 130 bool lostReference_ = false; 131}; 132 133static panda::FunctionRef* AsyncHooksCallbackTrampoline() 134{ 135 return nullptr; // not support async_hook yet, and the actions need to be improved in the future 136} 137 138static void CloseScope(NativeAsyncHookContext* nativeAsyncContext, 139 NativeCallbackScopeManager* callbackScopeMgr, 140 NativeCallbackScope* callbackScope, 141 NativeEngine* engine) 142{ 143 if (nativeAsyncContext != nullptr) { 144 nativeAsyncContext->CloseCallbackScope(engine, callbackScope); 145 } else { 146 if (callbackScopeMgr != nullptr) { 147 callbackScopeMgr->Close(callbackScope); 148 } 149 } 150} 151 152static panda::JSValueRef* InternalMakeCallback(NativeEngine* engine, panda::FunctionRef* funRef, 153 panda::JSValueRef* obj, panda::JSValueRef* const argv[], 154 size_t argc, NativeAsyncHookContext* asyncContext) 155{ 156 auto vm = engine->GetEcmaVm(); 157 if (funRef == nullptr) { 158 HILOG_ERROR("funRef is nullptr!"); 159 return *panda::JSValueRef::Undefined(vm); 160 } 161 162 bool useAsyncHooksTrampoline = false; 163 panda::FunctionRef* rstFunRef = AsyncHooksCallbackTrampoline(); 164 if (rstFunRef != nullptr) { 165 useAsyncHooksTrampoline = true; 166 } 167 NativeCallbackScopeManager* callbackScopeMgr = nullptr; 168 NativeCallbackScope* callbackScope; 169 if (asyncContext == nullptr) { 170 callbackScopeMgr = engine->GetCallbackScopeManager(); 171 callbackScope = callbackScopeMgr->Open(engine, 172 panda::Local<panda::ObjectRef>(reinterpret_cast<uintptr_t>(obj)), {0, 0}); 173 } else { 174 callbackScope = asyncContext->OpenCallbackScope(); 175 } 176 177 panda::JSValueRef* callBackRst; 178 if (useAsyncHooksTrampoline) { 179 callBackRst = nullptr; // not support async_hook yet, and the actions need to be improved in the future 180 } else { 181 callBackRst = funRef->CallForNapi(vm, obj, argv, argc); 182 } 183 184 if (callbackScope != nullptr) { 185 if (callBackRst == nullptr) { 186 callbackScope->MarkAsFailed(); 187 } 188 callbackScope->Close(); 189 } 190 191 CloseScope(asyncContext, callbackScopeMgr, callbackScope, engine); 192 return callBackRst; 193} 194 195panda::JSValueRef* MakeCallback(NativeEngine* engine, panda::FunctionRef* funRef, 196 panda::JSValueRef* obj, size_t argc, 197 panda::JSValueRef* const argv[], NativeAsyncHookContext* asyncContext) 198{ 199 auto vm = engine->GetEcmaVm(); 200 panda::JSValueRef* rst = InternalMakeCallback(engine, funRef, obj, argv, argc, asyncContext); 201 NativeCallbackScopeManager* callbackScopeMgr = engine->GetCallbackScopeManager(); 202 size_t depth = callbackScopeMgr->GetAsyncCallbackScopeDepth(); 203 if (rst == nullptr && depth == 0) { 204 return *panda::JSValueRef::Undefined(vm); 205 } 206 return rst; 207} 208 209#endif /* FOUNDATION_ACE_NAPI_NATIVE_ENGINE_NATIVE_ASYNC_HOOK_CONTEXT_H */ 210