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