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