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 
25 class NativeAsyncHookContext;
26 static panda::JSValueRef* InternalMakeCallback(NativeEngine* engine, panda::FunctionRef* funRef,
27                                                panda::JSValueRef* obj, panda::JSValueRef *const argv[],
28                                                size_t argc, NativeAsyncHookContext* asyncContext);
29 
30 enum CallbackScopeCode {
31     CALLBACK_SCOPE_OK = 0,
32     CALLBACK_SCOPE_INVALID_ARG,
33     CALLBACK_SCOPE_MISMATCH,
34 };
35 
36 class NativeAsyncHookContext {
37 public:
NativeAsyncHookContext(NativeEngine * env,panda::Local<panda::ObjectRef> resourceObject,const panda::Local<panda::StringRef> resourceName,bool isExternalResource)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 
~NativeAsyncHookContext()58     ~NativeAsyncHookContext()
59     {
60         resource_.FreeGlobalHandleAddr();
61         lostReference_ = true;
62         NativeAsyncWrap::EmitDestroy(env_, asyncId_);
63     }
64 
FreeGlobalCallBack(void * ref)65     static void FreeGlobalCallBack(void* ref)
66     {
67         auto that = reinterpret_cast<NativeAsyncHookContext*>(ref);
68         that->resource_.FreeGlobalHandleAddr();
69         that->lostReference_ = true;
70     }
71 
NativeFinalizeCallBack(void * ref)72     static void NativeFinalizeCallBack(void* ref) {}
73 
GetAsyncIdInfo()74     inline AsyncIdInfo GetAsyncIdInfo()
75     {
76         return {asyncId_, triggerAsyncId_};
77     }
78 
OpenCallbackScope()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 
EnsureReference()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 
CloseCallbackScope(NativeEngine * env,NativeCallbackScope * scope)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 
MakeCallback(panda::FunctionRef * funRef,panda::JSValueRef * obj,panda::JSValueRef * const argv[],size_t argc)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     }
125 private:
126     NativeEngine* env_ {nullptr};
127     double asyncId_ = 0;
128     double triggerAsyncId_ = 0;
129     panda::Global<panda::ObjectRef> resource_;
130     bool lostReference_ = false;
131 };
132 
AsyncHooksCallbackTrampoline()133 static panda::FunctionRef* AsyncHooksCallbackTrampoline()
134 {
135     return nullptr; // not support async_hook yet, and the actions need to be improved in the future
136 }
137 
CloseScope(NativeAsyncHookContext * nativeAsyncContext,NativeCallbackScopeManager * callbackScopeMgr,NativeCallbackScope * callbackScope,NativeEngine * engine)138 static 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 
InternalMakeCallback(NativeEngine * engine,panda::FunctionRef * funRef,panda::JSValueRef * obj,panda::JSValueRef * const argv[],size_t argc,NativeAsyncHookContext * asyncContext)152 static 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 
MakeCallback(NativeEngine * engine,panda::FunctionRef * funRef,panda::JSValueRef * obj,size_t argc,panda::JSValueRef * const argv[],NativeAsyncHookContext * asyncContext)195 panda::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