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 "napi_queue.h"
17 namespace OHOS::CalendarApi {
~ContextBase()18 ContextBase::~ContextBase()
19 {
20     LOG_DEBUG("no memory leak after callback or promise[resolved/rejected]");
21     if (env != nullptr) {
22         if (callbackRef != nullptr) {
23             auto status = napi_delete_reference(env, callbackRef);
24             LOG_DEBUG("status:%{public}d", status);
25         }
26         if (selfRef != nullptr) {
27             auto status = napi_delete_reference(env, selfRef);
28             LOG_DEBUG("status:%{public}d", status);
29         }
30         env = nullptr;
31     }
32 }
33 
GetCbInfo(napi_env envi, napi_callback_info info, NapiCbInfoParser parse, bool sync)34 void ContextBase::GetCbInfo(napi_env envi, napi_callback_info info, NapiCbInfoParser parse, bool sync)
35 {
36     env = envi;
37     size_t argc = ARGC_MAX;
38     napi_value argv[ARGC_MAX] = { nullptr };
39     status = napi_get_cb_info(env, info, &argc, argv, &self, nullptr);
40     CHECK_STATUS_RETURN_VOID(this, " napi_get_cb_info failed!");
41     CHECK_ARGS_RETURN_VOID(this, argc <= ARGC_MAX, " too many arguments!");
42     CHECK_ARGS_RETURN_VOID(this, self != nullptr, " no JavaScript this argument!");
43     if (!sync) {
44         napi_create_reference(env, self, 1, &selfRef);
45     }
46     status = napi_unwrap(env, self, &native);
47     CHECK_STATUS_RETURN_VOID(this, " self unwrap failed!");
48 
49     if (!sync && (argc > 0)) {
50         // get the last arguments :: <callback>
51         size_t index = argc - 1;
52         napi_valuetype type = napi_undefined;
53         napi_status tyst = napi_typeof(env, argv[index], &type);
54         if ((tyst == napi_ok) && (type == napi_function)) {
55             status = napi_create_reference(env, argv[index], 1, &callbackRef);
56             CHECK_STATUS_RETURN_VOID(this, " ref callback failed!");
57             argc = index;
58             LOG_DEBUG(" async callback, no promise");
59         } else {
60             LOG_DEBUG(" no callback, async pormose");
61         }
62     }
63 
64     if (parse) {
65         parse(argc, argv);
66     } else {
67         CHECK_ARGS_RETURN_VOID(this, argc == 0, " required no arguments!");
68     }
69 }
70 
AsyncWork(napi_env env, std::shared_ptr<ContextBase> ctxt, const std::string& name, NapiAsyncExecute execute, NapiAsyncComplete complete)71 napi_value NapiQueue::AsyncWork(napi_env env, std::shared_ptr<ContextBase> ctxt, const std::string& name,
72                                 NapiAsyncExecute execute, NapiAsyncComplete complete)
73 {
74     LOG_DEBUG("name = %{public}s", name.c_str());
75     AsyncContext *aCtx = new AsyncContext;
76     aCtx->env = env;
77     aCtx->ctx = std::move(ctxt);
78     aCtx->execute = std::move(execute);
79     aCtx->complete = std::move(complete);
80     napi_value promise = nullptr;
81     if (aCtx->ctx->callbackRef == nullptr) {
82         napi_create_promise(env, &aCtx->deferred, &promise);
83         LOG_DEBUG("AsyncWork create deferred promise");
84     } else {
85         napi_get_undefined(env, &promise);
86     }
87 
88     napi_value resource = nullptr;
89     napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &resource);
90     napi_create_async_work(
91         env, nullptr, resource,
92         [](napi_env env, void* data) {
93             CHECK_RETURN_VOID(data != nullptr, "AsyncWork napi_async_execute_callback nullptr");
94             auto actx = reinterpret_cast<AsyncContext*>(data);
95             LOG_DEBUG("AsyncWork napi_async_execute_callback ctxt->status=%{public}d", actx->ctx->status);
96             if (actx->execute && actx->ctx->status == napi_ok) {
97                 actx->execute();
98             }
99         },
100         [](napi_env env, napi_status status, void* data) {
101             CHECK_RETURN_VOID(data != nullptr, "AsyncWork napi_async_complete_callback nullptr");
102             auto actx = reinterpret_cast<AsyncContext*>(data);
103             LOG_DEBUG("AsyncWork napi_async_complete_callback status = %{public}d, ctxt->status = %{public}d",
104                 status, actx->ctx->status);
105             if ((status != napi_ok) && (actx->ctx->status == napi_ok)) {
106                 actx->ctx->status = status;
107             }
108             napi_value output = nullptr;
109             if ((actx->complete) && (status == napi_ok) && (actx->ctx->status == napi_ok)) {
110                 actx->complete(output);
111             }
112             GenerateOutput(*actx, output);
113             delete actx;
114         },
115         reinterpret_cast<void*>(aCtx), &aCtx->work);
116     auto status = napi_queue_async_work(env, aCtx->work);
117     if (status != napi_ok) {
118         napi_get_undefined(env, &promise);
119         delete aCtx;
120     }
121     return promise;
122 }
123 
GenerateOutput(AsyncContext &ctx, napi_value output)124 void NapiQueue::GenerateOutput(AsyncContext &ctx, napi_value output)
125 {
126     napi_value result[RESULT_ALL] = { nullptr };
127     if (ctx.ctx->status == napi_ok) {
128         napi_get_undefined(ctx.env, &result[RESULT_ERROR]);
129         if (output == nullptr) {
130             napi_get_undefined(ctx.env, &output);
131         }
132         result[RESULT_DATA] = output;
133     } else {
134         napi_value message = nullptr;
135         napi_create_string_utf8(ctx.env, ctx.ctx->error.c_str(), NAPI_AUTO_LENGTH, &message);
136         napi_create_error(ctx.env, nullptr, message, &result[RESULT_ERROR]);
137         napi_get_undefined(ctx.env, &result[RESULT_DATA]);
138     }
139     if (ctx.deferred != nullptr) {
140         if (ctx.ctx->status == napi_ok) {
141             LOG_DEBUG("GenerateOutput deferred promise resolved");
142             napi_resolve_deferred(ctx.env, ctx.deferred, result[RESULT_DATA]);
143         } else {
144             LOG_DEBUG("GenerateOutput deferred promise rejected");
145             napi_reject_deferred(ctx.env, ctx.deferred, result[RESULT_ERROR]);
146         }
147     } else {
148         napi_value callback = nullptr;
149         napi_get_reference_value(ctx.env, ctx.ctx->callbackRef, &callback);
150         napi_value callbackResult = nullptr;
151         LOG_DEBUG("GenerateOutput call callback function");
152         napi_call_function(ctx.env, nullptr, callback, RESULT_ALL, result, &callbackResult);
153     }
154 }
155 } // namespace OHOS::Calendar