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