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 NAPI_BASE_H
17 #define NAPI_BASE_H
18 
19 #include <memory>
20 
21 #include "napi_common_utils.h"
22 #include "napi/native_api.h"
23 #include "napi/native_common.h"
24 
25 namespace OHOS::UpdateEngine {
26 template <typename T> class NapiBase {
27 #define GET_PARAMS(env, info, num)    \
28     size_t argc = num;                \
29     napi_value args[num] = {nullptr}; \
30     napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)
31 
32 public:
33     NapiBase() = default;
34     ~NapiBase() = default;
35 
HandleFunc(napi_env env,napi_callback_info info,std::unique_ptr<T> & clientContext)36     static napi_value HandleFunc(napi_env env, napi_callback_info info,
37         std::unique_ptr<T> &clientContext)
38     {
39         if (clientContext == nullptr) {
40             ENGINE_LOGI("HandleFunc clientContext is null");
41             return nullptr;
42         }
43         std::string method = clientContext->method_;
44         ENGINE_LOGI("HandleFunc method: %{public}s", method.c_str());
45         napi_value result = clientContext->getNapiParam_(env, info, clientContext);
46         if (result == nullptr) {
47             ENGINE_LOGE("HandleFunc GetMigrateStatusParam fail");
48             return nullptr;
49         }
50         if (!Execute(env, clientContext)) {
51             ENGINE_LOGE("HandleFunc Execute error");
52             return result;
53         }
54         ENGINE_LOGI("HandleFunc method: %{public}s complete", method.c_str());
55         return result;
56     }
57 
GetCallbackParam(napi_env env,uint32_t argNum,size_t argc,napi_value args[],std::unique_ptr<T> & clientContext)58     static napi_value GetCallbackParam(napi_env env, uint32_t argNum, size_t argc, napi_value args[],
59         std::unique_ptr<T> &clientContext)
60     {
61         // 接口调用返回值,非返回内容
62         napi_value result = nullptr;
63         if (argc >= argNum) {
64             uint32_t callbackPosition = argNum - 1;
65             napi_valuetype callbackValueType;
66             napi_typeof(env, args[callbackPosition], &callbackValueType);
67             std::vector<std::pair<std::string, std::string>> paramInfos;
68             paramInfos.emplace_back("callback", "napi_function");
69             PARAM_CHECK(callbackValueType == napi_function, NapiCommonUtils::NapiThrowParamError(env, paramInfos);
70                return nullptr, "Failed to GetCallbackParam");
71             napi_create_reference(env, args[callbackPosition], 1, &clientContext->callbackRef_);
72             napi_get_undefined(env, &result); // 创建接口返回值对象
73         } else {
74             napi_create_promise(env, &clientContext->deferred_, &result);
75         }
76         return result;
77     }
78 
Execute(napi_env env,std::unique_ptr<T> & clientContext)79     static bool Execute(napi_env env, std::unique_ptr<T> &clientContext)
80     {
81         napi_value workName;
82         napi_create_string_utf8(env, clientContext->method_.c_str(), NAPI_AUTO_LENGTH, &workName);
83         if (napi_create_async_work(env, nullptr, workName, clientContext->executeFunc_, NapiBase::Complete,
84                                    static_cast<void *>(clientContext.get()), &clientContext->work_) != napi_ok) {
85             ENGINE_LOGE("Failed to create async work for %{public}s", clientContext->method_.c_str());
86             return false;
87         }
88         if (napi_queue_async_work_with_qos(env, clientContext->work_, napi_qos_default) != napi_ok) {
89             ENGINE_LOGE("Failed to queue async work for %{public}s", clientContext->method_.c_str());
90             return false;
91         }
92         ENGINE_LOGI("Execute finish");
93         clientContext.release(); // unique_ptr release之后,释放指针的控制权,后续由complete里面释放指针内容
94         return true;
95     }
96 
Complete(napi_env env,napi_status status,void * data)97     static void Complete(napi_env env, napi_status status, void *data)
98     {
99         if (data == nullptr) {
100             ENGINE_LOGE("Complete, data is null");
101             return;
102         }
103         constexpr size_t resultLen = 2;
104         T *clientContext = static_cast<T *>(data);
105         if (clientContext == nullptr) {
106             ENGINE_LOGE("Complete clientContext is null");
107             return;
108         }
109 
110         napi_value finalResult = nullptr;
111         if (clientContext->createValueFunc_ != nullptr) {
112             // 执行结果转换函数
113             finalResult = clientContext->createValueFunc_(env, *clientContext);
114         }
115 
116         if (clientContext->ipcRequestCode_ != 0) {
117             // ipc失败,获取失败原因
118             clientContext->getIpcBusinessError_(clientContext->method_, clientContext->ipcRequestCode_,
119                 clientContext->businessError_);
120         }
121 
122         napi_value result[resultLen] = { nullptr, nullptr };
123         bool isSuccess = BuildResult(env, clientContext, finalResult, result);
124         if (clientContext->deferred_) { // promise调用
125             ExecutePromiseFunc(env, clientContext, result, resultLen, isSuccess);
126         } else {
127             ExecuteCallbackFunc(env, clientContext, result, resultLen);
128         }
129         napi_delete_async_work(env, clientContext->work_);
130         delete clientContext; // Execute 中释放控制权的指针,在此处释放
131         clientContext = nullptr;
132     }
133 
ExecutePromiseFunc(napi_env env,T * clientContext,napi_value const * result,size_t len,bool isSuccess)134     static void ExecutePromiseFunc(napi_env env, T *clientContext, napi_value const * result, size_t len,
135         bool isSuccess)
136     {
137         constexpr size_t resultLength = 2;
138         if (len < resultLength) {
139             ENGINE_LOGE("length error:%{public}zu", len);
140             return;
141         }
142         napi_status callbackStatus = isSuccess ? napi_resolve_deferred(env, clientContext->deferred_, result[1]) :
143                                                  napi_reject_deferred(env, clientContext->deferred_, result[0]);
144         if (callbackStatus != napi_ok) {
145             ENGINE_LOGE("ExecutePromiseFunc error: %{public}d", callbackStatus);
146         }
147     }
148 
ExecuteCallbackFunc(napi_env env,T * clientContext,napi_value * result,size_t len)149     static void ExecuteCallbackFunc(napi_env env, T *clientContext, napi_value *result, size_t len)
150     {
151         napi_value callback = nullptr;
152         napi_status resultStatus = napi_get_reference_value(env, clientContext->callbackRef_, &callback);
153         if (resultStatus != napi_ok) {
154             ENGINE_LOGE("napi_get_reference_value failed result=%{public}d", resultStatus);
155             return;
156         }
157         napi_value userRet = nullptr;
158         resultStatus = napi_call_function(env, nullptr, callback, len, result, &userRet);
159         if (resultStatus != napi_ok) {
160             ENGINE_LOGE("napi_call_function failed result=%{public}d", resultStatus);
161             return;
162         }
163         resultStatus = napi_delete_reference(env, clientContext->callbackRef_);
164         if (resultStatus != napi_ok) {
165             ENGINE_LOGE("napi_delete_reference failed result=%{public}d", resultStatus);
166         }
167     }
168 
BuildResult(napi_env env,const T * clientContext,napi_value finalResult,napi_value * result)169     static bool BuildResult(napi_env env, const T *clientContext, napi_value finalResult, napi_value *result)
170     {
171         bool isSuccess = clientContext->businessError_.errorNum == CallResult::SUCCESS;
172         if (isSuccess) {
173             napi_get_undefined(env, &result[0]);
174             result[1] = finalResult;
175         } else {
176             NapiCommonUtils::BuildBusinessError(env, result[0], clientContext->businessError_);
177             napi_get_undefined(env, &result[1]);
178         }
179         return isSuccess;
180     }
181 };
182 } // namespace OHOS::UpdateEngine
183 #endif // NAPI_BASE_H