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 #ifndef UDMF_NAPI_QUEUE_H
17 #define UDMF_NAPI_QUEUE_H
18 
19 #include <functional>
20 #include <memory>
21 #include <string>
22 
23 #include "napi/native_api.h"
24 #include "napi/native_common.h"
25 #include "napi/native_node_api.h"
26 #include "napi_error_utils.h"
27 
28 namespace OHOS {
29 namespace UDMF {
30 using NapiCbInfoParser = std::function<void(size_t argc, napi_value *argv)>;
31 using NapiAsyncExecute = std::function<void(void)>;
32 using NapiAsyncComplete = std::function<void(napi_value &)>;
33 static constexpr size_t ARGC_MAX = 6;
34 struct ContextBase {
35     virtual ~ContextBase();
36     void GetCbInfo(
37         napi_env env, napi_callback_info info, NapiCbInfoParser parse = NapiCbInfoParser(), bool sync = false);
38 
39     inline void GetCbInfoSync(napi_env env, napi_callback_info info, const NapiCbInfoParser &parse = NapiCbInfoParser())
40     {
41         /* sync = true, means no callback, not AsyncWork. */
42         GetCbInfo(env, info, parse, true);
43     }
44 
45     napi_env env = nullptr;
46     napi_value output = nullptr;
47     napi_status status = napi_invalid_arg;
48     std::string error;
49     int32_t jsCode = 0;
50     bool isThrowError = false;
51 
52     napi_value self = nullptr;
53     void *native = nullptr;
54 
55 private:
56     napi_deferred deferred = nullptr;
57     napi_ref callbackRef = nullptr;
58     napi_ref selfRef = nullptr;
59 
60     NapiAsyncExecute execute = nullptr;
61     NapiAsyncComplete complete = nullptr;
62     std::shared_ptr<ContextBase> hold; /* cross thread data */
63 
64     friend class NapiQueue;
65 };
66 
67 /* check condition related to argc/argv, return and logging. */
68 #define ASSERT_ARGS(ctxt, condition, message)                                    \
69     do {                                                                         \
70         if (!(condition)) {                                                      \
71             (ctxt)->status = napi_invalid_arg;                                   \
72             (ctxt)->error = std::string(message);                                \
73             LOG_ERROR(UDMF_KITS_NAPI, "test (" #condition ") failed: " message); \
74             return;                                                              \
75         }                                                                        \
76     } while (0)
77 
78 #define ASSERT_STATUS(ctxt, message)                                                      \
79     do {                                                                                  \
80         if ((ctxt)->status != napi_ok) {                                                  \
81             (ctxt)->error = std::string(message);                                         \
82             LOG_ERROR(UDMF_KITS_NAPI, "test (ctxt->status == napi_ok) failed: " message); \
83             return;                                                                       \
84         }                                                                                 \
85     } while (0)
86 
87 /* check condition, return and logging if condition not true. */
88 #define ASSERT(condition, message, retVal)                                       \
89     do {                                                                         \
90         if (!(condition)) {                                                      \
91             LOG_ERROR(UDMF_KITS_NAPI, "test (" #condition ") failed: " message); \
92             return retVal;                                                       \
93         }                                                                        \
94     } while (0)
95 
96 #define ASSERT_VOID(condition, message)                                          \
97     do {                                                                         \
98         if (!(condition)) {                                                      \
99             LOG_ERROR(UDMF_KITS_NAPI, "test (" #condition ") failed: " message); \
100             return;                                                              \
101         }                                                                        \
102     } while (0)
103 
104 #define ASSERT_FALSE(condition, message)                                         \
105     do {                                                                         \
106         if (!(condition)) {                                                      \
107             LOG_ERROR(UDMF_KITS_NAPI, "test (" #condition ") failed: " message); \
108             return false;                                                        \
109         }                                                                        \
110     } while (0)
111 
112 #define ASSERT_NULL(condition, message) ASSERT(condition, message, nullptr)
113 
114 #define ASSERT_CALL(env, theCall, object)    \
115     do {                                     \
116         if ((theCall) != napi_ok) {          \
117             delete (object);                 \
118             GET_AND_THROW_LAST_ERROR((env)); \
119             return nullptr;                  \
120         }                                    \
121     } while (0)
122 
123 #define ASSERT_CALL_DELETE(env, theCall, object) \
124     do {                                         \
125         if ((theCall) != napi_ok) {              \
126             delete (object);                     \
127             GET_AND_THROW_LAST_ERROR((env));     \
128             return;                              \
129         }                                        \
130     } while (0)
131 
132 #define ASSERT_CALL_VOID(env, theCall)       \
133     do {                                     \
134         if ((theCall) != napi_ok) {          \
135             GET_AND_THROW_LAST_ERROR((env)); \
136             return;                          \
137         }                                    \
138     } while (0)
139 
140 #define ASSERT_CALL_DELETE_STATUS(env, theCall, object) \
141     do {                                                \
142         napi_status status = (theCall);                 \
143         if (status != napi_ok) {                        \
144             delete (object);                            \
145             GET_AND_THROW_LAST_ERROR((env));            \
146             return status;                              \
147         }                                               \
148     } while (0)
149 
150 #define ASSERT_CALL_STATUS(env, theCall)     \
151     do {                                     \
152         napi_status status = (theCall);      \
153         if (status != napi_ok) {             \
154             GET_AND_THROW_LAST_ERROR((env)); \
155             return status;                   \
156         }                                    \
157     } while (0)
158 
159 #define ASSERT_WITH_ERRCODE(ctxt, condition, errcode, message)                                     \
160     do {                                                                                           \
161         if (!(condition)) {                                                                        \
162             (ctxt)->status = napi_generic_failure;                                                 \
163             GenerateNapiError(errcode, (ctxt)->jsCode, (ctxt)->error);                             \
164             LOG_ERROR(UDMF_KITS_NAPI, "test (" #condition ") failed: " message);                   \
165             return;                                                                                \
166         }                                                                                          \
167     } while (0)
168 
169 class NapiQueue {
170 public:
171     static napi_value AsyncWork(napi_env env, std::shared_ptr<ContextBase> ctxt, const std::string &name,
172         NapiAsyncExecute execute = NapiAsyncExecute(), NapiAsyncComplete complete = NapiAsyncComplete());
173 
174 private:
175     enum {
176         /* AsyncCallback / Promise output result index  */
177         RESULT_ERROR = 0,
178         RESULT_DATA = 1,
179         RESULT_ALL = 2
180     };
181 
182     struct AsyncContext {
183         std::shared_ptr<ContextBase> ctxt;
184         NapiAsyncExecute execute = nullptr;
185         NapiAsyncComplete complete = nullptr;
186         napi_deferred deferred = nullptr;
187         napi_async_work work = nullptr;
188     };
189 
190     static void GenerateOutput(ContextBase *ctxt);
191 
192     static void onExecute(napi_env env, void *data);
193     static void onComplete(napi_env env, napi_status status, void *data);
194 };
195 } // namespace UDMF
196 } // namespace OHOS
197 #endif // UDMF_NAPI_QUEUE_H
198