• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024-2025 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_CALL_ARG_CONVERTORS_H
17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_CALL_ARG_CONVERTORS_H
18 
19 #include "plugins/ets/runtime/interop_js/call/proto_reader.h"
20 #include "plugins/ets/runtime/interop_js/js_convert.h"
21 #include "plugins/ets/runtime/types/ets_escompat_array.h"
22 
23 namespace ark::ets::interop::js {
24 
25 template <typename Convertor, typename FStore>
UnwrapVal(InteropCtx * ctx,napi_env env,napi_value jsVal,FStore & storeRes)26 static ALWAYS_INLINE bool UnwrapVal(InteropCtx *ctx, napi_env env, napi_value jsVal, FStore &storeRes)
27 {
28     using cpptype = typename Convertor::cpptype;  // NOLINT(readability-identifier-naming)
29     auto res = Convertor::Unwrap(ctx, env, jsVal);
30     if (UNLIKELY(!res.has_value())) {
31         return false;
32     }
33     if constexpr (std::is_pointer_v<cpptype>) {
34         storeRes(AsEtsObject(res.value())->GetCoreType());
35     } else {
36         storeRes(Value(res.value()).GetAsLong());
37     }
38     return true;
39 }
40 
41 template <typename FStore>
ConvertRefArgToEts(InteropCtx * ctx,Class * klass,FStore & storeRes,napi_value jsVal)42 [[nodiscard]] static ALWAYS_INLINE inline bool ConvertRefArgToEts(InteropCtx *ctx, Class *klass, FStore &storeRes,
43                                                                   napi_value jsVal)
44 {
45     auto env = ctx->GetJSEnv();
46 
47     // start fastpath
48     if (IsUndefined(env, jsVal)) {
49         storeRes(nullptr);
50         return true;
51     }
52     if (IsNull(env, jsVal)) {
53         if (LIKELY(klass->IsAssignableFrom(ctx->GetNullValueClass()))) {
54             storeRes(ctx->GetNullValue()->GetCoreType());
55             return true;
56         }
57     }
58     if (klass == ctx->GetJSValueClass()) {
59         return UnwrapVal<JSConvertJSValue>(ctx, env, jsVal, storeRes);
60     }
61     if (klass == ctx->GetStringClass()) {
62         return UnwrapVal<JSConvertString>(ctx, env, jsVal, storeRes);
63     }
64     // start slowpath
65     auto refconv = JSRefConvertResolve<true>(ctx, klass);
66     if (UNLIKELY(refconv == nullptr)) {
67         return false;
68     }
69     ObjectHeader *res = refconv->Unwrap(ctx, jsVal)->GetCoreType();
70     storeRes(res);
71     return res != nullptr;
72 }
73 
74 template <typename FStore>
ConvertPrimArgToEts(InteropCtx * ctx,panda_file::Type::TypeId id,FStore & storeRes,napi_value jsVal)75 [[nodiscard]] static ALWAYS_INLINE inline bool ConvertPrimArgToEts(InteropCtx *ctx, panda_file::Type::TypeId id,
76                                                                    FStore &storeRes, napi_value jsVal)
77 {
78     auto env = ctx->GetJSEnv();
79 
80     auto unwrapVal = [&ctx, &env, &jsVal, &storeRes](auto convTag) {
81         using Convertor = typename decltype(convTag)::type;  // convTag acts as lambda template parameter
82         return UnwrapVal<Convertor>(ctx, env, jsVal, storeRes);
83     };
84     switch (id) {
85         case panda_file::Type::TypeId::VOID: {
86             return true;  // do nothing
87         }
88         case panda_file::Type::TypeId::U1:
89             return unwrapVal(helpers::TypeIdentity<JSConvertU1>());
90         case panda_file::Type::TypeId::I8:
91             return unwrapVal(helpers::TypeIdentity<JSConvertI8>());
92         case panda_file::Type::TypeId::U8:
93             return unwrapVal(helpers::TypeIdentity<JSConvertU8>());
94         case panda_file::Type::TypeId::I16:
95             return unwrapVal(helpers::TypeIdentity<JSConvertI16>());
96         case panda_file::Type::TypeId::U16:
97             return unwrapVal(helpers::TypeIdentity<JSConvertU16>());
98         case panda_file::Type::TypeId::I32:
99             return unwrapVal(helpers::TypeIdentity<JSConvertI32>());
100         case panda_file::Type::TypeId::U32:
101             return unwrapVal(helpers::TypeIdentity<JSConvertU32>());
102         case panda_file::Type::TypeId::I64:
103             return unwrapVal(helpers::TypeIdentity<JSConvertI64>());
104         case panda_file::Type::TypeId::U64:
105             return unwrapVal(helpers::TypeIdentity<JSConvertU64>());
106         case panda_file::Type::TypeId::F32:
107             return unwrapVal(helpers::TypeIdentity<JSConvertF32>());
108         case panda_file::Type::TypeId::F64:
109             return unwrapVal(helpers::TypeIdentity<JSConvertF64>());
110         default:
111             UNREACHABLE();
112     }
113 }
114 
115 template <typename FStore, typename GetClass>
ConvertArgToEts(InteropCtx * ctx,panda_file::Type type,FStore & storeRes,const GetClass & getClass,napi_value jsVal)116 [[nodiscard]] static ALWAYS_INLINE inline bool ConvertArgToEts(InteropCtx *ctx, panda_file::Type type, FStore &storeRes,
117                                                                const GetClass &getClass, napi_value jsVal)
118 {
119     auto id = type.GetId();
120     if (id == panda_file::Type::TypeId::REFERENCE) {
121         return ConvertRefArgToEts(ctx, getClass(), storeRes, jsVal);
122     }
123     return ConvertPrimArgToEts(ctx, id, storeRes, jsVal);
124 }
125 
126 template <typename FStore>
ConvertArgToEts(InteropCtx * ctx,ProtoReader & protoReader,FStore & storeRes,napi_value jsVal)127 [[nodiscard]] static ALWAYS_INLINE inline bool ConvertArgToEts(InteropCtx *ctx, ProtoReader &protoReader,
128                                                                FStore &storeRes, napi_value jsVal)
129 {
130     return ConvertArgToEts(
131         ctx, protoReader.GetType(), storeRes, [&protoReader]() { return protoReader.GetClass(); }, jsVal);
132 }
133 
134 template <typename RestParamsArray>
DoPackRestParameters(EtsCoroutine * coro,InteropCtx * ctx,ProtoReader & protoReader,Span<napi_value> jsargv)135 static ObjectHeader **DoPackRestParameters(EtsCoroutine *coro, InteropCtx *ctx, ProtoReader &protoReader,
136                                            Span<napi_value> jsargv)
137 {
138     const size_t numRestParams = jsargv.size();
139 
140     RestParamsArray *objArr = [&]() {
141         if constexpr (std::is_same_v<RestParamsArray, EtsObjectArray>) {
142             EtsClass *etsClass = EtsClass::FromRuntimeClass(protoReader.GetClass()->GetComponentType());
143             return RestParamsArray::Create(etsClass, numRestParams);
144         } else {
145             return RestParamsArray::Create(numRestParams);
146         }
147     }();
148 
149     auto convertValue = [](auto val) -> typename RestParamsArray::ValueType {
150         constexpr bool IS_VAL_PTR = std::is_pointer_v<decltype(val)> || std::is_null_pointer_v<decltype(val)>;
151         constexpr bool IS_OBJ_ARR = std::is_same_v<RestParamsArray, EtsObjectArray>;
152         // Clang-tidy gives false positive error.
153         if constexpr (IS_OBJ_ARR) {
154             if constexpr (IS_VAL_PTR) {
155                 return EtsObject::FromCoreType(static_cast<ObjectHeader *>(val));
156             }
157         }
158         if constexpr (!IS_OBJ_ARR) {
159             if constexpr (!IS_VAL_PTR) {
160                 return *reinterpret_cast<typename RestParamsArray::ValueType *>(&val);
161             }
162         }
163         UNREACHABLE();
164     };
165 
166     VMHandle<RestParamsArray> restArgsArray(coro, objArr->GetCoreType());
167     for (uint32_t restArgIdx = 0; restArgIdx < numRestParams; ++restArgIdx) {
168         auto jsVal = jsargv[restArgIdx];
169         auto store = [&convertValue, restArgIdx, &restArgsArray](auto val) {
170             restArgsArray.GetPtr()->Set(restArgIdx, convertValue(val));
171         };
172         auto klass = protoReader.GetClass()->GetComponentType();
173         auto klassCb = [klass]() { return klass; };
174         if (UNLIKELY(!ConvertArgToEts(ctx, klass->GetType(), store, klassCb, jsVal))) {
175             if (coro->HasPendingException()) {
176                 ctx->ForwardEtsException(coro);
177             }
178             ASSERT(ctx->SanityJSExceptionPending());
179             return nullptr;
180         }
181     }
182     return reinterpret_cast<ObjectHeader **>(restArgsArray.GetAddress());
183 }
184 
185 // CC-OFFNXT(G.FMT.06-CPP, huge_depth) solid logic
PackRestParameters(EtsCoroutine * coro,InteropCtx * ctx,ProtoReader & protoReader,Span<napi_value> jsargv)186 [[maybe_unused]] static ObjectHeader **PackRestParameters(EtsCoroutine *coro, InteropCtx *ctx, ProtoReader &protoReader,
187                                                           Span<napi_value> jsargv)
188 {
189     if (!protoReader.GetClass()->IsArrayClass()) {
190         ASSERT(protoReader.GetClass() == ctx->GetArrayClass());
191         const size_t numRestParams = jsargv.size();
192 
193         EtsArrayObject<EtsObject> *objArr = EtsArrayObject<EtsObject>::Create(numRestParams);
194         VMHandle<EtsArrayObject<EtsObject>> restArgsArray(coro, objArr->GetCoreType());
195         for (uint32_t restArgIdx = 0; restArgIdx < numRestParams; ++restArgIdx) {
196             auto jsVal = jsargv[restArgIdx];
197             auto store = [restArgIdx, &restArgsArray](ObjectHeader *val) {
198                 restArgsArray.GetPtr()->SetRef(restArgIdx, EtsObject::FromCoreType(val));
199             };
200             if (UNLIKELY(!ConvertRefArgToEts(ctx, ctx->GetObjectClass(), store, jsVal))) {
201                 if (coro->HasPendingException()) {
202                     ctx->ForwardEtsException(coro);
203                 }
204                 ASSERT(ctx->SanityJSExceptionPending());
205                 return nullptr;
206             }
207         }
208         return reinterpret_cast<ObjectHeader **>(restArgsArray.GetAddress());
209     }
210 
211     panda_file::Type restParamsItemType = protoReader.GetClass()->GetComponentType()->GetType();
212     switch (restParamsItemType.GetId()) {
213         case panda_file::Type::TypeId::U1:
214             return DoPackRestParameters<EtsBooleanArray>(coro, ctx, protoReader, jsargv);
215         case panda_file::Type::TypeId::I8:
216             return DoPackRestParameters<EtsByteArray>(coro, ctx, protoReader, jsargv);
217         case panda_file::Type::TypeId::I16:
218             return DoPackRestParameters<EtsShortArray>(coro, ctx, protoReader, jsargv);
219         case panda_file::Type::TypeId::U16:
220             return DoPackRestParameters<EtsCharArray>(coro, ctx, protoReader, jsargv);
221         case panda_file::Type::TypeId::I32:
222             return DoPackRestParameters<EtsIntArray>(coro, ctx, protoReader, jsargv);
223         case panda_file::Type::TypeId::I64:
224             return DoPackRestParameters<EtsLongArray>(coro, ctx, protoReader, jsargv);
225         case panda_file::Type::TypeId::F32:
226             return DoPackRestParameters<EtsFloatArray>(coro, ctx, protoReader, jsargv);
227         case panda_file::Type::TypeId::F64:
228             return DoPackRestParameters<EtsDoubleArray>(coro, ctx, protoReader, jsargv);
229         case panda_file::Type::TypeId::REFERENCE:
230             return DoPackRestParameters<EtsObjectArray>(coro, ctx, protoReader, jsargv);
231         default:
232             UNREACHABLE();
233     }
234 }
235 
236 template <typename FRead>
ConvertRefArgToJS(InteropCtx * ctx,napi_value * resSlot,FRead & readVal)237 [[nodiscard]] static ALWAYS_INLINE inline bool ConvertRefArgToJS(InteropCtx *ctx, napi_value *resSlot, FRead &readVal)
238 {
239     ASSERT(ctx != nullptr);
240     auto env = ctx->GetJSEnv();
241     auto setResult = [resSlot](napi_value res) {
242         *resSlot = res;
243         return res != nullptr;
244     };
245     auto wrapRef = [&env, setResult](auto convTag, ObjectHeader *ref) -> bool {
246         using Convertor = typename decltype(convTag)::type;  // conv_tag acts as lambda template parameter
247         using cpptype = typename Convertor::cpptype;         // NOLINT(readability-identifier-naming)
248         cpptype value = std::remove_pointer_t<cpptype>::FromEtsObject(EtsObject::FromCoreType(ref));
249         return setResult(Convertor::Wrap(env, value));
250     };
251 
252     ObjectHeader *ref = readVal(helpers::TypeIdentity<ObjectHeader *>());
253     if (UNLIKELY(ref == nullptr)) {
254         *resSlot = GetUndefined(env);
255         return true;
256     }
257     if (UNLIKELY(ref == ctx->GetNullValue()->GetCoreType())) {
258         *resSlot = GetNull(env);
259         return true;
260     }
261 
262     auto klass = ref->template ClassAddr<Class>();
263     // start fastpath
264     if (klass == ctx->GetJSValueClass()) {
265         return wrapRef(helpers::TypeIdentity<JSConvertJSValue>(), ref);
266     }
267     if (klass == ctx->GetStringClass()) {
268         return wrapRef(helpers::TypeIdentity<JSConvertString>(), ref);
269     }
270     // start slowpath
271     VMHandle<ObjectHeader> handle(EtsCoroutine::GetCurrent(), ref);
272     auto refconv = JSRefConvertResolve(ctx, klass);
273     if (refconv == nullptr) {
274         return false;
275     }
276     return setResult(refconv->Wrap(ctx, EtsObject::FromCoreType(handle.GetPtr())));
277 }
278 
279 template <typename FRead>
ConvertArgToJS(InteropCtx * ctx,ProtoReader & protoReader,napi_value * resSlot,FRead & readVal)280 [[nodiscard]] static ALWAYS_INLINE inline bool ConvertArgToJS(InteropCtx *ctx, ProtoReader &protoReader,
281                                                               napi_value *resSlot, FRead &readVal)
282 {
283     auto env = ctx->GetJSEnv();
284 
285     auto wrapPrim = [&env, &readVal, resSlot](auto convTag) -> bool {
286         using Convertor = typename decltype(convTag)::type;  // convTag acts as lambda template parameter
287         using cpptype = typename Convertor::cpptype;         // NOLINT(readability-identifier-naming)
288         napi_value res = Convertor::Wrap(env, readVal(helpers::TypeIdentity<cpptype>()));
289         *resSlot = res;
290         return res != nullptr;
291     };
292 
293     switch (protoReader.GetType().GetId()) {
294         case panda_file::Type::TypeId::VOID: {
295             *resSlot = GetUndefined(env);
296             return true;
297         }
298         case panda_file::Type::TypeId::U1:
299             return wrapPrim(helpers::TypeIdentity<JSConvertU1>());
300         case panda_file::Type::TypeId::I8:
301             return wrapPrim(helpers::TypeIdentity<JSConvertI8>());
302         case panda_file::Type::TypeId::U8:
303             return wrapPrim(helpers::TypeIdentity<JSConvertU8>());
304         case panda_file::Type::TypeId::I16:
305             return wrapPrim(helpers::TypeIdentity<JSConvertI16>());
306         case panda_file::Type::TypeId::U16:
307             return wrapPrim(helpers::TypeIdentity<JSConvertU16>());
308         case panda_file::Type::TypeId::I32:
309             return wrapPrim(helpers::TypeIdentity<JSConvertI32>());
310         case panda_file::Type::TypeId::U32:
311             return wrapPrim(helpers::TypeIdentity<JSConvertU32>());
312         case panda_file::Type::TypeId::I64:
313             return wrapPrim(helpers::TypeIdentity<JSConvertI64>());
314         case panda_file::Type::TypeId::U64:
315             return wrapPrim(helpers::TypeIdentity<JSConvertU64>());
316         case panda_file::Type::TypeId::F32:
317             return wrapPrim(helpers::TypeIdentity<JSConvertF32>());
318         case panda_file::Type::TypeId::F64:
319             return wrapPrim(helpers::TypeIdentity<JSConvertF64>());
320         case panda_file::Type::TypeId::REFERENCE:
321             return ConvertRefArgToJS(ctx, resSlot, readVal);
322         default:
323             UNREACHABLE();
324     }
325 }
326 
327 }  // namespace ark::ets::interop::js
328 
329 #endif  // PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_CALL_ARG_CONVERTORS_H
330