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