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