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_JS_CONVERT_BASE_H
17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_CONVERT_BASE_H
18
19 #include "plugins/ets/runtime/ets_panda_file_items.h"
20 #include "plugins/ets/runtime/interop_js/interop_common.h"
21 #include "plugins/ets/runtime/interop_js/js_value.h"
22 #include "runtime/handle_scope-inl.h"
23 #include "runtime/include/coretypes/class.h"
24 #include "plugins/ets/runtime/types/ets_array.h"
25 #include "plugins/ets/runtime/types/ets_arraybuffer.h"
26 #include "plugins/ets/runtime/types/ets_string.h"
27 #include "plugins/ets/runtime/types/ets_promise.h"
28 #include "plugins/ets/runtime/types/ets_promise_ref.h"
29 #include "plugins/ets/runtime/types/ets_box_primitive-inl.h"
30 #include "plugins/ets/runtime/types/ets_method.h"
31
32 namespace ark::ets::interop::js {
33
34 template <typename T>
AsEtsObject(T * obj)35 inline EtsObject *AsEtsObject(T *obj)
36 {
37 static_assert(std::is_base_of_v<ObjectHeader, T>);
38 return reinterpret_cast<EtsObject *>(obj);
39 }
40
41 template <typename T>
FromEtsObject(EtsObject * obj)42 inline T *FromEtsObject(EtsObject *obj)
43 {
44 static_assert(std::is_base_of_v<ObjectHeader, T>);
45 return reinterpret_cast<T *>(obj);
46 }
47
48 void JSConvertTypeCheckFailed(const char *typeName);
JSConvertTypeCheckFailed(const std::string & s)49 inline void JSConvertTypeCheckFailed(const std::string &s)
50 {
51 JSConvertTypeCheckFailed(s.c_str());
52 }
53
54 // Base mixin class of JSConvert interface
55 // Represents primitive types and some built-in classes, has no state
56 template <typename Impl, typename ImplCpptype>
57 struct JSConvertBase {
58 JSConvertBase() = delete;
59 using cpptype = ImplCpptype;
60 static constexpr bool IS_REFTYPE = std::is_pointer_v<cpptype>;
61 static constexpr size_t TYPE_SIZE = IS_REFTYPE ? ClassHelper::OBJECT_POINTER_SIZE : sizeof(cpptype);
62
TypeCheckFailedJSConvertBase63 static void TypeCheckFailed()
64 {
65 JSConvertTypeCheckFailed(Impl::TYPE_NAME);
66 }
67
68 // Convert ets->js, returns nullptr if failed, throws JS exceptions
WrapJSConvertBase69 static napi_value Wrap(napi_env env, cpptype etsVal)
70 {
71 if constexpr (IS_REFTYPE) {
72 ASSERT(etsVal != nullptr);
73 }
74 auto res = Impl::WrapImpl(env, etsVal);
75 ASSERT(res != nullptr || InteropCtx::SanityJSExceptionPending());
76 return res;
77 }
78
79 // Convert js->ets, returns nullopt if failed, throws ETS/JS exceptions
UnwrapJSConvertBase80 static std::optional<cpptype> Unwrap(InteropCtx *ctx, napi_env env, napi_value jsVal)
81 {
82 if constexpr (IS_REFTYPE) {
83 ASSERT(!IsNull(env, jsVal));
84 }
85 auto res = Impl::UnwrapImpl(ctx, env, jsVal);
86 ASSERT(res.has_value() || InteropCtx::SanityJSExceptionPending() || InteropCtx::SanityETSExceptionPending());
87 return res;
88 }
89
WrapWithNullCheckJSConvertBase90 static napi_value WrapWithNullCheck(napi_env env, cpptype etsVal)
91 {
92 if constexpr (IS_REFTYPE) {
93 if (UNLIKELY(etsVal == nullptr)) {
94 return GetNull(env);
95 }
96 }
97 auto res = Impl::WrapImpl(env, etsVal);
98 ASSERT(res != nullptr || InteropCtx::SanityJSExceptionPending());
99 return res;
100 }
101
UnwrapWithNullCheckJSConvertBase102 static std::optional<cpptype> UnwrapWithNullCheck(InteropCtx *ctx, napi_env env, napi_value jsVal)
103 {
104 if constexpr (IS_REFTYPE) {
105 // NOTE(kprokopenko) can't assign undefined to EtsString *, hence fallback into UnwrapImpl
106 if (UNLIKELY(IsNull(env, jsVal))) {
107 return nullptr;
108 }
109 }
110 auto res = Impl::UnwrapImpl(ctx, env, jsVal);
111 ASSERT(res.has_value() || InteropCtx::SanityJSExceptionPending() || InteropCtx::SanityETSExceptionPending());
112 return res;
113 }
114 };
115
116 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
117 #define JSCONVERT_DEFINE_TYPE(type, cpptype_) \
118 struct JSConvert##type : public JSConvertBase<JSConvert##type, cpptype_> { \
119 static constexpr const char *TYPE_NAME = #type; \
120 /* Must not fail */ \
121 [[maybe_unused]] static inline napi_value WrapImpl([[maybe_unused]] napi_env env, \
122 [[maybe_unused]] cpptype etsVal); \
123 /* May fail */ \
124 [[maybe_unused]] static inline std::optional<cpptype> UnwrapImpl([[maybe_unused]] InteropCtx *ctx, \
125 [[maybe_unused]] napi_env env, \
126 [[maybe_unused]] napi_value jsVal); \
127 }
128
129 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
130 #define JSCONVERT_WRAP(type) \
131 inline napi_value JSConvert##type::WrapImpl([[maybe_unused]] napi_env env, [[maybe_unused]] cpptype etsVal)
132
133 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
134 #define JSCONVERT_UNWRAP(type) \
135 inline std::optional<JSConvert##type::cpptype> JSConvert##type::UnwrapImpl( \
136 [[maybe_unused]] InteropCtx *ctx, [[maybe_unused]] napi_env env, [[maybe_unused]] napi_value jsVal)
137 } // namespace ark::ets::interop::js
138
139 #endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_CONVERT_BASE_H
140