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_REFCONVERT_ARRAY_H_
17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_REFCONVERT_ARRAY_H_
18
19 #include "libpandabase/macros.h"
20 #include "plugins/ets/runtime/ets_class_linker_extension.h"
21 #include "plugins/ets/runtime/interop_js/interop_context.h"
22 #include "plugins/ets/runtime/interop_js/interop_common.h"
23 #include "plugins/ets/runtime/interop_js/js_refconvert.h"
24 #include "plugins/ets/runtime/interop_js/js_convert.h"
25 #include "runtime/mem/local_object_handle.h"
26 #include "plugins/ets/runtime/types/ets_object.h"
27
28 namespace ark::ets::interop::js {
29
30 // JSRefConvert adapter for builtin[] types
31 template <typename Conv>
32 class JSRefConvertBuiltinArray : public JSRefConvert {
33 public:
JSRefConvertBuiltinArray(Class * klass)34 explicit JSRefConvertBuiltinArray(Class *klass) : JSRefConvert(this), klass_(klass) {}
35
36 private:
37 using ElemCpptype = typename Conv::cpptype;
38
GetElem(coretypes::Array * arr,size_t idx)39 static ElemCpptype GetElem(coretypes::Array *arr, size_t idx)
40 {
41 if constexpr (Conv::IS_REFTYPE) {
42 auto elem = EtsObject::FromCoreType(arr->Get<ObjectHeader *>(idx));
43 return FromEtsObject<std::remove_pointer_t<ElemCpptype>>(elem);
44 } else {
45 return arr->Get<ElemCpptype>(idx);
46 }
47 }
48
SetElem(coretypes::Array * arr,size_t idx,ElemCpptype value)49 static void SetElem(coretypes::Array *arr, size_t idx, ElemCpptype value)
50 {
51 if constexpr (Conv::IS_REFTYPE) {
52 arr->Set(idx, AsEtsObject(value)->GetCoreType());
53 } else {
54 arr->Set(idx, value);
55 }
56 }
57
58 public:
WrapImpl(InteropCtx * ctx,EtsObject * obj)59 napi_value WrapImpl(InteropCtx *ctx, EtsObject *obj)
60 {
61 auto env = ctx->GetJSEnv();
62
63 auto etsArr = static_cast<coretypes::Array *>(obj->GetCoreType());
64 auto len = etsArr->GetLength();
65
66 NapiEscapableScope jsHandleScope(env);
67 napi_value jsArr;
68 NAPI_CHECK_FATAL(napi_create_array_with_length(env, len, &jsArr));
69
70 for (size_t idx = 0; idx < len; ++idx) {
71 ElemCpptype etsElem = GetElem(etsArr, idx);
72 auto jsElem = Conv::WrapWithNullCheck(env, etsElem);
73 if (UNLIKELY(jsElem == nullptr)) {
74 return nullptr;
75 }
76 napi_status rc = napi_set_element(env, jsArr, idx, jsElem);
77 if (UNLIKELY(NapiThrownGeneric(rc))) {
78 return nullptr;
79 }
80 }
81 jsHandleScope.Escape(jsArr);
82 return jsArr;
83 }
84
UnwrapImpl(InteropCtx * ctx,napi_value jsArr)85 EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value jsArr)
86 {
87 auto coro = EtsCoroutine::GetCurrent();
88 auto env = ctx->GetJSEnv();
89 {
90 bool isArray;
91 NAPI_CHECK_FATAL(napi_is_array(env, jsArr, &isArray));
92 if (UNLIKELY(!isArray)) {
93 JSConvertTypeCheckFailed("array");
94 return nullptr;
95 }
96 }
97
98 uint32_t len;
99 napi_status rc = napi_get_array_length(env, jsArr, &len);
100 if (UNLIKELY(NapiThrownGeneric(rc))) {
101 return nullptr;
102 }
103
104 // NOTE(vpukhov): elide handles for primitive arrays
105 LocalObjectHandle<coretypes::Array> etsArr(coro, coretypes::Array::Create(klass_, len));
106 NapiScope jsHandleScope(env);
107
108 for (size_t idx = 0; idx < len; ++idx) {
109 napi_value jsElem;
110 rc = napi_get_element(env, jsArr, idx, &jsElem);
111 if (UNLIKELY(NapiThrownGeneric(rc))) {
112 return nullptr;
113 }
114 auto res = Conv::UnwrapWithNullCheck(ctx, env, jsElem);
115 if (UNLIKELY(!res)) {
116 return nullptr;
117 }
118 SetElem(etsArr.GetPtr(), idx, res.value());
119 }
120
121 return EtsObject::FromCoreType(etsArr.GetPtr());
122 }
123
124 private:
125 Class *klass_ {};
126 };
127
128 template <ClassRoot CLASS_ROOT, typename Conv>
RegisterBuiltinArrayConvertor(JSRefConvertCache * cache,EtsClassLinkerExtension * ext)129 static inline void RegisterBuiltinArrayConvertor(JSRefConvertCache *cache, EtsClassLinkerExtension *ext)
130 {
131 auto aklass = ext->GetClassRoot(CLASS_ROOT);
132 cache->Insert(aklass, std::make_unique<JSRefConvertBuiltinArray<Conv>>(aklass));
133 }
134
135 // JSRefConvert adapter for reference[] types
136 class JSRefConvertReftypeArray : public JSRefConvert {
137 public:
JSRefConvertReftypeArray(Class * klass)138 explicit JSRefConvertReftypeArray(Class *klass) : JSRefConvert(this), klass_(klass) {}
139
WrapImpl(InteropCtx * ctx,EtsObject * obj)140 napi_value WrapImpl(InteropCtx *ctx, EtsObject *obj)
141 {
142 auto coro = EtsCoroutine::GetCurrent();
143 auto env = ctx->GetJSEnv();
144
145 LocalObjectHandle<coretypes::Array> etsArr(coro, obj->GetCoreType());
146 auto len = etsArr->GetLength();
147
148 NapiEscapableScope jsHandleScope(env);
149 napi_value jsArr;
150 NAPI_CHECK_FATAL(napi_create_array_with_length(env, len, &jsArr));
151
152 for (size_t idx = 0; idx < len; ++idx) {
153 EtsObject *etsElem = EtsObject::FromCoreType(etsArr->Get<ObjectHeader *>(idx));
154 napi_value jsElem;
155 if (LIKELY(etsElem != nullptr)) {
156 JSRefConvert *elemConv = GetElemConvertor(ctx, etsElem->GetClass());
157 if (UNLIKELY(elemConv == nullptr)) {
158 return nullptr;
159 }
160 jsElem = elemConv->Wrap(ctx, etsElem);
161 if (UNLIKELY(jsElem == nullptr)) {
162 return nullptr;
163 }
164 } else {
165 jsElem = GetNull(env);
166 }
167 napi_status rc = napi_set_element(env, jsArr, idx, jsElem);
168 if (UNLIKELY(NapiThrownGeneric(rc))) {
169 return nullptr;
170 }
171 }
172 jsHandleScope.Escape(jsArr);
173 return jsArr;
174 }
175
UnwrapNonUndefined(InteropCtx * ctx,napi_value jsElem)176 EtsObject *UnwrapNonUndefined(InteropCtx *ctx, napi_value jsElem)
177 {
178 if (UNLIKELY(baseElemConv_ == nullptr)) {
179 baseElemConv_ = JSRefConvertResolve(ctx, klass_->GetComponentType());
180 if (UNLIKELY(baseElemConv_ == nullptr)) {
181 return nullptr;
182 }
183 }
184 EtsObject *etsElem = baseElemConv_->Unwrap(ctx, jsElem);
185 if (UNLIKELY(etsElem == nullptr)) {
186 return nullptr;
187 }
188 return etsElem;
189 }
190
UnwrapImpl(InteropCtx * ctx,napi_value jsArr)191 EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value jsArr)
192 {
193 auto coro = EtsCoroutine::GetCurrent();
194 auto env = ctx->GetJSEnv();
195 {
196 bool isArray;
197 NAPI_CHECK_FATAL(napi_is_array(env, jsArr, &isArray));
198 if (UNLIKELY(!isArray)) {
199 JSConvertTypeCheckFailed("array");
200 return nullptr;
201 }
202 }
203
204 uint32_t len;
205 napi_status rc = napi_get_array_length(env, jsArr, &len);
206 if (UNLIKELY(NapiThrownGeneric(rc))) {
207 return nullptr;
208 }
209
210 LocalObjectHandle<coretypes::Array> etsArr(coro, coretypes::Array::Create(klass_, len));
211 NapiScope jsHandleScope(env);
212
213 for (size_t idx = 0; idx < len; ++idx) {
214 napi_value jsElem;
215 rc = napi_get_element(env, jsArr, idx, &jsElem);
216 if (UNLIKELY(NapiThrownGeneric(rc))) {
217 return nullptr;
218 }
219 if (LIKELY(!IsNullOrUndefined(env, jsElem))) {
220 auto *etsElem = UnwrapNonUndefined(ctx, jsElem);
221 if (UNLIKELY(etsElem == nullptr)) {
222 return nullptr;
223 }
224 etsArr->Set(idx, etsElem->GetCoreType());
225 }
226 }
227
228 return EtsObject::FromCoreType(etsArr.GetPtr());
229 }
230
231 private:
232 static constexpr auto ELEM_SIZE = ClassHelper::OBJECT_POINTER_SIZE;
233
GetElemConvertor(InteropCtx * ctx,EtsClass * elemEtsKlass)234 JSRefConvert *GetElemConvertor(InteropCtx *ctx, EtsClass *elemEtsKlass)
235 {
236 Class *elemKlass = elemEtsKlass->GetRuntimeClass();
237 if (elemKlass != klass_->GetComponentType()) {
238 return JSRefConvertResolve(ctx, elemKlass);
239 }
240 if (LIKELY(baseElemConv_ != nullptr)) {
241 return baseElemConv_;
242 }
243 return baseElemConv_ = JSRefConvertResolve(ctx, klass_->GetComponentType());
244 }
245
246 Class *klass_ {};
247 JSRefConvert *baseElemConv_ {};
248 };
249
250 } // namespace ark::ets::interop::js
251
252 #endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_REFCONVERT_ARRAY_H_
253