• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_CONVERT_H
17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_CONVERT_H
18 
19 #include "js_convert_base.h"
20 
21 namespace ark::ets::interop::js {
22 
23 template <typename Cpptype>
24 struct JSConvertNumeric : public JSConvertBase<JSConvertNumeric<Cpptype>, Cpptype> {
25     static constexpr const char *TYPE_NAME = "number";
26 
27     template <typename P = Cpptype>
WrapImplJSConvertNumeric28     static std::enable_if_t<std::is_integral_v<P>, napi_value> WrapImpl(napi_env env, Cpptype etsVal)
29     {
30         napi_value jsVal;
31         if constexpr (sizeof(Cpptype) >= sizeof(int32_t)) {
32             NAPI_CHECK_FATAL(napi_create_int64(env, etsVal, &jsVal));
33         } else if constexpr (std::is_signed_v<Cpptype>) {
34             NAPI_CHECK_FATAL(napi_create_int32(env, etsVal, &jsVal));
35         } else {
36             NAPI_CHECK_FATAL(napi_create_uint32(env, etsVal, &jsVal));
37         }
38         return jsVal;
39     }
40 
41     template <typename P = Cpptype>
UnwrapImplJSConvertNumeric42     static std::enable_if_t<std::is_integral_v<P>, std::optional<Cpptype>> UnwrapImpl([[maybe_unused]] InteropCtx *ctx,
43                                                                                       napi_env env, napi_value jsVal)
44     {
45         if (UNLIKELY(GetValueType(env, jsVal) != napi_number)) {
46             JSConvertNumeric::TypeCheckFailed();
47             return {};
48         }
49         Cpptype etsVal;
50         if constexpr (sizeof(Cpptype) >= sizeof(int32_t)) {
51             int64_t val;
52             NAPI_CHECK_FATAL(napi_get_value_int64(env, jsVal, &val));
53             etsVal = static_cast<Cpptype>(val);
54         } else if constexpr (std::is_signed_v<Cpptype>) {
55             int32_t val;
56             NAPI_CHECK_FATAL(napi_get_value_int32(env, jsVal, &val));
57             etsVal = static_cast<Cpptype>(val);
58         } else {
59             uint32_t val;
60             NAPI_CHECK_FATAL(napi_get_value_uint32(env, jsVal, &val));
61             etsVal = static_cast<Cpptype>(val);
62         }
63         return etsVal;
64     }
65 
66     template <typename P = Cpptype>
WrapImplJSConvertNumeric67     static std::enable_if_t<std::is_floating_point_v<P>, napi_value> WrapImpl(napi_env env, Cpptype etsVal)
68     {
69         napi_value jsVal;
70         NAPI_CHECK_FATAL(napi_create_double(env, etsVal, &jsVal));
71         return jsVal;
72     }
73 
74     template <typename P = Cpptype>
UnwrapImplJSConvertNumeric75     static std::enable_if_t<std::is_floating_point_v<P>, std::optional<Cpptype>> UnwrapImpl(
76         [[maybe_unused]] InteropCtx *ctx, napi_env env, napi_value jsVal)
77     {
78         if (UNLIKELY(GetValueType(env, jsVal) != napi_number)) {
79             JSConvertNumeric::TypeCheckFailed();
80             return {};
81         }
82         double val;
83         NAPI_CHECK_FATAL(napi_get_value_double(env, jsVal, &val));
84         return val;
85     }
86 };
87 
88 using JSConvertI8 = JSConvertNumeric<int8_t>;
89 using JSConvertU8 = JSConvertNumeric<uint8_t>;
90 using JSConvertI16 = JSConvertNumeric<int16_t>;
91 using JSConvertU16 = JSConvertNumeric<uint16_t>;
92 using JSConvertI32 = JSConvertNumeric<int32_t>;
93 using JSConvertU32 = JSConvertNumeric<uint32_t>;
94 using JSConvertI64 = JSConvertNumeric<int64_t>;
95 using JSConvertU64 = JSConvertNumeric<uint64_t>;
96 using JSConvertF32 = JSConvertNumeric<float>;
97 using JSConvertF64 = JSConvertNumeric<double>;
98 
99 JSCONVERT_DEFINE_TYPE(U1, bool);
JSCONVERT_WRAP(U1)100 JSCONVERT_WRAP(U1)
101 {
102     napi_value jsVal;
103     NAPI_CHECK_FATAL(napi_get_boolean(env, static_cast<bool>(etsVal), &jsVal));
104     return jsVal;
105 }
JSCONVERT_UNWRAP(U1)106 JSCONVERT_UNWRAP(U1)
107 {
108     if (UNLIKELY(GetValueType(env, jsVal) != napi_boolean)) {
109         TypeCheckFailed();
110         return {};
111     }
112     bool val;
113     NAPI_CHECK_FATAL(napi_get_value_bool(env, jsVal, &val));
114     return val;
115 }
116 
117 JSCONVERT_DEFINE_TYPE(StdlibBoolean, EtsObject *);
JSCONVERT_WRAP(StdlibBoolean)118 JSCONVERT_WRAP(StdlibBoolean)
119 {
120     auto *val = reinterpret_cast<EtsBoxPrimitive<EtsBoolean> *>(etsVal);
121     napi_value jsVal;
122     NAPI_CHECK_FATAL(napi_get_boolean(env, val->GetValue(), &jsVal));
123     return jsVal;
124 }
JSCONVERT_UNWRAP(StdlibBoolean)125 JSCONVERT_UNWRAP(StdlibBoolean)
126 {
127     bool val;
128     NAPI_CHECK_FATAL(napi_get_value_bool(env, jsVal, &val));
129     return EtsBoxPrimitive<EtsBoolean>::Create(EtsCoroutine::GetCurrent(), static_cast<EtsBoolean>(val));
130 }
131 
132 JSCONVERT_DEFINE_TYPE(StdlibByte, EtsObject *);
JSCONVERT_WRAP(StdlibByte)133 JSCONVERT_WRAP(StdlibByte)
134 {
135     auto *val = reinterpret_cast<EtsBoxPrimitive<EtsByte> *>(etsVal);
136     napi_value jsVal;
137     NAPI_CHECK_FATAL(napi_create_int32(env, val->GetValue(), &jsVal));
138     return jsVal;
139 }
JSCONVERT_UNWRAP(StdlibByte)140 JSCONVERT_UNWRAP(StdlibByte)
141 {
142     int32_t val;
143     NAPI_CHECK_FATAL(napi_get_value_int32(env, jsVal, &val));
144     if (val < std::numeric_limits<EtsByte>::min() || val > std::numeric_limits<EtsByte>::max()) {
145         TypeCheckFailed();
146         return {};
147     }
148     return EtsBoxPrimitive<EtsByte>::Create(EtsCoroutine::GetCurrent(), val);
149 }
150 
151 JSCONVERT_DEFINE_TYPE(StdlibChar, EtsObject *);
JSCONVERT_WRAP(StdlibChar)152 JSCONVERT_WRAP(StdlibChar)
153 {
154     auto *val = reinterpret_cast<EtsBoxPrimitive<EtsChar> *>(etsVal);
155     napi_value jsVal;
156     std::array<char16_t, 2U> str = {static_cast<char16_t>(val->GetValue()), 0};
157     NAPI_CHECK_FATAL(napi_create_string_utf16(env, str.data(), 1, &jsVal));
158     return jsVal;
159 }
JSCONVERT_UNWRAP(StdlibChar)160 JSCONVERT_UNWRAP(StdlibChar)
161 {
162     napi_valuetype type;
163     napi_typeof(env, jsVal, &type);
164     EtsChar val;
165     if (type == napi_number) {
166         int32_t ival;
167         NAPI_CHECK_FATAL(napi_get_value_int32(env, jsVal, &ival));
168         if (ival < 0 || ival > std::numeric_limits<EtsChar>::max()) {
169             TypeCheckFailed();
170             return {};
171         }
172         val = static_cast<uint16_t>(ival);
173     } else if (type == napi_string) {
174         size_t len = 0;
175         NAPI_CHECK_FATAL(napi_get_value_string_utf16(env, jsVal, nullptr, 0, &len));
176         if (len != 1) {
177             TypeCheckFailed();
178             return {};
179         }
180         char16_t cval;
181         NAPI_CHECK_FATAL(napi_get_value_string_utf16(env, jsVal, &cval, 1, &len));
182         val = static_cast<EtsChar>(cval);
183     } else {
184         TypeCheckFailed();
185         return {};
186     }
187     return EtsBoxPrimitive<EtsChar>::Create(EtsCoroutine::GetCurrent(), val);
188 }
189 
190 JSCONVERT_DEFINE_TYPE(StdlibShort, EtsObject *);
JSCONVERT_WRAP(StdlibShort)191 JSCONVERT_WRAP(StdlibShort)
192 {
193     auto *val = reinterpret_cast<EtsBoxPrimitive<EtsShort> *>(etsVal);
194     napi_value jsVal;
195     NAPI_CHECK_FATAL(napi_create_int32(env, val->GetValue(), &jsVal));
196     return jsVal;
197 }
JSCONVERT_UNWRAP(StdlibShort)198 JSCONVERT_UNWRAP(StdlibShort)
199 {
200     int32_t val;
201     NAPI_CHECK_FATAL(napi_get_value_int32(env, jsVal, &val));
202     if (val < std::numeric_limits<EtsShort>::min() || val > std::numeric_limits<EtsShort>::max()) {
203         TypeCheckFailed();
204         return {};
205     }
206     return EtsBoxPrimitive<EtsShort>::Create(EtsCoroutine::GetCurrent(), static_cast<EtsShort>(val));
207 }
208 
209 JSCONVERT_DEFINE_TYPE(StdlibInt, EtsObject *);
JSCONVERT_WRAP(StdlibInt)210 JSCONVERT_WRAP(StdlibInt)
211 {
212     auto *val = reinterpret_cast<EtsBoxPrimitive<EtsInt> *>(etsVal);
213     napi_value jsVal;
214     NAPI_CHECK_FATAL(napi_create_int32(env, val->GetValue(), &jsVal));
215     return jsVal;
216 }
JSCONVERT_UNWRAP(StdlibInt)217 JSCONVERT_UNWRAP(StdlibInt)
218 {
219     EtsInt val;
220     NAPI_CHECK_FATAL(napi_get_value_int32(env, jsVal, &val));
221     return EtsBoxPrimitive<EtsInt>::Create(EtsCoroutine::GetCurrent(), val);
222 }
223 
224 JSCONVERT_DEFINE_TYPE(StdlibLong, EtsObject *);
JSCONVERT_WRAP(StdlibLong)225 JSCONVERT_WRAP(StdlibLong)
226 {
227     auto *val = reinterpret_cast<EtsBoxPrimitive<EtsLong> *>(etsVal);
228     napi_value jsVal;
229     NAPI_CHECK_FATAL(napi_create_int64(env, val->GetValue(), &jsVal));
230     return jsVal;
231 }
JSCONVERT_UNWRAP(StdlibLong)232 JSCONVERT_UNWRAP(StdlibLong)
233 {
234     EtsLong val;
235     NAPI_CHECK_FATAL(napi_get_value_int64(env, jsVal, &val));
236     return EtsBoxPrimitive<EtsLong>::Create(EtsCoroutine::GetCurrent(), val);
237 }
238 
239 JSCONVERT_DEFINE_TYPE(StdlibFloat, EtsObject *);
JSCONVERT_WRAP(StdlibFloat)240 JSCONVERT_WRAP(StdlibFloat)
241 {
242     auto *val = reinterpret_cast<EtsBoxPrimitive<EtsFloat> *>(etsVal);
243     napi_value jsVal;
244     NAPI_CHECK_FATAL(napi_create_double(env, static_cast<double>(val->GetValue()), &jsVal));
245     return jsVal;
246 }
JSCONVERT_UNWRAP(StdlibFloat)247 JSCONVERT_UNWRAP(StdlibFloat)
248 {
249     double val;
250     NAPI_CHECK_FATAL(napi_get_value_double(env, jsVal, &val));
251     auto fval = static_cast<EtsFloat>(val);
252     if (fval != val) {
253         TypeCheckFailed();
254         return {};
255     }
256     return EtsBoxPrimitive<EtsFloat>::Create(EtsCoroutine::GetCurrent(), fval);
257 }
258 
259 JSCONVERT_DEFINE_TYPE(StdlibDouble, EtsObject *);
JSCONVERT_WRAP(StdlibDouble)260 JSCONVERT_WRAP(StdlibDouble)
261 {
262     auto *val = reinterpret_cast<EtsBoxPrimitive<EtsDouble> *>(etsVal);
263     napi_value jsVal;
264     NAPI_CHECK_FATAL(napi_create_double(env, val->GetValue(), &jsVal));
265     return jsVal;
266 }
JSCONVERT_UNWRAP(StdlibDouble)267 JSCONVERT_UNWRAP(StdlibDouble)
268 {
269     EtsDouble val;
270     NAPI_CHECK_FATAL(napi_get_value_double(env, jsVal, &val));
271     return EtsBoxPrimitive<EtsDouble>::Create(EtsCoroutine::GetCurrent(), val);
272 }
273 
274 JSCONVERT_DEFINE_TYPE(String, EtsString *);
JSCONVERT_WRAP(String)275 JSCONVERT_WRAP(String)
276 {
277     napi_value jsVal;
278     if (UNLIKELY(etsVal->IsUtf16())) {
279         auto str = reinterpret_cast<char16_t *>(etsVal->GetDataUtf16());
280         NAPI_CHECK_FATAL(napi_create_string_utf16(env, str, etsVal->GetUtf16Length(), &jsVal));
281     } else {
282         auto str = utf::Mutf8AsCString(etsVal->GetDataMUtf8());
283         // -1 for NULL terminated Mutf8 string!!!
284         NAPI_CHECK_FATAL(napi_create_string_utf8(env, str, etsVal->GetMUtf8Length() - 1, &jsVal));
285     }
286     return jsVal;
287 }
JSCONVERT_UNWRAP(String)288 JSCONVERT_UNWRAP(String)
289 {
290     if (UNLIKELY(GetValueType(env, jsVal) != napi_string)) {
291         TypeCheckFailed();
292         return {};
293     }
294     std::string value = GetString(env, jsVal);
295     return EtsString::CreateFromUtf8(value.data(), value.length());
296 }
297 JSCONVERT_DEFINE_TYPE(BigInt, EtsBigInt *);
JSCONVERT_WRAP(BigInt)298 JSCONVERT_WRAP(BigInt)
299 {
300     auto size = etsVal->GetBytes()->GetActualLength();  // size includes extra sign element
301     auto data = etsVal->GetBytes()->GetData();
302 
303     std::vector<uint32_t> etsArray;
304     etsArray.reserve(size);
305     for (size_t i = 0; i < size; ++i) {
306         etsArray.emplace_back(static_cast<uint32_t>(data->Get(i)->GetValue()));
307     }
308 
309     SmallVector<uint64_t, 4U> jsArray;
310     int sign = 0;
311     if (size > 1) {
312         sign = GeBigIntSign(etsArray);
313         if (sign == 1) {
314             ConvertFromTwosComplement(etsArray);
315         }
316 
317         jsArray = ConvertBigIntArrayFromEtsToJs(etsArray);
318     } else {
319         jsArray = {0};
320     }
321     napi_value jsVal;
322     NAPI_CHECK_FATAL(napi_create_bigint_words(env, sign, jsArray.size(), jsArray.data(), &jsVal));
323 
324     return jsVal;
325 }
JSCONVERT_UNWRAP(BigInt)326 JSCONVERT_UNWRAP(BigInt)
327 {
328     if (UNLIKELY(GetValueType(env, jsVal) != napi_bigint)) {
329         TypeCheckFailed();
330         return {};
331     }
332 
333     auto [words, signBit] = GetBigInt(env, jsVal);
334     std::vector<EtsInt> array = ConvertBigIntArrayFromJsToEts(words, signBit);
335 
336     auto arrayKlass = EtsClass::FromRuntimeClass(ctx->GetArrayClass());
337     auto etsIntArray = EtsTypedObjectArray<EtsBoxPrimitive<EtsInt>>::Create(arrayKlass, array.size());
338 
339     for (size_t i = 0; i < array.size(); ++i) {
340         etsIntArray->Set(i, EtsBoxPrimitive<EtsInt>::Create(EtsCoroutine::GetCurrent(), array[i]));
341     }
342 
343     auto bigIntArrayField = EtsBoxedIntArray::FromEtsObject(EtsObject::Create(arrayKlass));
344     bigIntArrayField->SetFieldPrimitive<EtsInt>(EtsBoxedIntArray::GetActualLengthOffset(), false, array.size());
345     bigIntArrayField->SetFieldObject(EtsBoxedIntArray::GetBufferOffset(), reinterpret_cast<EtsObject *>(etsIntArray));
346 
347     auto bigintKlass = EtsClass::FromRuntimeClass(ctx->GetBigIntClass());
348     auto bigInt = EtsBigInt::FromEtsObject(EtsObject::Create(bigintKlass));
349     bigInt->SetFieldObject(EtsBigInt::GetBytesOffset(), bigIntArrayField);
350 
351     return bigInt;
352 }
353 
354 JSCONVERT_DEFINE_TYPE(JSValue, JSValue *);
JSCONVERT_WRAP(JSValue)355 JSCONVERT_WRAP(JSValue)
356 {
357     return etsVal->GetNapiValue(env);
358 }
JSCONVERT_UNWRAP(JSValue)359 JSCONVERT_UNWRAP(JSValue)
360 {
361     return JSValue::Create(EtsCoroutine::GetCurrent(), ctx, jsVal);
362 }
363 
364 // JSError convertors are supposed to box JSValue objects, do not treat them in any other way
365 JSCONVERT_DEFINE_TYPE(JSError, EtsObject *);
JSCONVERT_WRAP(JSError)366 JSCONVERT_WRAP(JSError)
367 {
368     auto coro = EtsCoroutine::GetCurrent();
369     auto ctx = InteropCtx::Current(coro);
370 
371     auto klass = etsVal->GetClass();
372     INTEROP_FATAL_IF(klass->GetRuntimeClass() != ctx->GetJSErrorClass());
373 
374     // NOTE(vpukhov): remove call after adding a mirror-class for JSError
375     auto method = klass->GetMethod("getValue");
376     ASSERT(method != nullptr);
377     std::array args = {Value(etsVal->GetCoreType())};
378     auto val = JSValue::FromCoreType(method->GetPandaMethod()->Invoke(coro, args.data()).GetAs<ObjectHeader *>());
379     INTEROP_FATAL_IF(val == nullptr);
380     return val->GetNapiValue(env);
381 }
JSCONVERT_UNWRAP(JSError)382 JSCONVERT_UNWRAP(JSError)
383 {
384     auto coro = EtsCoroutine::GetCurrent();
385     auto value = JSValue::Create(coro, ctx, jsVal);
386     if (UNLIKELY(value == nullptr)) {
387         return {};
388     }
389     auto res = ctx->CreateETSCoreJSError(coro, value);
390     if (UNLIKELY(res == nullptr)) {
391         return {};
392     }
393     return res;
394 }
395 
396 JSCONVERT_DEFINE_TYPE(Promise, EtsPromise *);
397 
JSCONVERT_WRAP(Promise)398 JSCONVERT_WRAP(Promise)
399 {
400     auto *coro = EtsCoroutine::GetCurrent();
401     auto ctx = InteropCtx::Current(coro);
402     ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
403     // SharedReferenceStorage uses object's MarkWord to store interop hash.
404     // Also runtime may lock a Promise object (this operation also requires MarkWord modification).
405     // Interop hash and locks cannot co-exist. That is why a separate object (EtsPromiseRef) is used for
406     // mapping between js and ets Promise objects.
407     // When a ets Promise object goes to JS we should get the corresponding EtsPromiseRef object.
408     // So the ets Promise object should know about EtsPromiseRef which is stored in 'interopObject' field.
409     EtsObject *interopObj = etsVal->GetInteropObject(coro);
410     if (interopObj != nullptr && storage->HasReference(interopObj)) {
411         return storage->GetReference(interopObj)->GetJsObject(env);
412     }
413 
414     [[maybe_unused]] EtsHandleScope s(coro);
415     EtsHandle<EtsPromise> hpromise(coro, etsVal);
416     napi_deferred deferred;
417     napi_value jsPromise;
418     NAPI_CHECK_FATAL(napi_create_promise(env, &deferred, &jsPromise));
419     EtsMutex::LockHolder holder(hpromise);
420     uint32_t state = hpromise->GetState();
421     if (state != EtsPromise::STATE_PENDING) {
422         EtsHandle<EtsObject> value(coro, hpromise->GetValue(coro));
423         napi_value completionValue;
424         if (value.GetPtr() == nullptr) {
425             completionValue = GetNull(env);
426         } else {
427             auto refconv = JSRefConvertResolve(ctx, value->GetClass()->GetRuntimeClass());
428             completionValue = refconv->Wrap(ctx, value.GetPtr());
429         }
430         if (hpromise->IsResolved()) {
431             NAPI_CHECK_FATAL(napi_resolve_deferred(env, deferred, completionValue));
432         } else {
433             NAPI_CHECK_FATAL(napi_reject_deferred(env, deferred, completionValue));
434         }
435     } else {
436         ASSERT_MANAGED_CODE();
437         RemotePromiseResolver *resolver =
438             Runtime::GetCurrent()->GetInternalAllocator()->New<JsRemotePromiseResolver>(deferred);
439         hpromise->SetEtsPromiseResolver(resolver);
440     }
441     EtsPromiseRef *ref = EtsPromiseRef::Create(coro);
442     ref->SetTarget(coro, hpromise->AsObject());
443     hpromise->SetInteropObject(coro, ref);
444     [[maybe_unused]] auto *sharedRef = storage->CreateETSObjectRef(ctx, ref, jsPromise);
445     ASSERT(sharedRef != nullptr);
446     return jsPromise;
447 }
448 
JSCONVERT_UNWRAP(Promise)449 JSCONVERT_UNWRAP(Promise)
450 {
451 #ifndef NDEBUG
452     bool isPromise = false;
453     NAPI_CHECK_FATAL(napi_is_promise(env, jsVal, &isPromise));
454     ASSERT(isPromise);
455 #endif
456     auto coro = EtsCoroutine::GetCurrent();
457     ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
458     ets_proxy::SharedReference *sharedRef = storage->GetReference(env, jsVal);
459     if (sharedRef != nullptr) {
460         auto *ref = reinterpret_cast<EtsPromiseRef *>(sharedRef->GetEtsObject(ctx));
461         ASSERT(ref->GetTarget(coro) != nullptr);
462         return EtsPromise::FromEtsObject(ref->GetTarget(coro));
463     }
464 
465     [[maybe_unused]] EtsHandleScope s(coro);
466     auto *promise = EtsPromise::Create(coro);
467     EtsHandle<EtsPromise> hpromise(coro, promise);
468     EtsHandle<EtsPromiseRef> href(coro, EtsPromiseRef::Create(coro));
469     href->SetTarget(coro, hpromise->AsObject());
470     hpromise->SetInteropObject(coro, href.GetPtr());
471     storage->CreateJSObjectRef(ctx, href.GetPtr(), jsVal);
472     hpromise->SetLinkedPromise(coro, href->AsObject());
473     return hpromise.GetPtr();
474 }
475 
476 JSCONVERT_DEFINE_TYPE(ArrayBuffer, EtsArrayBuffer *);
477 
JSCONVERT_WRAP(ArrayBuffer)478 JSCONVERT_WRAP(ArrayBuffer)
479 {
480     napi_value buf;
481     void *data;
482     NAPI_CHECK_FATAL(napi_create_arraybuffer(env, etsVal->GetByteLength(), &data, &buf));
483     std::copy_n(reinterpret_cast<const uint8_t *>(etsVal->GetData()->GetData<const void *>()), etsVal->GetByteLength(),
484                 reinterpret_cast<uint8_t *>(data));
485     return buf;
486 }
487 
JSCONVERT_UNWRAP(ArrayBuffer)488 JSCONVERT_UNWRAP(ArrayBuffer)
489 {
490     bool isArraybuf = false;
491     NAPI_CHECK_FATAL(napi_is_arraybuffer(env, jsVal, &isArraybuf));
492     if (!isArraybuf) {
493         InteropCtx::Fatal("Passed object is not an ArrayBuffer!");
494         UNREACHABLE();
495     }
496     void *data = nullptr;
497     size_t byteLength = 0;
498     NAPI_CHECK_FATAL(napi_get_arraybuffer_info(env, jsVal, &data, &byteLength));
499     auto *currentCoro = EtsCoroutine::GetCurrent();
500     [[maybe_unused]] EtsHandleScope s(currentCoro);
501     EtsClass *arraybufKlass = currentCoro->GetPandaVM()->GetClassLinker()->GetArrayBufferClass();
502     EtsHandle<EtsArrayBuffer> buf(currentCoro,
503                                   reinterpret_cast<EtsArrayBuffer *>(EtsObject::Create(currentCoro, arraybufKlass)));
504     buf->SetByteLength(static_cast<EtsInt>(byteLength));
505     EtsHandle<EtsByteArray> byteArray(currentCoro, reinterpret_cast<EtsByteArray *>(EtsByteArray::Create(byteLength)));
506     buf->SetData(currentCoro, byteArray.GetPtr());
507     std::copy_n(reinterpret_cast<uint8_t *>(data), byteLength, buf->GetData()->GetData<EtsByte>());
508     return buf.GetPtr();
509 }
510 
511 JSCONVERT_DEFINE_TYPE(EtsUndefined, EtsObject *);
JSCONVERT_WRAP(EtsUndefined)512 JSCONVERT_WRAP(EtsUndefined)
513 {
514     return GetUndefined(env);
515 }
516 
JSCONVERT_UNWRAP(EtsUndefined)517 JSCONVERT_UNWRAP(EtsUndefined)
518 {
519     if (UNLIKELY(!IsUndefined(env, jsVal))) {
520         TypeCheckFailed();
521         return {};
522     }
523     return ctx->GetUndefinedObject();
524 }
525 
526 #undef JSCONVERT_DEFINE_TYPE
527 #undef JSCONVERT_WRAP
528 #undef JSCONVERT_UNWRAP
529 
530 template <typename T>
JSValueGetByName(InteropCtx * ctx,JSValue * jsvalue,const char * name)531 static ALWAYS_INLINE inline std::optional<typename T::cpptype> JSValueGetByName(InteropCtx *ctx, JSValue *jsvalue,
532                                                                                 const char *name)
533 {
534     auto env = ctx->GetJSEnv();
535     napi_value jsVal = jsvalue->GetNapiValue(env);
536     napi_status rc = napi_get_named_property(env, jsVal, name, &jsVal);
537     if (UNLIKELY(NapiThrownGeneric(rc))) {
538         return {};
539     }
540     return T::UnwrapWithNullCheck(ctx, env, jsVal);
541 }
542 
543 template <typename T>
JSValueSetByName(InteropCtx * ctx,JSValue * jsvalue,const char * name,typename T::cpptype etsPropVal)544 [[nodiscard]] static ALWAYS_INLINE inline bool JSValueSetByName(InteropCtx *ctx, JSValue *jsvalue, const char *name,
545                                                                 typename T::cpptype etsPropVal)
546 {
547     auto env = ctx->GetJSEnv();
548     napi_value jsVal = jsvalue->GetNapiValue(env);
549     napi_value jsPropVal = T::WrapWithNullCheck(env, etsPropVal);
550     if (UNLIKELY(jsPropVal == nullptr)) {
551         return false;
552     }
553     napi_status rc = napi_set_named_property(env, jsVal, name, jsPropVal);
554     return !NapiThrownGeneric(rc);
555 }
556 
557 }  // namespace ark::ets::interop::js
558 
559 #endif  // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_CONVERT_H
560