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