• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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