1 /**
2 * Copyright (c) 2023-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 #include "macros.h"
17 #include "plugins/ets/runtime/interop_js/call/call.h"
18 #include "plugins/ets/runtime/interop_js/js_convert.h"
19 #include "plugins/ets/runtime/interop_js/js_value.h"
20 #include "plugins/ets/runtime/interop_js/interop_common.h"
21 #include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h"
22 #include "plugins/ets/runtime/interop_js/code_scopes.h"
23 #include "plugins/ets/runtime/interop_js/logger.h"
24 #include "plugins/ets/runtime/types/ets_string.h"
25 #include "runtime/include/class_linker-inl.h"
26 #include "runtime/coroutines/stackful_coroutine.h"
27 #include "types/ets_object.h"
28
29 // NOLINTBEGIN(readability-identifier-naming)
30 // CC-OFFNXT(G.FMT.10-CPP) project code style
31 napi_status __attribute__((weak))
32 napi_load_module_with_module_request(napi_env env, const char *request_name, napi_value *result);
33 // NOLINTEND(readability-identifier-naming)
34
StringStartWith(std::string_view str,std::string_view startStr)35 static bool StringStartWith(std::string_view str, std::string_view startStr)
36 {
37 size_t startStrLen = startStr.length();
38 return str.compare(0, startStrLen, startStr) == 0;
39 }
40
41 namespace ark::ets::interop::js {
42
NotNativeOhmUrl(std::string_view url)43 [[maybe_unused]] static bool NotNativeOhmUrl(std::string_view url)
44 {
45 constexpr std::string_view PREFIX_BUNDLE = "@bundle:";
46 constexpr std::string_view PREFIX_NORMALIZED = "@normalized:";
47 constexpr std::string_view PREFIX_PACKAGE = "@package:";
48 return StringStartWith(url, PREFIX_BUNDLE) || StringStartWith(url, PREFIX_NORMALIZED) ||
49 StringStartWith(url, PREFIX_PACKAGE) || (url[0] != '@');
50 }
51
LoadJSModule(const PandaString & moduleName)52 [[maybe_unused]] static JSValue *LoadJSModule(const PandaString &moduleName)
53 {
54 auto coro = EtsCoroutine::GetCurrent();
55 auto ctx = InteropCtx::Current(coro);
56 if (ctx == nullptr) {
57 ThrowNoInteropContextException();
58 return nullptr;
59 }
60 INTEROP_CODE_SCOPE_ETS(coro);
61 auto env = ctx->GetJSEnv();
62 napi_value result;
63 napi_status status;
64 {
65 ScopedNativeCodeThread etsNativeScope(coro);
66 NapiScope jsHandleScope(env);
67 status = napi_load_module_with_module_request(env, moduleName.c_str(), &result);
68 if (status == napi_pending_exception) {
69 napi_value exp;
70 NAPI_CHECK_FATAL(napi_get_and_clear_last_exception(env, &exp));
71 NAPI_CHECK_FATAL(napi_fatal_exception(env, exp));
72 INTEROP_LOG(FATAL) << "Unable to load module due to exception";
73 UNREACHABLE();
74 }
75 }
76 INTEROP_FATAL_IF(status != napi_ok);
77 INTEROP_FATAL_IF(IsUndefined(env, result));
78 return JSValue::CreateRefValue(coro, ctx, result, napi_object);
79 }
80
JSRuntimeFinalizationRegistryCallback(EtsObject * cbarg)81 void JSRuntimeFinalizationRegistryCallback(EtsObject *cbarg)
82 {
83 auto coro = EtsCoroutine::GetCurrent();
84 auto ctx = InteropCtx::Current(coro);
85 if (ctx == nullptr) {
86 ThrowNoInteropContextException();
87 return;
88 }
89 ASSERT(cbarg->GetClass()->GetRuntimeClass() == ctx->GetJSValueClass());
90 return JSValue::FinalizeETSWeak(ctx, cbarg);
91 }
92
JSRuntimeNewJSValueDouble(double v)93 JSValue *JSRuntimeNewJSValueDouble(double v)
94 {
95 auto coro = EtsCoroutine::GetCurrent();
96 auto ctx = InteropCtx::Current(coro);
97 if (ctx == nullptr) {
98 ThrowNoInteropContextException();
99 return nullptr;
100 }
101 return JSValue::CreateNumber(coro, ctx, v);
102 }
103
JSRuntimeNewJSValueBoolean(uint8_t v)104 JSValue *JSRuntimeNewJSValueBoolean(uint8_t v)
105 {
106 auto coro = EtsCoroutine::GetCurrent();
107 auto ctx = InteropCtx::Current(coro);
108 if (ctx == nullptr) {
109 ThrowNoInteropContextException();
110 return nullptr;
111 }
112 return JSValue::CreateBoolean(coro, ctx, static_cast<bool>(v));
113 }
114
JSRuntimeNewJSValueString(EtsString * v)115 JSValue *JSRuntimeNewJSValueString(EtsString *v)
116 {
117 auto coro = EtsCoroutine::GetCurrent();
118 auto ctx = InteropCtx::Current(coro);
119 if (ctx == nullptr) {
120 ThrowNoInteropContextException();
121 return nullptr;
122 }
123 std::string str;
124 if (v->IsUtf16()) {
125 InteropCtx::Fatal("not implemented");
126 } else {
127 str = std::string(utf::Mutf8AsCString(v->GetDataMUtf8()), v->GetLength());
128 }
129 return JSValue::CreateString(coro, ctx, std::move(str));
130 }
131
JSRuntimeNewJSValueObject(EtsObject * v)132 JSValue *JSRuntimeNewJSValueObject(EtsObject *v)
133 {
134 if (v == nullptr) {
135 return nullptr;
136 }
137
138 auto coro = EtsCoroutine::GetCurrent();
139 auto ctx = InteropCtx::Current(coro);
140 if (ctx == nullptr) {
141 ThrowNoInteropContextException();
142 return nullptr;
143 }
144 if (v->GetClass() == PlatformTypes(coro)->interopJSValue) {
145 return JSValue::FromEtsObject(v);
146 }
147 INTEROP_CODE_SCOPE_ETS(coro);
148 auto env = ctx->GetJSEnv();
149 NapiScope jsHandleScope(env);
150
151 auto refconv = JSRefConvertResolve(ctx, v->GetClass()->GetRuntimeClass());
152 if (UNLIKELY(refconv == nullptr)) {
153 // For builtin without supported converter
154 ASSERT(coro->HasPendingException());
155 return nullptr;
156 }
157 auto result = refconv->Wrap(ctx, v);
158
159 auto res = JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result);
160 if (!res) {
161 ctx->ForwardJSException(coro);
162 return nullptr;
163 }
164
165 return res.value();
166 }
167
JSRuntimeIsJSValue(EtsObject * v)168 uint8_t JSRuntimeIsJSValue(EtsObject *v)
169 {
170 if (v == nullptr) {
171 return 0U;
172 }
173 auto coro = EtsCoroutine::GetCurrent();
174 return static_cast<uint8_t>(v->GetClass() == PlatformTypes(coro)->interopJSValue);
175 }
176
JSRuntimeNewJSValueBigInt(EtsBigInt * v)177 JSValue *JSRuntimeNewJSValueBigInt(EtsBigInt *v)
178 {
179 if (v == nullptr) {
180 return nullptr;
181 }
182
183 auto coro = EtsCoroutine::GetCurrent();
184 auto ctx = InteropCtx::Current(coro);
185 INTEROP_CODE_SCOPE_ETS(coro);
186 auto env = ctx->GetJSEnv();
187 NapiScope jsHandleScope(env);
188
189 auto refconv = JSRefConvertResolve(ctx, v->GetClass()->GetRuntimeClass());
190 auto result = refconv->Wrap(ctx, v);
191
192 auto res = JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result);
193 if (!res) {
194 ctx->ForwardJSException(coro);
195 return {};
196 }
197
198 return res.value();
199 }
200
JSRuntimeGetValueDouble(JSValue * etsJsValue)201 double JSRuntimeGetValueDouble(JSValue *etsJsValue)
202 {
203 return etsJsValue->GetNumber();
204 }
205
JSRuntimeGetValueBoolean(JSValue * etsJsValue)206 uint8_t JSRuntimeGetValueBoolean(JSValue *etsJsValue)
207 {
208 return static_cast<uint8_t>(etsJsValue->GetBoolean());
209 }
210
JSRuntimeGetValueString(JSValue * etsJsValue)211 EtsString *JSRuntimeGetValueString(JSValue *etsJsValue)
212 {
213 ASSERT(etsJsValue != nullptr);
214
215 auto coro = EtsCoroutine::GetCurrent();
216 auto ctx = InteropCtx::Current(coro);
217 if (ctx == nullptr) {
218 ThrowNoInteropContextException();
219 return nullptr;
220 }
221 auto env = ctx->GetJSEnv();
222
223 NapiScope jsHandleScope(env);
224
225 napi_value jsVal = JSConvertJSValue::Wrap(env, etsJsValue);
226 auto res = JSConvertString::Unwrap(ctx, env, jsVal);
227 if (UNLIKELY(!res)) {
228 if (NapiIsExceptionPending(env)) {
229 ctx->ForwardJSException(coro);
230 }
231 ASSERT(ctx->SanityETSExceptionPending());
232 return nullptr;
233 }
234
235 return res.value();
236 }
237
JSRuntimeGetValueObject(JSValue * etsJsValue,EtsClass * clsObj)238 EtsObject *JSRuntimeGetValueObject(JSValue *etsJsValue, EtsClass *clsObj)
239 {
240 auto coro = EtsCoroutine::GetCurrent();
241 auto ctx = InteropCtx::Current(coro);
242 if (ctx == nullptr) {
243 ThrowNoInteropContextException();
244 return nullptr;
245 }
246 auto cls = clsObj->GetRuntimeClass();
247
248 if (etsJsValue == nullptr) {
249 return nullptr;
250 }
251
252 // NOTE(kprokopenko): awful solution, see #14765
253 if (etsJsValue->AsObject() == ctx->GetNullValue()) {
254 return etsJsValue->AsObject();
255 }
256
257 INTEROP_CODE_SCOPE_ETS(coro);
258 auto env = ctx->GetJSEnv();
259 NapiScope jsHandleScope(env);
260 napi_value jsVal = JSConvertJSValue::Wrap(env, etsJsValue);
261 if (IsUndefined(env, jsVal)) {
262 return nullptr;
263 }
264
265 auto refconv = JSRefConvertResolve<true>(ctx, cls);
266 if (UNLIKELY(refconv == nullptr)) {
267 if (NapiIsExceptionPending(env)) {
268 ctx->ForwardJSException(coro);
269 }
270 ASSERT(ctx->SanityETSExceptionPending());
271 return nullptr;
272 }
273
274 auto res = refconv->Unwrap(ctx, jsVal);
275 if (UNLIKELY(!res)) {
276 if (NapiIsExceptionPending(env)) {
277 ctx->ForwardJSException(coro);
278 }
279 res = nullptr;
280 }
281 return res;
282 }
283
JSRuntimeGetUndefined()284 JSValue *JSRuntimeGetUndefined()
285 {
286 auto coro = EtsCoroutine::GetCurrent();
287 auto ctx = InteropCtx::Current(coro);
288 if (ctx == nullptr) {
289 ThrowNoInteropContextException();
290 return nullptr;
291 }
292 return JSValue::CreateUndefined(coro, ctx);
293 }
294
JSRuntimeGetNull()295 JSValue *JSRuntimeGetNull()
296 {
297 auto coro = EtsCoroutine::GetCurrent();
298 auto ctx = InteropCtx::Current(coro);
299 if (ctx == nullptr) {
300 ThrowNoInteropContextException();
301 return nullptr;
302 }
303 return JSValue::CreateNull(coro, ctx);
304 }
305
JSRuntimeGetGlobal()306 JSValue *JSRuntimeGetGlobal()
307 {
308 auto coro = EtsCoroutine::GetCurrent();
309 auto ctx = InteropCtx::Current(coro);
310 if (ctx == nullptr) {
311 ThrowNoInteropContextException();
312 return nullptr;
313 }
314 INTEROP_CODE_SCOPE_ETS(coro);
315 auto env = ctx->GetJSEnv();
316 NapiScope jsHandleScope(env);
317
318 return JSValue::CreateRefValue(coro, ctx, GetGlobal(env), napi_object);
319 }
320
JSRuntimeCreateObject()321 JSValue *JSRuntimeCreateObject()
322 {
323 auto coro = EtsCoroutine::GetCurrent();
324 auto ctx = InteropCtx::Current(coro);
325 if (ctx == nullptr) {
326 ThrowNoInteropContextException();
327 return nullptr;
328 }
329 INTEROP_CODE_SCOPE_ETS(coro);
330 auto env = ctx->GetJSEnv();
331 NapiScope jsHandleScope(env);
332
333 napi_value obj;
334 NAPI_CHECK_FATAL(napi_create_object(env, &obj));
335 return JSValue::CreateRefValue(coro, ctx, obj, napi_object);
336 }
337
JSRuntimeCreateArray()338 JSValue *JSRuntimeCreateArray()
339 {
340 auto coro = EtsCoroutine::GetCurrent();
341 auto ctx = InteropCtx::Current(coro);
342 INTEROP_CODE_SCOPE_ETS(coro);
343 auto env = ctx->GetJSEnv();
344 NapiScope jsHandleScope(env);
345
346 napi_value array;
347 NAPI_CHECK_FATAL(napi_create_array(env, &array));
348 return JSValue::Create(coro, ctx, array);
349 }
350
JSRuntimeInstanceOfDynamic(JSValue * object,JSValue * ctor)351 uint8_t JSRuntimeInstanceOfDynamic(JSValue *object, JSValue *ctor)
352 {
353 auto coro = EtsCoroutine::GetCurrent();
354 auto ctx = InteropCtx::Current(coro);
355 if (ctx == nullptr) {
356 ThrowNoInteropContextException();
357 return 0;
358 }
359 INTEROP_CODE_SCOPE_ETS(coro);
360 auto env = ctx->GetJSEnv();
361 NapiScope jsHandleScope(env);
362
363 auto jsObj = JSConvertJSValue::WrapWithNullCheck(env, object);
364 auto jsCtor = JSConvertJSValue::WrapWithNullCheck(env, ctor);
365 bool res;
366 napi_status rc = napi_instanceof(env, jsObj, jsCtor, &res);
367 if (UNLIKELY(NapiThrownGeneric(rc))) {
368 ctx->ForwardJSException(coro);
369 return 0;
370 }
371 return static_cast<uint8_t>(res);
372 }
373
JSRuntimeInstanceOfStatic(JSValue * etsJsValue,EtsClass * etsCls)374 uint8_t JSRuntimeInstanceOfStatic(JSValue *etsJsValue, EtsClass *etsCls)
375 {
376 ASSERT(etsCls != nullptr);
377
378 if (etsJsValue == nullptr) {
379 return 0;
380 }
381
382 auto coro = EtsCoroutine::GetCurrent();
383 auto ctx = InteropCtx::Current(coro);
384 if (ctx == nullptr) {
385 ThrowNoInteropContextException();
386 return 0;
387 }
388 auto env = ctx->GetJSEnv();
389
390 Class *cls = etsCls->GetRuntimeClass();
391 if (!cls->IsInitialized() && !IsStdClass(cls)) {
392 // 'js_value' haven't been created from the uninitialized class
393 return 0;
394 }
395
396 // Check if object has SharedReference
397 ets_proxy::SharedReference *sharedRef = [=] {
398 NapiScope jsHandleScope(env);
399 napi_value jsValue = JSConvertJSValue::Wrap(env, etsJsValue);
400 return ctx->GetSharedRefStorage()->GetReference(env, jsValue);
401 }();
402
403 if (sharedRef != nullptr) {
404 EtsObject *etsObject = sharedRef->GetEtsObject();
405 return static_cast<uint8_t>(etsCls->IsAssignableFrom(etsObject->GetClass()));
406 }
407
408 if (IsStdClass(cls)) {
409 // NOTE(v.cherkashin): Add support compat types, #13577
410 INTEROP_LOG(FATAL) << __func__ << " doesn't support compat types";
411 }
412
413 return 0;
414 }
415
ResolveModuleName(std::string_view module)416 std::pair<std::string_view, std::string_view> ResolveModuleName(std::string_view module)
417 {
418 static const std::unordered_set<std::string_view> NATIVE_MODULE_LIST = {
419 "@system.app", "@ohos.app", "@system.router", "@system.curves",
420 "@ohos.curves", "@system.matrix4", "@ohos.matrix4"};
421
422 constexpr std::string_view REQUIRE = "require";
423 constexpr std::string_view REQUIRE_NAPI = "requireNapi";
424 constexpr std::string_view REQUIRE_NATIVE_MODULE = "requireNativeModule";
425 constexpr std::string_view SYSTEM_PLUGIN_PREFIX = "@system.";
426 constexpr std::string_view OHOS_PLUGIN_PREFIX = "@ohos.";
427
428 if (NATIVE_MODULE_LIST.count(module) != 0) {
429 return {module.substr(1), REQUIRE_NATIVE_MODULE};
430 }
431
432 if (module.compare(0, SYSTEM_PLUGIN_PREFIX.size(), SYSTEM_PLUGIN_PREFIX) == 0) {
433 return {module.substr(SYSTEM_PLUGIN_PREFIX.size()), REQUIRE_NAPI};
434 }
435
436 if (module.compare(0, OHOS_PLUGIN_PREFIX.size(), OHOS_PLUGIN_PREFIX) == 0) {
437 return {module.substr(OHOS_PLUGIN_PREFIX.size()), REQUIRE_NAPI};
438 }
439
440 return {module, REQUIRE};
441 }
442
JSRuntimeLoadModule(EtsString * module)443 JSValue *JSRuntimeLoadModule(EtsString *module)
444 {
445 auto coro = EtsCoroutine::GetCurrent();
446 auto ctx = InteropCtx::Current(coro);
447 if (ctx == nullptr) {
448 ThrowNoInteropContextException();
449 return nullptr;
450 }
451 INTEROP_CODE_SCOPE_ETS(coro);
452 auto env = ctx->GetJSEnv();
453
454 PandaString moduleName = module->GetMutf8();
455 #if defined(PANDA_TARGET_OHOS) || defined(PANDA_JS_ETS_HYBRID_MODE)
456 if (NotNativeOhmUrl(moduleName)) {
457 return LoadJSModule(moduleName);
458 }
459 #endif
460 auto [mod, func] = ResolveModuleName(moduleName);
461
462 napi_value modObj;
463 {
464 ScopedNativeCodeThread etsNativeScope(coro);
465 NapiEscapableScope jsHandleScope(env);
466 napi_value requireFn;
467 NAPI_CHECK_FATAL(napi_get_named_property(env, GetGlobal(env), func.data(), &requireFn));
468
469 INTEROP_FATAL_IF(GetValueType(env, requireFn) != napi_function);
470 {
471 napi_value jsName;
472 NAPI_CHECK_FATAL(napi_create_string_utf8(env, mod.data(), NAPI_AUTO_LENGTH, &jsName));
473 std::array<napi_value, 1> args = {jsName};
474 napi_value recv;
475 NAPI_CHECK_FATAL(napi_get_undefined(env, &recv));
476 auto status = (napi_call_function(env, recv, requireFn, args.size(), args.data(), &modObj));
477 if (status == napi_pending_exception) {
478 napi_value exp;
479 NAPI_CHECK_FATAL(napi_get_and_clear_last_exception(env, &exp));
480 NAPI_CHECK_FATAL(napi_fatal_exception(env, exp));
481 INTEROP_LOG(FATAL) << "Unable to load module due to exception";
482 UNREACHABLE();
483 }
484 INTEROP_FATAL_IF(status != napi_ok);
485 }
486 INTEROP_FATAL_IF(IsNull(env, modObj));
487 jsHandleScope.Escape(modObj);
488 }
489
490 ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
491 ets_proxy::SharedReference *sharedRef = storage->GetReference(env, modObj);
492 if (sharedRef != nullptr) {
493 return JSValue::FromEtsObject(sharedRef->GetEtsObject());
494 }
495 return JSValue::CreateRefValue(coro, ctx, modObj, napi_object);
496 }
497
JSRuntimeStrictEqual(JSValue * lhs,JSValue * rhs)498 uint8_t JSRuntimeStrictEqual([[maybe_unused]] JSValue *lhs, [[maybe_unused]] JSValue *rhs)
499 {
500 auto coro = EtsCoroutine::GetCurrent();
501 auto ctx = InteropCtx::Current(coro);
502 if (ctx == nullptr) {
503 ThrowNoInteropContextException();
504 return 0;
505 }
506 INTEROP_CODE_SCOPE_ETS(coro);
507 auto env = ctx->GetJSEnv();
508 NapiScope jsHandleScope(env);
509
510 bool result = false;
511 NAPI_CHECK_FATAL(napi_strict_equals(env, JSConvertJSValue::WrapWithNullCheck(env, lhs),
512 JSConvertJSValue::WrapWithNullCheck(env, rhs), &result));
513 return static_cast<uint8_t>(result);
514 }
515
JSRuntimeHasProperty(JSValue * object,EtsString * name)516 uint8_t JSRuntimeHasProperty(JSValue *object, EtsString *name)
517 {
518 auto coro = EtsCoroutine::GetCurrent();
519 auto ctx = InteropCtx::Current(coro);
520 INTEROP_CODE_SCOPE_ETS(coro);
521 auto env = ctx->GetJSEnv();
522 NapiScope jsHandleScope(env);
523
524 bool result = false;
525 napi_status jsStatus;
526 {
527 ScopedNativeCodeThread nativeScope(coro);
528 jsStatus = napi_has_property(env, JSConvertJSValue::WrapWithNullCheck(env, object),
529 JSConvertString::WrapWithNullCheck(env, name), &result);
530 }
531 CHECK_NAPI_STATUS(jsStatus, ctx, coro, result);
532 return static_cast<uint8_t>(result);
533 }
534
JSRuntimeGetProperty(JSValue * object,JSValue * property)535 JSValue *JSRuntimeGetProperty(JSValue *object, JSValue *property)
536 {
537 auto coro = EtsCoroutine::GetCurrent();
538 auto ctx = InteropCtx::Current(coro);
539 INTEROP_CODE_SCOPE_ETS(coro);
540 auto env = ctx->GetJSEnv();
541 NapiScope jsHandleScope(env);
542
543 auto jsThis = JSConvertJSValue::WrapWithNullCheck(env, object);
544 auto key = JSConvertJSValue::WrapWithNullCheck(env, property);
545
546 napi_value result;
547 {
548 ScopedNativeCodeThread nativeScope(coro);
549 NAPI_CHECK_FATAL(napi_get_property(env, jsThis, key, &result));
550 }
551 return JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result).value();
552 }
553
JSRuntimeHasPropertyJSValue(JSValue * object,JSValue * property)554 uint8_t JSRuntimeHasPropertyJSValue(JSValue *object, JSValue *property)
555 {
556 auto coro = EtsCoroutine::GetCurrent();
557 auto ctx = InteropCtx::Current(coro);
558 INTEROP_CODE_SCOPE_ETS(coro);
559 auto env = ctx->GetJSEnv();
560 NapiScope jsHandleScope(env);
561
562 bool result = false;
563 napi_status jsStatus;
564 {
565 ScopedNativeCodeThread nativeScope(coro);
566 jsStatus = napi_has_property(env, JSConvertJSValue::WrapWithNullCheck(env, object),
567 JSConvertJSValue::WrapWithNullCheck(env, property), &result);
568 }
569 CHECK_NAPI_STATUS(jsStatus, ctx, coro, result);
570 return static_cast<uint8_t>(result);
571 }
572
JSRuntimeHasElement(JSValue * object,int index)573 uint8_t JSRuntimeHasElement(JSValue *object, int index)
574 {
575 auto coro = EtsCoroutine::GetCurrent();
576 auto ctx = InteropCtx::Current(coro);
577 INTEROP_CODE_SCOPE_ETS(coro);
578 auto env = ctx->GetJSEnv();
579 NapiScope jsHandleScope(env);
580
581 bool result = false;
582 napi_status jsStatus;
583 {
584 ScopedNativeCodeThread nativeScope(coro);
585 jsStatus = napi_has_element(env, JSConvertJSValue::WrapWithNullCheck(env, object), index, &result);
586 }
587 CHECK_NAPI_STATUS(jsStatus, ctx, coro, result);
588 return static_cast<uint8_t>(result);
589 }
590
JSRuntimeHasOwnProperty(JSValue * object,EtsString * name)591 uint8_t JSRuntimeHasOwnProperty(JSValue *object, EtsString *name)
592 {
593 auto coro = EtsCoroutine::GetCurrent();
594 auto ctx = InteropCtx::Current(coro);
595 INTEROP_CODE_SCOPE_ETS(coro);
596 auto env = ctx->GetJSEnv();
597 NapiScope jsHandleScope(env);
598
599 bool result = false;
600 napi_status jsStatus;
601 {
602 ScopedNativeCodeThread nativeScope(coro);
603 jsStatus = napi_has_own_property(env, JSConvertJSValue::WrapWithNullCheck(env, object),
604 JSConvertString::WrapWithNullCheck(env, name), &result);
605 }
606 CHECK_NAPI_STATUS(jsStatus, ctx, coro, result);
607 return static_cast<uint8_t>(result);
608 }
609
JSRuntimeHasOwnPropertyJSValue(JSValue * object,JSValue * property)610 uint8_t JSRuntimeHasOwnPropertyJSValue(JSValue *object, JSValue *property)
611 {
612 auto coro = EtsCoroutine::GetCurrent();
613 auto ctx = InteropCtx::Current(coro);
614 INTEROP_CODE_SCOPE_ETS(coro);
615 auto env = ctx->GetJSEnv();
616 NapiScope jsHandleScope(env);
617
618 bool result = false;
619 napi_status jsStatus;
620 {
621 ScopedNativeCodeThread nativeScope(coro);
622 jsStatus = napi_has_own_property(env, JSConvertJSValue::WrapWithNullCheck(env, object),
623 JSConvertJSValue::WrapWithNullCheck(env, property), &result);
624 }
625 CHECK_NAPI_STATUS(jsStatus, ctx, coro, result);
626 return static_cast<uint8_t>(result);
627 }
628
JSRuntimeTypeOf(JSValue * object)629 EtsString *JSRuntimeTypeOf(JSValue *object)
630 {
631 if (object == nullptr) {
632 return nullptr;
633 }
634 switch (object->GetType()) {
635 case napi_undefined:
636 return EtsString::CreateFromMUtf8("undefined");
637 case napi_null:
638 return EtsString::CreateFromMUtf8("object");
639 case napi_boolean:
640 return EtsString::CreateFromMUtf8("boolean");
641 case napi_number:
642 return EtsString::CreateFromMUtf8("number");
643 case napi_string:
644 return EtsString::CreateFromMUtf8("string");
645 case napi_symbol:
646 return EtsString::CreateFromMUtf8("object");
647 case napi_object: {
648 auto coro = EtsCoroutine::GetCurrent();
649 auto ctx = InteropCtx::Current(coro);
650 INTEROP_CODE_SCOPE_ETS(coro);
651 auto env = ctx->GetJSEnv();
652 NapiScope jsHandleScope(env);
653 napi_value jsValue = JSConvertJSValue::Wrap(env, object);
654 // (note: need to use JS_TYPE, #ICIFWJ)
655 if (IsConstructor(env, jsValue, "Number")) {
656 return EtsString::CreateFromMUtf8("number");
657 }
658 if (IsConstructor(env, jsValue, "Boolean")) {
659 return EtsString::CreateFromMUtf8("boolean");
660 }
661 if (IsConstructor(env, jsValue, "String")) {
662 return EtsString::CreateFromMUtf8("string");
663 }
664 return EtsString::CreateFromMUtf8("object");
665 }
666 case napi_function:
667 return EtsString::CreateFromMUtf8("function");
668 case napi_external:
669 return EtsString::CreateFromMUtf8("external");
670 case napi_bigint:
671 return EtsString::CreateFromMUtf8("bigint");
672 default:
673 UNREACHABLE();
674 }
675 }
676
JSRuntimeInvoke(JSValue * recv,JSValue * func,EtsArray * args)677 JSValue *JSRuntimeInvoke(JSValue *recv, JSValue *func, EtsArray *args)
678 {
679 auto coro = EtsCoroutine::GetCurrent();
680 auto ctx = InteropCtx::Current(coro);
681 INTEROP_CODE_SCOPE_ETS(coro);
682 auto env = ctx->GetJSEnv();
683 NapiScope jsHandleScope(env);
684
685 PandaVector<napi_value> realArgs;
686
687 for (size_t i = 0; i < args->GetLength(); i++) {
688 auto objHeader = args->GetCoreType()->Get<ObjectHeader *>(i);
689 if (objHeader == nullptr) {
690 break;
691 }
692
693 auto t = helpers::TypeIdentity<JSConvertJSValue>();
694 using Convertor = typename decltype(t)::type;
695 using Cpptype = typename Convertor::cpptype;
696 Cpptype value = std::remove_pointer_t<Cpptype>::FromEtsObject(EtsObject::FromCoreType(objHeader));
697 realArgs.push_back(Convertor::Wrap(env, value));
698 }
699
700 napi_value retVal;
701 napi_status jsStatus;
702 auto recvEtsObject = JSConvertJSValue::WrapWithNullCheck(env, recv);
703 auto funcEtsObject = JSConvertJSValue::WrapWithNullCheck(env, func);
704 {
705 ScopedNativeCodeThread nativeScope(coro);
706 jsStatus = napi_call_function(env, recvEtsObject, funcEtsObject, realArgs.size(), realArgs.data(), &retVal);
707 }
708
709 if (jsStatus != napi_ok) {
710 ctx->ForwardJSException(coro);
711 return nullptr;
712 }
713 return JSConvertJSValue::UnwrapWithNullCheck(ctx, env, retVal).value();
714 }
715
JSRuntimeInstantiate(JSValue * callable,EtsArray * args)716 JSValue *JSRuntimeInstantiate(JSValue *callable, EtsArray *args)
717 {
718 auto coro = EtsCoroutine::GetCurrent();
719 auto ctx = InteropCtx::Current(coro);
720 INTEROP_CODE_SCOPE_ETS(coro);
721 auto env = ctx->GetJSEnv();
722 NapiScope jsHandleScope(env);
723
724 PandaVector<napi_value> realArgs;
725
726 for (size_t i = 0; i < args->GetLength(); i++) {
727 auto objHeader = args->GetCoreType()->Get<ObjectHeader *>(i);
728 if (objHeader == nullptr) {
729 break;
730 }
731
732 auto t = helpers::TypeIdentity<JSConvertJSValue>();
733 using Convertor = typename decltype(t)::type;
734 using Cpptype = typename Convertor::cpptype;
735 Cpptype value = std::remove_pointer_t<Cpptype>::FromEtsObject(EtsObject::FromCoreType(objHeader));
736 realArgs.push_back(Convertor::Wrap(env, value));
737 }
738
739 auto ctor = JSConvertJSValue::WrapWithNullCheck(env, callable);
740 napi_status jsStatus;
741 napi_value retVal;
742 {
743 ScopedNativeCodeThread nativeScope(coro);
744 jsStatus = napi_new_instance(env, ctor, realArgs.size(), realArgs.data(), &retVal);
745 }
746
747 if (jsStatus != napi_ok) {
748 ctx->ForwardJSException(coro);
749 return nullptr;
750 }
751 return JSConvertJSValue::UnwrapWithNullCheck(ctx, env, retVal).value();
752 }
753
JSRuntimeIsPromise(JSValue * object)754 uint8_t JSRuntimeIsPromise(JSValue *object)
755 {
756 auto coro = EtsCoroutine::GetCurrent();
757 auto ctx = InteropCtx::Current(coro);
758 INTEROP_CODE_SCOPE_ETS(coro);
759 auto env = ctx->GetJSEnv();
760 NapiScope jsHandleScope(env);
761
762 bool result = false;
763 NAPI_CHECK_FATAL(napi_is_promise(env, JSConvertJSValue::WrapWithNullCheck(env, object), &result));
764 return static_cast<uint8_t>(result);
765 }
766
JSRuntimeInstanceOfStaticType(JSValue * object,EtsTypeAPIType * paramType)767 uint8_t JSRuntimeInstanceOfStaticType(JSValue *object, EtsTypeAPIType *paramType)
768 {
769 auto coro = EtsCoroutine::GetCurrent();
770 auto ctx = InteropCtx::Current(coro);
771 INTEROP_CODE_SCOPE_ETS(coro);
772 auto env = ctx->GetJSEnv();
773 NapiScope jsHandleScope(env);
774 EtsHandleScope scope(coro);
775
776 if (object == nullptr || paramType == nullptr) {
777 return static_cast<uint8_t>(false);
778 }
779
780 EtsHandle<EtsClass> klass(coro, paramType->GetClass());
781 EtsField *field = klass->GetFieldIDByName("cls", nullptr);
782 auto clsObj = paramType->GetFieldObject(field);
783 if (clsObj == nullptr) {
784 INTEROP_LOG(ERROR) << "failed to get field by name";
785 return static_cast<uint8_t>(false);
786 }
787 auto cls = reinterpret_cast<EtsClass *>(clsObj);
788 napi_value jsValue = JSConvertJSValue::Wrap(env, object);
789 if (jsValue == nullptr) {
790 return static_cast<uint8_t>(false);
791 }
792 auto etsObjectRes = JSConvertEtsObject::UnwrapImpl(ctx, env, jsValue);
793 if (!etsObjectRes.has_value()) {
794 return static_cast<uint8_t>(false);
795 }
796 EtsObject *etsObject = etsObjectRes.value();
797 bool result = cls->IsAssignableFrom(etsObject->GetClass());
798 return static_cast<uint8_t>(result);
799 }
800
JSValueToString(JSValue * object)801 EtsString *JSValueToString(JSValue *object)
802 {
803 ASSERT(object != nullptr);
804
805 auto coro = EtsCoroutine::GetCurrent();
806 auto ctx = InteropCtx::Current(coro);
807 if (ctx == nullptr) {
808 ThrowNoInteropContextException();
809 return nullptr;
810 }
811 napi_env env = ctx->GetJSEnv();
812
813 NapiScope jsHandleScope(env);
814
815 napi_value strObj;
816 [[maybe_unused]] napi_status status;
817 {
818 auto napiValue = JSConvertJSValue::Wrap(env, object);
819
820 ScopedNativeCodeThread nativeScope(coro); // NOTE: Scope(Native/Managed)CodeThread should be optional here
821 status = napi_coerce_to_string(env, napiValue, &strObj);
822 }
823
824 #if defined(PANDA_TARGET_OHOS) || defined(PANDA_JS_ETS_HYBRID_MODE)
825 // #22503 napi_coerce_to_string in arkui/napi implementation returns napi_ok in case of exception.
826 if (UNLIKELY(NapiIsExceptionPending(env))) {
827 ctx->ForwardJSException(coro);
828 return nullptr;
829 }
830 #else
831 if (UNLIKELY(status != napi_ok)) {
832 INTEROP_FATAL_IF(status != napi_string_expected && status != napi_pending_exception);
833 ctx->ForwardJSException(coro);
834 return nullptr;
835 }
836 #endif
837
838 auto res = JSConvertString::UnwrapWithNullCheck(ctx, env, strObj);
839 if (UNLIKELY(!res.has_value())) {
840 if (NapiIsExceptionPending(env)) {
841 ctx->ForwardJSException(coro);
842 }
843 ASSERT(ctx->SanityETSExceptionPending());
844 return nullptr;
845 }
846 return res.value();
847 }
848
ToLocal(void * value)849 napi_value ToLocal(void *value)
850 {
851 return reinterpret_cast<napi_value>(value);
852 }
853
CompilerGetJSNamedProperty(void * val,char * propStr)854 void *CompilerGetJSNamedProperty(void *val, char *propStr)
855 {
856 auto coro = EtsCoroutine::GetCurrent();
857 auto ctx = InteropCtx::Current(coro);
858 if (ctx == nullptr) {
859 ThrowNoInteropContextException();
860 return nullptr;
861 }
862 INTEROP_CODE_SCOPE_ETS(coro);
863 napi_env env = ctx->GetJSEnv();
864 auto jsVal = ToLocal(val);
865 napi_value res;
866 napi_status rc = napi_get_named_property(env, jsVal, propStr, &res);
867 if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
868 ctx->ForwardJSException(coro);
869 ASSERT(NapiIsExceptionPending(env));
870 return nullptr;
871 }
872 return res;
873 }
874
CompilerGetJSProperty(void * val,void * prop)875 void *CompilerGetJSProperty(void *val, void *prop)
876 {
877 auto coro = EtsCoroutine::GetCurrent();
878 auto ctx = InteropCtx::Current(coro);
879 if (ctx == nullptr) {
880 ThrowNoInteropContextException();
881 return nullptr;
882 }
883 INTEROP_CODE_SCOPE_ETS(coro);
884 napi_env env = ctx->GetJSEnv();
885 auto jsVal = ToLocal(val);
886 napi_value res;
887 napi_status rc = napi_get_property(env, jsVal, ToLocal(prop), &res);
888 if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
889 ctx->ForwardJSException(coro);
890 ASSERT(NapiIsExceptionPending(env));
891 return nullptr;
892 }
893 return res;
894 }
895
CompilerGetJSElement(void * val,int32_t index)896 void *CompilerGetJSElement(void *val, int32_t index)
897 {
898 auto coro = EtsCoroutine::GetCurrent();
899 auto ctx = InteropCtx::Current(coro);
900 if (ctx == nullptr) {
901 ThrowNoInteropContextException();
902 return nullptr;
903 }
904 auto env = ctx->GetJSEnv();
905
906 napi_value res;
907 auto rc = napi_get_element(env, ToLocal(val), index, &res);
908 if (UNLIKELY(NapiThrownGeneric(rc))) {
909 ctx->ForwardJSException(coro);
910 return nullptr;
911 }
912 return res;
913 }
914
CompilerJSCallCheck(void * fn)915 void *CompilerJSCallCheck(void *fn)
916 {
917 auto jsFn = ToLocal(fn);
918
919 auto coro = EtsCoroutine::GetCurrent();
920 auto ctx = InteropCtx::Current(coro);
921 if (ctx == nullptr) {
922 ThrowNoInteropContextException();
923 return nullptr;
924 }
925 INTEROP_CODE_SCOPE_ETS(coro);
926 napi_env env = ctx->GetJSEnv();
927 if (UNLIKELY(GetValueType(env, jsFn) != napi_function)) {
928 ctx->ThrowJSTypeError(env, "call target is not a function");
929 ctx->ForwardJSException(coro);
930 return nullptr;
931 }
932 return fn;
933 }
934
CompilerJSNewInstance(void * fn,uint32_t argc,void * args)935 void *CompilerJSNewInstance(void *fn, uint32_t argc, void *args)
936 {
937 auto jsFn = ToLocal(fn);
938 auto jsArgs = reinterpret_cast<napi_value *>(args);
939
940 auto coro = EtsCoroutine::GetCurrent();
941 auto ctx = InteropCtx::Current(coro);
942 if (ctx == nullptr) {
943 ThrowNoInteropContextException();
944 return nullptr;
945 }
946 INTEROP_CODE_SCOPE_ETS(coro);
947 napi_env env = ctx->GetJSEnv();
948
949 napi_value jsRet;
950 napi_status jsStatus;
951 {
952 ScopedNativeCodeThread nativeScope(coro);
953 jsStatus = napi_new_instance(env, jsFn, argc, jsArgs, &jsRet);
954 }
955
956 if (UNLIKELY(jsStatus != napi_ok)) {
957 INTEROP_FATAL_IF(jsStatus != napi_pending_exception);
958 ctx->ForwardJSException(coro);
959 INTEROP_LOG(DEBUG) << "JSValueJSCall: exit with pending exception";
960 return nullptr;
961 }
962 return jsRet;
963 }
964
CreateLocalScope()965 void CreateLocalScope()
966 {
967 auto coro = EtsCoroutine::GetCurrent();
968 auto ctx = InteropCtx::Current(coro);
969 if (ctx == nullptr) {
970 ThrowNoInteropContextException();
971 return;
972 }
973 napi_env env = ctx->GetJSEnv();
974 auto scopesStorage = ctx->GetLocalScopesStorage();
975 ASSERT(coro->IsCurrentFrameCompiled());
976 scopesStorage->CreateLocalScope(env, coro->GetCurrentFrame());
977 }
978
CompilerDestroyLocalScope()979 void CompilerDestroyLocalScope()
980 {
981 auto coro = EtsCoroutine::GetCurrent();
982 auto ctx = InteropCtx::Current(coro);
983 if (ctx == nullptr) {
984 ThrowNoInteropContextException();
985 return;
986 }
987 napi_env env = ctx->GetJSEnv();
988 auto scopesStorage = ctx->GetLocalScopesStorage();
989 ASSERT(coro->IsCurrentFrameCompiled());
990 scopesStorage->DestroyTopLocalScope(env, coro->GetCurrentFrame());
991 }
992
CompilerLoadJSConstantPool()993 void *CompilerLoadJSConstantPool()
994 {
995 auto coro = EtsCoroutine::GetCurrent();
996 auto ctx = InteropCtx::Current(coro);
997 if (ctx == nullptr) {
998 ThrowNoInteropContextException();
999 return nullptr;
1000 }
1001 return ctx->GetConstStringStorage()->GetConstantPool();
1002 }
1003
CompilerInitJSCallClassForCtx(void * klassPtr)1004 void CompilerInitJSCallClassForCtx(void *klassPtr)
1005 {
1006 auto coro = EtsCoroutine::GetCurrent();
1007 auto ctx = InteropCtx::Current(coro);
1008 if (ctx == nullptr) {
1009 ThrowNoInteropContextException();
1010 return;
1011 }
1012 auto klass = reinterpret_cast<Class *>(klassPtr);
1013 ctx->GetConstStringStorage()->LoadDynamicCallClass(klass);
1014 }
1015
CompilerConvertVoidToLocal()1016 void *CompilerConvertVoidToLocal()
1017 {
1018 auto coro = EtsCoroutine::GetCurrent();
1019 auto ctx = InteropCtx::Current(coro);
1020 if (ctx == nullptr) {
1021 ThrowNoInteropContextException();
1022 return nullptr;
1023 }
1024 napi_env env = ctx->GetJSEnv();
1025 return GetUndefined(env);
1026 }
1027
CompilerConvertRefTypeToLocal(EtsObject * etsValue)1028 void *CompilerConvertRefTypeToLocal(EtsObject *etsValue)
1029 {
1030 auto coro = EtsCoroutine::GetCurrent();
1031 auto ctx = InteropCtx::Current(coro);
1032 if (ctx == nullptr) {
1033 ThrowNoInteropContextException();
1034 return nullptr;
1035 }
1036 INTEROP_CODE_SCOPE_ETS(coro);
1037 napi_env env = ctx->GetJSEnv();
1038
1039 auto ref = etsValue->GetCoreType();
1040 if (UNLIKELY(ref == nullptr)) {
1041 return GetNull(env);
1042 }
1043
1044 auto klass = ref->template ClassAddr<Class>();
1045 // start fastpath
1046 if (klass == ctx->GetJSValueClass()) {
1047 auto value = JSValue::FromEtsObject(EtsObject::FromCoreType(ref));
1048 napi_value res = JSConvertJSValue::Wrap(env, value);
1049 if (UNLIKELY(res == nullptr)) {
1050 ctx->ForwardJSException(coro);
1051 }
1052 return res;
1053 }
1054 if (klass == ctx->GetStringClass()) {
1055 auto value = EtsString::FromEtsObject(EtsObject::FromCoreType(ref));
1056 napi_value res = JSConvertString::Wrap(env, value);
1057 if (UNLIKELY(res == nullptr)) {
1058 ctx->ForwardJSException(coro);
1059 }
1060 return res;
1061 }
1062 // start slowpath
1063 HandleScope<ObjectHeader *> scope(coro);
1064 VMHandle<ObjectHeader> handle(coro, ref);
1065 auto refconv = JSRefConvertResolve(ctx, klass);
1066 auto res = refconv->Wrap(ctx, EtsObject::FromCoreType(handle.GetPtr()));
1067 if (UNLIKELY(res == nullptr)) {
1068 ctx->ForwardJSException(coro);
1069 }
1070 return res;
1071 }
1072
CompilerConvertLocalToString(void * value)1073 EtsString *CompilerConvertLocalToString(void *value)
1074 {
1075 auto jsVal = ToLocal(value);
1076
1077 auto coro = EtsCoroutine::GetCurrent();
1078 auto ctx = InteropCtx::Current(coro);
1079 if (ctx == nullptr) {
1080 ThrowNoInteropContextException();
1081 return nullptr;
1082 }
1083 napi_env env = ctx->GetJSEnv();
1084 // NOTE(kprokopenko): can't assign undefined to EtsString *, see #14765
1085 if (UNLIKELY(IsNullOrUndefined(env, jsVal))) {
1086 return nullptr;
1087 }
1088
1089 auto res = JSConvertString::Unwrap(ctx, env, jsVal);
1090 if (UNLIKELY(!res.has_value())) {
1091 if (NapiIsExceptionPending(env)) {
1092 ctx->ForwardJSException(coro);
1093 }
1094 ASSERT(ctx->SanityETSExceptionPending());
1095 return nullptr;
1096 }
1097 return res.value();
1098 }
1099
CompilerConvertLocalToRefType(void * klassPtr,void * value)1100 EtsObject *CompilerConvertLocalToRefType(void *klassPtr, void *value)
1101 {
1102 auto jsVal = ToLocal(value);
1103 auto klass = reinterpret_cast<Class *>(klassPtr);
1104
1105 auto coro = EtsCoroutine::GetCurrent();
1106 auto ctx = InteropCtx::Current(coro);
1107 if (ctx == nullptr) {
1108 ThrowNoInteropContextException();
1109 return nullptr;
1110 }
1111 INTEROP_CODE_SCOPE_ETS(coro);
1112 napi_env env = ctx->GetJSEnv();
1113 if (UNLIKELY(IsNull(env, jsVal))) {
1114 return ctx->GetNullValue();
1115 }
1116 if (UNLIKELY(IsUndefined(env, jsVal))) {
1117 return nullptr;
1118 }
1119
1120 // start slowpath
1121 auto refconv = JSRefConvertResolve<true>(ctx, klass);
1122 if (UNLIKELY(refconv == nullptr)) {
1123 if (NapiIsExceptionPending(env)) {
1124 ctx->ForwardJSException(coro);
1125 }
1126 ASSERT(ctx->SanityETSExceptionPending());
1127 return nullptr;
1128 }
1129 ObjectHeader *res = refconv->Unwrap(ctx, jsVal)->GetCoreType();
1130 if (UNLIKELY(res == nullptr)) {
1131 if (NapiIsExceptionPending(env)) {
1132 ctx->ForwardJSException(coro);
1133 }
1134 ASSERT(ctx->SanityETSExceptionPending());
1135 return nullptr;
1136 }
1137 return EtsObject::FromCoreType(res);
1138 }
1139
JSONStringify(JSValue * jsvalue)1140 EtsString *JSONStringify(JSValue *jsvalue)
1141 {
1142 ASSERT(jsvalue != nullptr);
1143
1144 auto coro = EtsCoroutine::GetCurrent();
1145 auto ctx = InteropCtx::Current(coro);
1146 if (ctx == nullptr) {
1147 ThrowNoInteropContextException();
1148 return nullptr;
1149 }
1150 auto env = ctx->GetJSEnv();
1151 NapiScope jsHandleScope(env);
1152
1153 auto global = GetGlobal(env);
1154
1155 napi_value jsonInstance;
1156 NAPI_CHECK_FATAL(napi_get_named_property(env, global, "JSON", &jsonInstance));
1157
1158 napi_value stringify;
1159 NAPI_CHECK_FATAL(napi_get_named_property(env, jsonInstance, "stringify", &stringify));
1160
1161 auto object = JSConvertJSValue::Wrap(env, jsvalue);
1162 std::array<napi_value, 1> args = {object};
1163
1164 napi_value result;
1165 napi_status jsStatus;
1166 {
1167 ScopedNativeCodeThread nativeScope(coro);
1168 jsStatus = napi_call_function(env, jsonInstance, stringify, args.size(), args.data(), &result);
1169 }
1170
1171 if (UNLIKELY(jsStatus != napi_ok)) {
1172 INTEROP_FATAL_IF(jsStatus != napi_pending_exception);
1173 ctx->ForwardJSException(coro);
1174 return nullptr;
1175 }
1176 if (IsUndefined(env, result)) {
1177 return EtsString::CreateFromMUtf8("undefined");
1178 }
1179 auto res = JSConvertString::Unwrap(ctx, env, result);
1180 return res.value_or(nullptr);
1181 }
1182
SettleJsPromise(EtsObject * value,napi_deferred deferred,EtsInt state)1183 void SettleJsPromise(EtsObject *value, napi_deferred deferred, EtsInt state)
1184 {
1185 auto *coro = EtsCoroutine::GetCurrent();
1186 ASSERT(coro->GetCoroutineManager()->IsMainWorker(coro));
1187 auto *ctx = InteropCtx::Current(coro);
1188 if (ctx == nullptr) {
1189 ThrowNoInteropContextException();
1190 return;
1191 }
1192 INTEROP_CODE_SCOPE_ETS(coro);
1193 napi_env env = ctx->GetJSEnv();
1194 napi_value completionValue;
1195
1196 NapiScope napiScope(env);
1197 if (value == nullptr) {
1198 completionValue = GetUndefined(env);
1199 } else {
1200 auto refconv = JSRefConvertResolve(ctx, value->GetClass()->GetRuntimeClass());
1201 completionValue = refconv->Wrap(ctx, value);
1202 }
1203 ScopedNativeCodeThread nativeScope(coro);
1204 napi_status status = state == EtsPromise::STATE_RESOLVED ? napi_resolve_deferred(env, deferred, completionValue)
1205 : napi_reject_deferred(env, deferred, completionValue);
1206 if (status != napi_ok) {
1207 napi_throw_error(env, nullptr, "Cannot resolve promise");
1208 }
1209 }
1210
PromiseInteropResolve(EtsObject * value,EtsLong deferred)1211 void PromiseInteropResolve(EtsObject *value, EtsLong deferred)
1212 {
1213 SettleJsPromise(value, reinterpret_cast<napi_deferred>(deferred), EtsPromise::STATE_RESOLVED);
1214 }
1215
PromiseInteropReject(EtsObject * value,EtsLong deferred)1216 void PromiseInteropReject(EtsObject *value, EtsLong deferred)
1217 {
1218 SettleJsPromise(value, reinterpret_cast<napi_deferred>(deferred), EtsPromise::STATE_REJECTED);
1219 }
1220
JSRuntimeGetPropertyJSValueyByKey(JSValue * objectValue,JSValue * keyValue)1221 JSValue *JSRuntimeGetPropertyJSValueyByKey(JSValue *objectValue, JSValue *keyValue)
1222 {
1223 ASSERT(objectValue != nullptr);
1224 ASSERT(keyValue != nullptr);
1225
1226 auto coro = EtsCoroutine::GetCurrent();
1227 auto ctx = InteropCtx::Current(coro);
1228 auto env = ctx->GetJSEnv();
1229
1230 NapiScope jsHandleScope(env);
1231
1232 napi_value objectNapiValue = JSConvertJSValue::Wrap(env, objectValue);
1233 napi_value keyNapiValue = JSConvertJSValue::Wrap(env, keyValue);
1234
1235 napi_value result;
1236 auto status = napi_get_property(env, objectNapiValue, keyNapiValue, &result);
1237 if (UNLIKELY(status == napi_object_expected || NapiThrownGeneric(status))) {
1238 if (NapiIsExceptionPending(env)) {
1239 ctx->ForwardJSException(coro);
1240 }
1241 ASSERT(ctx->SanityETSExceptionPending());
1242 return nullptr;
1243 }
1244 INTEROP_FATAL_IF(status != napi_ok);
1245
1246 auto res = JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result);
1247 if (UNLIKELY(!res)) {
1248 if (NapiIsExceptionPending(env)) {
1249 ctx->ForwardJSException(coro);
1250 }
1251 ASSERT(ctx->SanityETSExceptionPending());
1252 return nullptr;
1253 }
1254
1255 return res.value();
1256 }
1257
TransferArrayBufferToStatic(ESValue * object)1258 EtsEscompatArrayBuffer *TransferArrayBufferToStatic(ESValue *object)
1259 {
1260 auto coro = EtsCoroutine::GetCurrent();
1261 auto ctx = InteropCtx::Current(coro);
1262 if (ctx == nullptr) {
1263 ThrowNoInteropContextException();
1264 return nullptr;
1265 }
1266 INTEROP_CODE_SCOPE_ETS(coro);
1267 auto env = ctx->GetJSEnv();
1268 NapiScope jsHandleScope(env);
1269
1270 napi_value dynamicArrayBuffer = object->GetEo()->GetNapiValue(ctx->GetJSEnv());
1271
1272 bool isArrayBuffer = false;
1273 NAPI_CHECK_FATAL(napi_is_arraybuffer(env, dynamicArrayBuffer, &isArrayBuffer));
1274 if (!isArrayBuffer) {
1275 ctx->ThrowETSError(coro, "Dynamic object is not arraybuffer");
1276 }
1277
1278 void *data = nullptr;
1279 size_t byteLength = 0;
1280 // NOTE(dslynko, #23919): finalize semantics of resizable ArrayBuffers
1281 NAPI_CHECK_FATAL(napi_get_arraybuffer_info(env, dynamicArrayBuffer, &data, &byteLength));
1282
1283 [[maybe_unused]] EtsHandleScope s(coro);
1284 void *etsData = nullptr;
1285 auto *arrayBuffer = EtsEscompatArrayBuffer::Create(coro, byteLength, &etsData);
1286 std::copy_n(reinterpret_cast<uint8_t *>(data), byteLength, reinterpret_cast<uint8_t *>(etsData));
1287 return arrayBuffer;
1288 }
1289
TransferArrayBufferToDynamic(EtsEscompatArrayBuffer * staticArrayBuffer)1290 EtsObject *TransferArrayBufferToDynamic(EtsEscompatArrayBuffer *staticArrayBuffer)
1291 {
1292 auto coro = EtsCoroutine::GetCurrent();
1293 auto ctx = InteropCtx::Current(coro);
1294 if (ctx == nullptr) {
1295 ThrowNoInteropContextException();
1296 return {};
1297 }
1298 INTEROP_CODE_SCOPE_ETS(coro);
1299 auto env = ctx->GetJSEnv();
1300 NapiScope jsHandleScope(env);
1301
1302 napi_value dynamicArrayBuffer = nullptr;
1303 void *data;
1304 // NOTE(dslynko, #23919): finalize semantics of resizable ArrayBuffers
1305 NAPI_CHECK_FATAL(napi_create_arraybuffer(env, staticArrayBuffer->GetByteLength(), &data, &dynamicArrayBuffer));
1306 std::copy_n(reinterpret_cast<const uint8_t *>(staticArrayBuffer->GetData()), staticArrayBuffer->GetByteLength(),
1307 reinterpret_cast<uint8_t *>(data));
1308
1309 JSValue *etsJSValue = JSValue::Create(coro, ctx, dynamicArrayBuffer);
1310 return reinterpret_cast<EtsObject *>(etsJSValue);
1311 }
1312
CreateDynamicTypedArray(EtsEscompatArrayBuffer * staticArrayBuffer,int32_t typedArrayType,double length,double byteOffset)1313 EtsObject *CreateDynamicTypedArray(EtsEscompatArrayBuffer *staticArrayBuffer, int32_t typedArrayType, double length,
1314 double byteOffset)
1315 {
1316 auto coro = EtsCoroutine::GetCurrent();
1317 auto ctx = InteropCtx::Current(coro);
1318 if (ctx == nullptr) {
1319 ThrowNoInteropContextException();
1320 return {};
1321 }
1322 INTEROP_CODE_SCOPE_ETS(coro);
1323 auto env = ctx->GetJSEnv();
1324 NapiScope jsHandleScope(env);
1325
1326 napi_value dynamicArrayBuffer = nullptr;
1327 void *data;
1328 NAPI_CHECK_FATAL(napi_create_arraybuffer(env, staticArrayBuffer->GetByteLength(), &data, &dynamicArrayBuffer));
1329 std::copy_n(reinterpret_cast<const uint8_t *>(staticArrayBuffer->GetData()), staticArrayBuffer->GetByteLength(),
1330 reinterpret_cast<uint8_t *>(data));
1331
1332 napi_value dynamicTypedArray = nullptr;
1333 napi_create_typedarray(env, static_cast<napi_typedarray_type>(typedArrayType), length, dynamicArrayBuffer,
1334 byteOffset, &dynamicTypedArray);
1335
1336 JSValue *etsJSValue = JSValue::Create(coro, ctx, dynamicTypedArray);
1337 return reinterpret_cast<EtsObject *>(etsJSValue);
1338 }
1339
CreateDynamicDataView(EtsEscompatArrayBuffer * staticArrayBuffer,double byteLength,double byteOffset)1340 EtsObject *CreateDynamicDataView(EtsEscompatArrayBuffer *staticArrayBuffer, double byteLength, double byteOffset)
1341 {
1342 auto coro = EtsCoroutine::GetCurrent();
1343 auto ctx = InteropCtx::Current(coro);
1344 if (ctx == nullptr) {
1345 ThrowNoInteropContextException();
1346 return {};
1347 }
1348 INTEROP_CODE_SCOPE_ETS(coro);
1349 auto env = ctx->GetJSEnv();
1350 NapiScope jsHandleScope(env);
1351
1352 napi_value dynamicArrayBuffer = nullptr;
1353 void *data;
1354 NAPI_CHECK_FATAL(napi_create_arraybuffer(env, staticArrayBuffer->GetByteLength(), &data, &dynamicArrayBuffer));
1355 std::copy_n(reinterpret_cast<const uint8_t *>(staticArrayBuffer->GetData()), staticArrayBuffer->GetByteLength(),
1356 reinterpret_cast<uint8_t *>(data));
1357
1358 napi_value dynamicDataView = nullptr;
1359 napi_create_dataview(env, byteLength, dynamicArrayBuffer, byteOffset, &dynamicDataView);
1360
1361 JSValue *etsJSValue = JSValue::Create(coro, ctx, dynamicDataView);
1362 return reinterpret_cast<EtsObject *>(etsJSValue);
1363 }
1364
SetInteropRuntimeLinker(EtsRuntimeLinker * linker)1365 void SetInteropRuntimeLinker(EtsRuntimeLinker *linker)
1366 {
1367 auto coro = EtsCoroutine::GetCurrent();
1368 auto ctx = InteropCtx::Current(coro);
1369 if (ctx == nullptr) {
1370 ThrowNoInteropContextException();
1371 return;
1372 }
1373 INTEROP_CODE_SCOPE_ETS(coro);
1374
1375 ctx->SetDefaultLinkerContext(linker);
1376 }
1377
1378 } // namespace ark::ets::interop::js
1379