1 /*
2  * Copyright (c) 2023 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 #include "intell_voice_napi_queue.h"
17 #include "intell_voice_common_napi.h"
18 #include "intell_voice_napi_util.h"
19 #include "intell_voice_log.h"
20 
21 #define LOG_TAG "IntellVoiceNapiQueue"
22 
23 using namespace std;
24 
25 namespace OHOS {
26 namespace IntellVoiceNapi {
AsyncContext(napi_env env)27 AsyncContext::AsyncContext(napi_env env) : env_(env)
28 {
29     INTELL_VOICE_LOG_INFO("enter");
30 }
31 
~AsyncContext()32 AsyncContext::~AsyncContext()
33 {
34     INTELL_VOICE_LOG_INFO("enter");
35     if (env_ != nullptr) {
36         if (work_ != nullptr) {
37             napi_delete_async_work(env_, work_);
38         }
39         if (callbackRef_ != nullptr) {
40             napi_delete_reference(env_, callbackRef_);
41         }
42         env_ = nullptr;
43     }
44 }
45 
GetCbInfo(napi_env env,napi_callback_info info,size_t callBackIndex,CbInfoParser parser)46 bool AsyncContext::GetCbInfo(napi_env env, napi_callback_info info, size_t callBackIndex, CbInfoParser parser)
47 {
48     INTELL_VOICE_LOG_INFO("enter");
49     napi_status status;
50     napi_value jsThis = nullptr;
51     const int32_t refCount = 1;
52     size_t argc = ARGC_MAX;
53     napi_value args[ARGC_MAX] = { nullptr };
54 
55     status = napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
56     CHECK_CONDITION_RETURN_FALSE((status != napi_ok), "Failed to get cb info");
57 
58     status = napi_unwrap(env, jsThis, &instanceNapi_);
59     CHECK_CONDITION_RETURN_FALSE((status != napi_ok), "Failed to get engine napi instance");
60 
61     if (argc > callBackIndex) {
62         napi_valuetype valueType = napi_undefined;
63         napi_typeof(env, args[callBackIndex], &valueType);
64         CHECK_CONDITION_RETURN_FALSE((valueType != napi_function), "Failed to get asyncCallback, type mismatch");
65 
66         status = napi_create_reference(env, args[callBackIndex], refCount, &callbackRef_);
67         CHECK_CONDITION_RETURN_FALSE((status != napi_ok), "Failed to create callback reference");
68     }
69 
70     if (parser) {
71         return parser(argc, args);
72     }
73 
74     return true;
75 }
76 
AsyncWork(napi_env env,shared_ptr<AsyncContext> context,const string & name,AsyncExecute execute)77 napi_value NapiAsync::AsyncWork(napi_env env, shared_ptr<AsyncContext> context, const string &name,
78     AsyncExecute execute)
79 {
80     INTELL_VOICE_LOG_INFO("enter");
81     napi_value result = nullptr;
82     if (context->callbackRef_ == nullptr) {
83         napi_create_promise(env, &context->deferred_, &result);
84     } else {
85         napi_get_undefined(env, &result);
86     }
87 
88     napi_value resource = nullptr;
89     napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &resource);
90     napi_status status = napi_create_async_work(env, nullptr, resource, execute, AsyncCompleteCallback,
91         static_cast<void *>(context.get()), &context->work_);
92     if (status != napi_ok) {
93         INTELL_VOICE_LOG_ERROR("failed to create async work");
94         result = nullptr;
95         return result;
96     }
97 
98     status = napi_queue_async_work(env, context->work_);
99     if (status != napi_ok) {
100         INTELL_VOICE_LOG_ERROR("failed to queue async work");
101         result = nullptr;
102     } else {
103         context->contextSp_ = context;
104     }
105 
106     return result;
107 }
108 
AsyncWork(napi_env env,shared_ptr<AsyncContext> context,const string & name,AsyncExecute execute,napi_async_complete_callback complete)109 napi_value NapiAsync::AsyncWork(napi_env env, shared_ptr<AsyncContext> context, const string &name,
110     AsyncExecute execute, napi_async_complete_callback complete)
111 {
112     INTELL_VOICE_LOG_INFO("enter");
113     napi_value result = nullptr;
114     if (context->callbackRef_ == nullptr) {
115         napi_create_promise(env, &context->deferred_, &result);
116     } else {
117         napi_get_undefined(env, &result);
118     }
119 
120     napi_value resource = nullptr;
121     napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &resource);
122     napi_status status = napi_create_async_work(env, nullptr, resource, execute, complete,
123         static_cast<void *>(context.get()), &context->work_);
124     if (status != napi_ok) {
125         INTELL_VOICE_LOG_ERROR("failed to create async work");
126         result = nullptr;
127         return result;
128     }
129 
130     status = napi_queue_async_work(env, context->work_);
131     context->contextSp_ = context;
132 
133     return result;
134 }
135 
CommonCallbackRoutine(napi_env env,AsyncContext * context,const napi_value & data)136 void NapiAsync::CommonCallbackRoutine(napi_env env, AsyncContext *context, const napi_value &data)
137 {
138     CHECK_CONDITION_RETURN_VOID((context == nullptr), "async context is null");
139 
140     if (context->deferred_) {
141         HandlePromise(env, context, data);
142     } else {
143         HandleAsyncCallback(env, context, data);
144     }
145     context->contextSp_.reset();
146 }
147 
HandlePromise(napi_env env,AsyncContext * context,const napi_value & data)148 void NapiAsync::HandlePromise(napi_env env, AsyncContext *context, const napi_value &data)
149 {
150     INTELL_VOICE_LOG_INFO("enter");
151     napi_value result = nullptr;
152     napi_value error = nullptr;
153     napi_get_undefined(env, &result);
154     napi_get_undefined(env, &error);
155 
156     if (context->result_ == NAPI_INTELLIGENT_VOICE_SUCCESS) {
157         result = data;
158         napi_resolve_deferred(env, context->deferred_, result);
159         return;
160     }
161     napi_value errMessage = nullptr;
162     napi_value errCode = nullptr;
163     std::string msg = IntellVoiceCommonNapi::GetMessageByCode(context->result_);
164     napi_create_string_utf8(env, msg.c_str(), NAPI_AUTO_LENGTH, &errMessage);
165     napi_create_string_utf8(env, std::to_string(context->result_).c_str(), NAPI_AUTO_LENGTH, &errCode);
166     napi_create_error(env, errCode, errMessage, &error);
167     napi_reject_deferred(env, context->deferred_, error);
168 }
169 
HandleAsyncCallback(napi_env env,AsyncContext * context,const napi_value & data)170 void NapiAsync::HandleAsyncCallback(napi_env env, AsyncContext *context, const napi_value &data)
171 {
172     napi_value args[ARGC_TWO] = {nullptr, nullptr};
173     napi_value callback = nullptr;
174     napi_value ret = nullptr;
175     if (context->result_ == NAPI_INTELLIGENT_VOICE_SUCCESS) {
176         napi_get_undefined(env, &args[ARG_INDEX_0]);
177         args[ARG_INDEX_1] = data;
178     } else {
179         napi_value errMessage = nullptr;
180         napi_value errCode = nullptr;
181         std::string msg = IntellVoiceCommonNapi::GetMessageByCode(context->result_);
182         napi_create_string_utf8(env, msg.c_str(), NAPI_AUTO_LENGTH, &errMessage);
183         napi_create_string_utf8(env, std::to_string(context->result_).c_str(), NAPI_AUTO_LENGTH, &errCode);
184         napi_create_error(env, errCode, errMessage, &args[ARG_INDEX_0]);
185         napi_get_undefined(env, &args[ARG_INDEX_1]);
186     }
187     napi_get_reference_value(env, context->callbackRef_, &callback);
188     napi_call_function(env, nullptr, callback, ARGC_TWO, args, &ret);
189 }
190 
AsyncCompleteCallback(napi_env env,napi_status status,void * data)191 void NapiAsync::AsyncCompleteCallback(napi_env env, napi_status status, void *data)
192 {
193     INTELL_VOICE_LOG_INFO("enter");
194     CHECK_CONDITION_RETURN_VOID((data == nullptr), "async complete callback data is null");
195     napi_value result = nullptr;
196     auto context = static_cast<AsyncContext *>(data);
197 
198     if (context->result_ != NAPI_INTELLIGENT_VOICE_SUCCESS) {
199         napi_get_undefined(env, &result);
200         NapiAsync::CommonCallbackRoutine(env, context, result);
201         return;
202     }
203 
204     if (context->complete_ != nullptr) {
205         context->complete_(env, context, result);
206     } else {
207         napi_get_undefined(env, &result);
208     }
209     CommonCallbackRoutine(env, context, result);
210 }
211 }  // namespace IntellVoiceNapi
212 }  // namespace OHOS
213