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