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 // Need to read element pointer again, since it could have been moved by GC
161 etsElem = EtsObject::FromCoreType(etsArr->Get<ObjectHeader *>(idx));
162 ASSERT(etsElem != nullptr);
163 jsElem = elemConv->Wrap(ctx, etsElem);
164 if (UNLIKELY(jsElem == nullptr)) {
165 return nullptr;
166 }
167 } else {
168 jsElem = GetNull(env);
169 }
170 napi_status rc = napi_set_element(env, jsArr, idx, jsElem);
171 if (UNLIKELY(NapiThrownGeneric(rc))) {
172 return nullptr;
173 }
174 }
175 jsHandleScope.Escape(jsArr);
176 return jsArr;
177 }
178
UnwrapNonUndefined(InteropCtx * ctx,napi_value jsElem)179 EtsObject *UnwrapNonUndefined(InteropCtx *ctx, napi_value jsElem)
180 {
181 if (UNLIKELY(baseElemConv_ == nullptr)) {
182 baseElemConv_ = JSRefConvertResolve(ctx, klass_->GetComponentType());
183 if (UNLIKELY(baseElemConv_ == nullptr)) {
184 return nullptr;
185 }
186 }
187 EtsObject *etsElem = baseElemConv_->Unwrap(ctx, jsElem);
188 if (UNLIKELY(etsElem == nullptr)) {
189 return nullptr;
190 }
191 return etsElem;
192 }
193
UnwrapImpl(InteropCtx * ctx,napi_value jsArr)194 EtsObject *UnwrapImpl(InteropCtx *ctx, napi_value jsArr)
195 {
196 auto coro = EtsCoroutine::GetCurrent();
197 auto env = ctx->GetJSEnv();
198 {
199 bool isArray;
200 NAPI_CHECK_FATAL(napi_is_array(env, jsArr, &isArray));
201 if (UNLIKELY(!isArray)) {
202 JSConvertTypeCheckFailed("array");
203 return nullptr;
204 }
205 }
206
207 uint32_t len;
208 napi_status rc = napi_get_array_length(env, jsArr, &len);
209 if (UNLIKELY(NapiThrownGeneric(rc))) {
210 return nullptr;
211 }
212
213 LocalObjectHandle<coretypes::Array> etsArr(coro, coretypes::Array::Create(klass_, len));
214 NapiScope jsHandleScope(env);
215
216 for (size_t idx = 0; idx < len; ++idx) {
217 napi_value jsElem;
218 rc = napi_get_element(env, jsArr, idx, &jsElem);
219 if (UNLIKELY(NapiThrownGeneric(rc))) {
220 return nullptr;
221 }
222 if (LIKELY(!IsNullOrUndefined(env, jsElem))) {
223 auto *etsElem = UnwrapNonUndefined(ctx, jsElem);
224 if (UNLIKELY(etsElem == nullptr)) {
225 return nullptr;
226 }
227 etsArr->Set(idx, etsElem->GetCoreType());
228 }
229 }
230
231 return EtsObject::FromCoreType(etsArr.GetPtr());
232 }
233
234 private:
235 static constexpr auto ELEM_SIZE = ClassHelper::OBJECT_POINTER_SIZE;
236
GetElemConvertor(InteropCtx * ctx,EtsClass * elemEtsKlass)237 JSRefConvert *GetElemConvertor(InteropCtx *ctx, EtsClass *elemEtsKlass)
238 {
239 Class *elemKlass = elemEtsKlass->GetRuntimeClass();
240 if (elemKlass != klass_->GetComponentType()) {
241 return JSRefConvertResolve(ctx, elemKlass);
242 }
243 if (LIKELY(baseElemConv_ != nullptr)) {
244 return baseElemConv_;
245 }
246 return baseElemConv_ = JSRefConvertResolve(ctx, klass_->GetComponentType());
247 }
248
249 Class *klass_ {};
250 JSRefConvert *baseElemConv_ {};
251 };
252
253 } // namespace ark::ets::interop::js
254
255 #endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_REFCONVERT_ARRAY_H_
256