1 /**
2 * Copyright (c) 2021-2025 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 PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_COMMON_H_
17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_COMMON_H_
18
19 #include "runtime/include/thread_scopes.h"
20 #include "runtime/mem/refstorage/global_object_storage.h"
21 #include "plugins/ets/runtime/interop_js/logger.h"
22 #include "plugins/ets/runtime/types/ets_bigint.h"
23 #include "utils/small_vector.h"
24
25 #include <node_api.h>
26
27 #include <functional>
28
29 #if defined(PANDA_JS_ETS_HYBRID_MODE)
30 #include "interfaces/inner_api/napi/native_node_hybrid_api.h"
31 #else
32 // NOLINTBEGIN(readability-identifier-naming)
33 napi_status __attribute__((weak)) // CC-OFF(G.FMT.07) project code style
34 napi_wrap_with_xref(napi_env env, napi_value js_object, void *native_object, napi_finalize finalize_cb,
35 napi_ref *result);
36 napi_status __attribute__((weak)) // CC-OFF(G.FMT.07) project code style
37 napi_xref_unwrap(napi_env env, napi_value js_object, void **result);
38 napi_status __attribute__((weak)) // CC-OFF(G.FMT.07) project code style
39 napi_create_xref(napi_env env, napi_value value, uint32_t initial_refcount, napi_ref *result);
40 napi_status __attribute__((weak)) // CC-OFF(G.FMT.07) project code style
41 napi_register_appstate_callback(napi_env env, void (*f)(int a1, int64_t a2));
42 // NOLINTEND(readability-identifier-naming)
43 #endif
44
45 namespace ark::ets::interop::js {
46
47 constexpr size_t BIGINT_BITS_NUM = 32;
48 constexpr size_t BIT_64 = 64;
49
50 // NOLINTBEGIN(cppcoreguidelines-macro-usage)
51
52 [[noreturn]] PANDA_PUBLIC_API void InteropFatal(const char *message);
53 [[noreturn]] PANDA_PUBLIC_API void InteropFatal(const std::string &message);
54 [[noreturn]] PANDA_PUBLIC_API void InteropFatal(const char *message, napi_status status);
55
56 std::pair<SmallVector<uint64_t, 4U>, int> GetBigInt(napi_env env, napi_value jsVal);
57 SmallVector<uint64_t, 4U> ConvertBigIntArrayFromEtsToJs(const std::vector<uint32_t> &etsArray);
58 std::vector<EtsInt> ConvertBigIntArrayFromJsToEts(SmallVector<uint64_t, 4U> &jsArray);
59
60 PANDA_PUBLIC_API void ThrowNoInteropContextException();
61
62 bool NapiGetProperty(napi_env env, napi_value object, napi_value key, napi_value *result);
63 bool NapiGetNamedProperty(napi_env env, napi_value object, const char *utf8name, napi_value *result);
64
65 // Alternative for ASSERT(!expr) with interop stacktraces, enabled in NDEBUG
66 #define INTEROP_FATAL_IF(expr) \
67 do { \
68 bool _expr = (expr); \
69 if (UNLIKELY(_expr)) { \
70 InteropFatal("INTEROP_FATAL: " #expr); \
71 UNREACHABLE(); \
72 } \
73 } while (0)
74
75 #define INTEROP_RETURN_IF(expr) \
76 do { \
77 bool _expr = (expr); \
78 if (UNLIKELY(_expr)) { \
79 return false; \
80 } \
81 } while (0)
82
83 #ifndef NDEBUG
84 void InteropTrace(const char *func, const char *file, int line);
85 #define INTEROP_TRACE() \
86 do { \
87 InteropTrace(__func__, __FILE__, __LINE__); \
88 } while (0)
89 #else
90 #define INTEROP_TRACE()
91 #endif
92
93 #if !defined(NDEBUG)
94 #define NAPI_ASSERT_OK(expr) INTEROP_FATAL_IF(expr)
95 #else
96 #define NAPI_ASSERT_OK(expr) \
97 do { \
98 (expr); \
99 } while (0)
100 #endif
101
102 // Assertion for napi_* calls success, enabled in NDEBUG
103 #define NAPI_CHECK_FATAL(status) \
104 do { \
105 napi_status _status = (status); \
106 if (UNLIKELY(_status != napi_ok)) { \
107 InteropFatal("NAPI_CHECK_FATAL: " #status, _status); \
108 UNREACHABLE(); \
109 } \
110 } while (0)
111
112 #define NAPI_CHECK_RETURN(status) \
113 do { \
114 napi_status _status = (status); \
115 if (UNLIKELY(_status != napi_ok)) { \
116 return false; \
117 } \
118 } while (0)
119
120 #define CHECK_NAPI_STATUS(jsStatus, ctx, coro, result) \
121 do { \
122 napi_status local_status = (jsStatus); \
123 if (local_status != napi_ok) { \
124 if ((ctx) != nullptr && (coro) != nullptr) { \
125 (ctx)->ForwardJSException(coro); \
126 } \
127 (result) = false; \
128 } \
129 } while (0)
130
131 // NOLINTEND(cppcoreguidelines-macro-usage)
132
133 class NapiScope {
134 public:
NapiScope(napi_env env)135 explicit NapiScope(napi_env env) : env_(env)
136 {
137 [[maybe_unused]] auto status = napi_open_handle_scope(env_, &scope_);
138 ASSERT(status == napi_ok);
139 }
140
~NapiScope()141 ~NapiScope()
142 {
143 [[maybe_unused]] auto status = napi_close_handle_scope(env_, scope_);
144 ASSERT(status == napi_ok);
145 }
146
147 NO_COPY_SEMANTIC(NapiScope);
148 NO_MOVE_SEMANTIC(NapiScope);
149
150 private:
151 napi_env env_ {};
152 napi_handle_scope scope_ {};
153 };
154
155 class NapiEscapableScope {
156 public:
NapiEscapableScope(napi_env env)157 explicit NapiEscapableScope(napi_env env) : env_(env)
158 {
159 [[maybe_unused]] auto status = napi_open_escapable_handle_scope(env_, &scope_);
160 ASSERT(status == napi_ok);
161 }
162
Escape(napi_value & val)163 void Escape(napi_value &val)
164 {
165 [[maybe_unused]] auto status = napi_escape_handle(env_, scope_, val, &val);
166 ASSERT(status == napi_ok);
167 }
168
~NapiEscapableScope()169 ~NapiEscapableScope()
170 {
171 [[maybe_unused]] auto status = napi_close_escapable_handle_scope(env_, scope_);
172 ASSERT(status == napi_ok);
173 }
174
175 NO_COPY_SEMANTIC(NapiEscapableScope);
176 NO_MOVE_SEMANTIC(NapiEscapableScope);
177
178 private:
179 napi_env env_ {};
180 napi_escapable_handle_scope scope_ {};
181 };
182
GetValueType(napi_env env,napi_value val)183 inline napi_valuetype GetValueType(napi_env env, napi_value val)
184 {
185 napi_valuetype vtype;
186 NAPI_CHECK_FATAL(napi_typeof(env, val, &vtype));
187 return vtype;
188 }
189
GetReferenceValue(napi_env env,napi_ref ref)190 inline napi_value GetReferenceValue(napi_env env, napi_ref ref)
191 {
192 napi_value val;
193 NAPI_CHECK_FATAL(napi_get_reference_value(env, ref, &val));
194 return val;
195 }
196
GetUndefined(napi_env env)197 inline napi_value GetUndefined(napi_env env)
198 {
199 napi_value jsValueUndefined {};
200 NAPI_CHECK_FATAL(napi_get_undefined(env, &jsValueUndefined));
201 return jsValueUndefined;
202 }
203
GetNull(napi_env env)204 inline napi_value GetNull(napi_env env)
205 {
206 napi_value jsValueNull {};
207 NAPI_CHECK_FATAL(napi_get_null(env, &jsValueNull));
208 return jsValueNull;
209 }
210
GetGlobal(napi_env env)211 inline napi_value GetGlobal(napi_env env)
212 {
213 napi_value jsValueGlobal {};
214 NAPI_CHECK_FATAL(napi_get_global(env, &jsValueGlobal));
215 return jsValueGlobal;
216 }
217
GetBooleanValue(napi_env env,bool val)218 inline napi_value GetBooleanValue(napi_env env, bool val)
219 {
220 napi_value jsValueBoolean {};
221 NAPI_CHECK_FATAL(napi_get_boolean(env, val, &jsValueBoolean));
222 return jsValueBoolean;
223 }
224
IsNull(napi_env env,napi_value val)225 inline bool IsNull(napi_env env, napi_value val)
226 {
227 return GetValueType(env, val) == napi_null;
228 }
229
IsUndefined(napi_env env,napi_value val)230 inline bool IsUndefined(napi_env env, napi_value val)
231 {
232 return GetValueType(env, val) == napi_undefined;
233 }
234
IsNullOrUndefined(napi_env env,napi_value val)235 inline bool IsNullOrUndefined(napi_env env, napi_value val)
236 {
237 napi_valuetype vtype = GetValueType(env, val);
238 return vtype == napi_undefined || vtype == napi_null;
239 }
240
GetString(napi_env env,napi_value jsVal)241 inline std::string GetString(napi_env env, napi_value jsVal)
242 {
243 size_t length;
244 NAPI_CHECK_FATAL(napi_get_value_string_utf8(env, jsVal, nullptr, 0, &length));
245 std::string value;
246 value.resize(length);
247 // +1 for NULL terminated string!!!
248 NAPI_CHECK_FATAL(napi_get_value_string_utf8(env, jsVal, value.data(), value.size() + 1, &length));
249 return value;
250 }
251
NapiIsExceptionPending(napi_env env)252 inline bool NapiIsExceptionPending(napi_env env)
253 {
254 bool pending;
255 NAPI_CHECK_FATAL(napi_is_exception_pending(env, &pending));
256 return pending;
257 }
258
NapiThrownGeneric(napi_status rc)259 inline bool NapiThrownGeneric(napi_status rc)
260 {
261 INTEROP_FATAL_IF(rc != napi_ok && rc != napi_generic_failure);
262 return rc == napi_generic_failure;
263 }
264
NapiCallFunction(napi_env env,napi_value recv,napi_value func,size_t argc,const napi_value * argv,napi_value * result)265 inline napi_status NapiCallFunction(napi_env env, napi_value recv, napi_value func, size_t argc, const napi_value *argv,
266 napi_value *result)
267 {
268 #if defined(PANDA_TARGET_OHOS) || defined(PANDA_JS_ETS_HYBRID_MODE)
269 napi_value dummy;
270 result = result != nullptr ? result : &dummy;
271 #endif // PANDA_TARGET_OHOS
272 return napi_call_function(env, recv, func, argc, argv, result);
273 }
274
275 } // namespace ark::ets::interop::js
276
277 #endif // PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_COMMON_H_
278