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
298 JSCONVERT_DEFINE_TYPE(JSValue, JSValue *);
JSCONVERT_WRAP(JSValue)299 JSCONVERT_WRAP(JSValue)
300 {
301 return etsVal->GetNapiValue(env);
302 }
JSCONVERT_UNWRAP(JSValue)303 JSCONVERT_UNWRAP(JSValue)
304 {
305 return JSValue::Create(EtsCoroutine::GetCurrent(), ctx, jsVal);
306 }
307
308 // JSError convertors are supposed to box JSValue objects, do not treat them in any other way
309 JSCONVERT_DEFINE_TYPE(JSError, EtsObject *);
JSCONVERT_WRAP(JSError)310 JSCONVERT_WRAP(JSError)
311 {
312 auto coro = EtsCoroutine::GetCurrent();
313 auto ctx = InteropCtx::Current(coro);
314
315 auto klass = etsVal->GetClass();
316 INTEROP_FATAL_IF(klass->GetRuntimeClass() != ctx->GetJSErrorClass());
317
318 // NOTE(vpukhov): remove call after adding a mirror-class for JSError
319 auto method = klass->GetMethod("getValue");
320 ASSERT(method != nullptr);
321 std::array args = {Value(etsVal->GetCoreType())};
322 auto val = JSValue::FromCoreType(method->GetPandaMethod()->Invoke(coro, args.data()).GetAs<ObjectHeader *>());
323 INTEROP_FATAL_IF(val == nullptr);
324 return val->GetNapiValue(env);
325 }
JSCONVERT_UNWRAP(JSError)326 JSCONVERT_UNWRAP(JSError)
327 {
328 auto coro = EtsCoroutine::GetCurrent();
329 auto value = JSValue::Create(coro, ctx, jsVal);
330 if (UNLIKELY(value == nullptr)) {
331 return {};
332 }
333 auto res = ctx->CreateETSCoreJSError(coro, value);
334 if (UNLIKELY(res == nullptr)) {
335 return {};
336 }
337 return res;
338 }
339
340 JSCONVERT_DEFINE_TYPE(Promise, EtsPromise *);
341
JSCONVERT_WRAP(Promise)342 JSCONVERT_WRAP(Promise)
343 {
344 auto *coro = EtsCoroutine::GetCurrent();
345 auto ctx = InteropCtx::Current(coro);
346 ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
347 // SharedReferenceStorage uses object's MarkWord to store interop hash.
348 // Also runtime may lock a Promise object (this operation also requires MarkWord modification).
349 // Interop hash and locks cannot co-exist. That is why a separate object (EtsPromiseRef) is used for
350 // mapping between js and ets Promise objects.
351 // When a ets Promise object goes to JS we should get the corresponding EtsPromiseRef object.
352 // So the ets Promise object should know about EtsPromiseRef which is stored in 'interopObject' field.
353 EtsObject *interopObj = etsVal->GetInteropObject(coro);
354 if (interopObj != nullptr && storage->HasReference(interopObj)) {
355 return storage->GetReference(interopObj)->GetJsObject(env);
356 }
357
358 [[maybe_unused]] EtsHandleScope s(coro);
359 EtsHandle<EtsPromise> hpromise(coro, etsVal);
360 napi_deferred deferred;
361 napi_value jsPromise;
362 NAPI_CHECK_FATAL(napi_create_promise(env, &deferred, &jsPromise));
363 hpromise->Lock();
364 uint32_t state = hpromise->GetState();
365 hpromise->Unlock();
366 if (state != EtsPromise::STATE_PENDING) {
367 EtsObject *value = hpromise->GetValue(coro);
368 napi_value completionValue;
369 if (value == nullptr) {
370 completionValue = GetNull(env);
371 } else {
372 auto refconv = JSRefConvertResolve(ctx, value->GetClass()->GetRuntimeClass());
373 completionValue = refconv->Wrap(ctx, value);
374 }
375 if (etsVal->IsResolved()) {
376 NAPI_CHECK_FATAL(napi_resolve_deferred(env, deferred, completionValue));
377 } else {
378 NAPI_CHECK_FATAL(napi_reject_deferred(env, deferred, completionValue));
379 }
380 } else {
381 ASSERT_MANAGED_CODE();
382 Method *connect = ctx->GetPromiseInteropConnectMethod();
383 std::array<Value, 2U> args = {Value(hpromise.GetPtr()), Value(reinterpret_cast<int64_t>(deferred))};
384 connect->Invoke(coro, args.data());
385 }
386 EtsPromiseRef *ref = EtsPromiseRef::Create(coro);
387 ref->SetTarget(coro, hpromise->AsObject());
388 hpromise->SetInteropObject(coro, ref);
389 [[maybe_unused]] auto *sharedRef = storage->CreateETSObjectRef(ctx, ref, jsPromise);
390 ASSERT(sharedRef != nullptr);
391 return jsPromise;
392 }
393
JSCONVERT_UNWRAP(Promise)394 JSCONVERT_UNWRAP(Promise)
395 {
396 #ifndef NDEBUG
397 bool isPromise = false;
398 NAPI_CHECK_FATAL(napi_is_promise(env, jsVal, &isPromise));
399 ASSERT(isPromise);
400 #endif
401 auto coro = EtsCoroutine::GetCurrent();
402 ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
403 ets_proxy::SharedReference *sharedRef = storage->GetReference(env, jsVal);
404 if (sharedRef != nullptr) {
405 auto *ref = reinterpret_cast<EtsPromiseRef *>(sharedRef->GetEtsObject(ctx));
406 ASSERT(ref->GetTarget(coro) != nullptr);
407 return EtsPromise::FromEtsObject(ref->GetTarget(coro));
408 }
409
410 [[maybe_unused]] EtsHandleScope s(coro);
411 auto *promise = EtsPromise::Create(coro);
412 EtsHandle<EtsPromise> hpromise(coro, promise);
413 EtsHandle<EtsPromiseRef> href(coro, EtsPromiseRef::Create(coro));
414 href->SetTarget(coro, hpromise->AsObject());
415 hpromise->SetInteropObject(coro, href.GetPtr());
416 storage->CreateJSObjectRef(ctx, href.GetPtr(), jsVal);
417 hpromise->SetLinkedPromise(coro, href->AsObject());
418 return hpromise.GetPtr();
419 }
420
421 JSCONVERT_DEFINE_TYPE(ArrayBuffer, EtsArrayBuffer *);
422
JSCONVERT_WRAP(ArrayBuffer)423 JSCONVERT_WRAP(ArrayBuffer)
424 {
425 napi_value buf;
426 void *data;
427 NAPI_CHECK_FATAL(napi_create_arraybuffer(env, etsVal->GetByteLength(), &data, &buf));
428 std::copy_n(reinterpret_cast<const uint8_t *>(etsVal->GetData()->GetData<const void *>()), etsVal->GetByteLength(),
429 reinterpret_cast<uint8_t *>(data));
430 return buf;
431 }
432
JSCONVERT_UNWRAP(ArrayBuffer)433 JSCONVERT_UNWRAP(ArrayBuffer)
434 {
435 bool isArraybuf = false;
436 NAPI_CHECK_FATAL(napi_is_arraybuffer(env, jsVal, &isArraybuf));
437 if (!isArraybuf) {
438 InteropCtx::Fatal("Passed object is not an ArrayBuffer!");
439 UNREACHABLE();
440 }
441 void *data = nullptr;
442 size_t byteLength = 0;
443 NAPI_CHECK_FATAL(napi_get_arraybuffer_info(env, jsVal, &data, &byteLength));
444 auto *currentCoro = EtsCoroutine::GetCurrent();
445 [[maybe_unused]] EtsHandleScope s(currentCoro);
446 EtsClass *arraybufKlass = currentCoro->GetPandaVM()->GetClassLinker()->GetArrayBufferClass();
447 EtsHandle<EtsArrayBuffer> buf(currentCoro,
448 reinterpret_cast<EtsArrayBuffer *>(EtsObject::Create(currentCoro, arraybufKlass)));
449 buf->SetByteLength(static_cast<EtsInt>(byteLength));
450 buf->SetData(currentCoro, EtsByteArray::Create(byteLength));
451 std::copy_n(reinterpret_cast<uint8_t *>(data), byteLength, buf->GetData()->GetData<EtsByte>());
452 return buf.GetPtr();
453 }
454
455 JSCONVERT_DEFINE_TYPE(EtsUndefined, EtsObject *);
JSCONVERT_WRAP(EtsUndefined)456 JSCONVERT_WRAP(EtsUndefined)
457 {
458 return GetUndefined(env);
459 }
460
JSCONVERT_UNWRAP(EtsUndefined)461 JSCONVERT_UNWRAP(EtsUndefined)
462 {
463 if (UNLIKELY(!IsUndefined(env, jsVal))) {
464 TypeCheckFailed();
465 return {};
466 }
467 return ctx->GetUndefinedObject();
468 }
469
470 #undef JSCONVERT_DEFINE_TYPE
471 #undef JSCONVERT_WRAP
472 #undef JSCONVERT_UNWRAP
473
474 template <typename T>
JSValueGetByName(InteropCtx * ctx,JSValue * jsvalue,const char * name)475 static ALWAYS_INLINE inline std::optional<typename T::cpptype> JSValueGetByName(InteropCtx *ctx, JSValue *jsvalue,
476 const char *name)
477 {
478 auto env = ctx->GetJSEnv();
479 napi_value jsVal = jsvalue->GetNapiValue(env);
480 napi_status rc = napi_get_named_property(env, jsVal, name, &jsVal);
481 if (UNLIKELY(NapiThrownGeneric(rc))) {
482 return {};
483 }
484 return T::UnwrapWithNullCheck(ctx, env, jsVal);
485 }
486
487 template <typename T>
JSValueSetByName(InteropCtx * ctx,JSValue * jsvalue,const char * name,typename T::cpptype etsPropVal)488 [[nodiscard]] static ALWAYS_INLINE inline bool JSValueSetByName(InteropCtx *ctx, JSValue *jsvalue, const char *name,
489 typename T::cpptype etsPropVal)
490 {
491 auto env = ctx->GetJSEnv();
492 napi_value jsVal = jsvalue->GetNapiValue(env);
493 napi_value jsPropVal = T::WrapWithNullCheck(env, etsPropVal);
494 if (UNLIKELY(jsPropVal == nullptr)) {
495 return false;
496 }
497 napi_status rc = napi_set_named_property(env, jsVal, name, jsPropVal);
498 return !NapiThrownGeneric(rc);
499 }
500
501 } // namespace ark::ets::interop::js
502
503 #endif // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_JS_CONVERT_H
504