1 /* 2 * Copyright (c) 2021 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 #ifndef PREFERENCES_JSKIT_NAPI_ASYNC_PROXY_H 16 #define PREFERENCES_JSKIT_NAPI_ASYNC_PROXY_H 17 #include <vector> 18 19 #include "napi/native_api.h" 20 #include "napi/native_common.h" 21 #include "napi/native_node_api.h" 22 #include "securec.h" 23 24 namespace OHOS { 25 namespace AppDataMgrJsKit { 26 constexpr int MAX_INPUT_COUNT = 10; 27 constexpr int OK = 0; 28 constexpr int ERR = -1; 29 30 // T inherits AysncContext 31 template<class T> class NapiAsyncProxy { 32 public: 33 constexpr static int RESULT_COUNT = 2; 34 using InputParser = void (*)(const napi_env &, const napi_value &, T *); 35 using NapiAsyncExecute = int (*)(T *); 36 using NapiAsyncComplete = int (*)(T *, napi_value &); 37 38 // AsyncContext base 39 struct AysncContext { AysncContextAysncContext40 AysncContext() 41 { 42 } ~AysncContextAysncContext43 virtual ~AysncContext() 44 { 45 if (env == nullptr) { 46 return; 47 } 48 napi_delete_async_work(env, work); 49 napi_delete_reference(env, callbackRef); 50 } 51 napi_env env = nullptr; 52 napi_callback_info info = nullptr; 53 napi_async_work work = nullptr; 54 napi_deferred deferred = nullptr; 55 napi_ref callbackRef = nullptr; 56 NapiAsyncExecute execFunc = nullptr; 57 int execStatus = ERR; 58 NapiAsyncComplete completeFunc = nullptr; 59 void *boundObj = nullptr; 60 }; 61 62 public: Init(napi_env env,napi_callback_info info)63 void Init(napi_env env, napi_callback_info info) 64 { 65 asyncContext = new T(); 66 if (asyncContext == nullptr) { 67 return; 68 } 69 asyncContext->env = env; 70 asyncContext->info = info; 71 } 72 ~NapiAsyncProxy()73 ~NapiAsyncProxy() 74 { 75 if (asyncContext == nullptr) { 76 return; 77 } 78 79 delete asyncContext; 80 asyncContext = nullptr; 81 } 82 DefParserThis(const napi_env & env,const napi_value & self,T * context)83 static void DefParserThis(const napi_env &env, const napi_value &self, T *context) 84 { 85 napi_unwrap(env, self, &context->boundObj); 86 } 87 88 void ParseInputs(const std::vector<InputParser> &parsers, InputParser parserThis = DefParserThis) 89 { 90 if (asyncContext == nullptr) { 91 return; 92 } 93 94 napi_value thisObj = nullptr; 95 size_t argc = parsers.size() + 1; 96 napi_value args[MAX_INPUT_COUNT] = { 0 }; 97 napi_get_cb_info(asyncContext->env, asyncContext->info, &argc, args, &thisObj, nullptr); 98 for (size_t i = 0; i < argc && argc <= MAX_INPUT_COUNT; i++) { 99 if (i >= parsers.size()) { 100 napi_valuetype valueType = napi_undefined; 101 napi_typeof(asyncContext->env, args[i], &valueType); 102 if (valueType == napi_function) { 103 napi_create_reference(asyncContext->env, args[i], 1, &asyncContext->callbackRef); 104 } 105 break; 106 } 107 auto *parserFunction = parsers[i]; 108 if (parserFunction != nullptr) { 109 parserFunction(asyncContext->env, args[i], this->asyncContext); 110 } 111 } 112 parserThis(asyncContext->env, thisObj, asyncContext); 113 } 114 DoAsyncWork(std::string resourceName,NapiAsyncExecute execFunc,NapiAsyncComplete completeFunc)115 napi_value DoAsyncWork(std::string resourceName, NapiAsyncExecute execFunc, NapiAsyncComplete completeFunc) 116 { 117 if (asyncContext == nullptr) { 118 return nullptr; 119 } 120 121 napi_value ret = nullptr; 122 if (asyncContext->callbackRef == nullptr) { 123 napi_create_promise(asyncContext->env, &asyncContext->deferred, &ret); 124 } else { 125 napi_get_undefined(asyncContext->env, &ret); 126 } 127 128 napi_value resource = nullptr; 129 napi_create_string_utf8(asyncContext->env, resourceName.c_str(), NAPI_AUTO_LENGTH, &resource); 130 asyncContext->execFunc = execFunc; 131 asyncContext->completeFunc = completeFunc; 132 NAPI_CALL_BASE(asyncContext->env, napi_create_async_work(asyncContext->env, nullptr, resource, 133 [](napi_env env, void *data) { 134 T *context = (T *)data; 135 context->execStatus = context->execFunc(context); 136 }, 137 [](napi_env env, napi_status status, void *data) { 138 T *context = (T *)data; 139 napi_value output = nullptr; 140 int completeStatus = context->completeFunc(context, output); 141 napi_value result[RESULT_COUNT] = { 0 }; 142 if (context->execStatus == OK && completeStatus == OK) { 143 napi_get_undefined(env, &result[0]); 144 result[1] = output; 145 } else { 146 napi_value message = nullptr; 147 napi_create_string_utf8(env, "async call failed", NAPI_AUTO_LENGTH, &message); 148 napi_create_error(env, nullptr, message, &result[0]); 149 napi_get_undefined(env, &result[1]); 150 } 151 if (context->deferred) { 152 // promise 153 if (context->execStatus == OK && completeStatus == OK) { 154 napi_resolve_deferred(env, context->deferred, result[1]); 155 } else { 156 napi_reject_deferred(env, context->deferred, result[0]); 157 } 158 } else { 159 // callback 160 napi_value callback = nullptr; 161 napi_get_reference_value(env, context->callbackRef, &callback); 162 napi_value callbackResult = nullptr; 163 napi_call_function(env, nullptr, callback, RESULT_COUNT, result, &callbackResult); 164 } 165 delete context; 166 }, 167 (void *)asyncContext, &asyncContext->work), ret); 168 NAPI_CALL_BASE(asyncContext->env, napi_queue_async_work(asyncContext->env, asyncContext->work), ret); 169 return ret; 170 } 171 172 private: 173 T *asyncContext; 174 }; 175 } // namespace AppDataMgrJsKit 176 } // namespace OHOS 177 #endif 178