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