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