• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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