• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "plugins/ets/runtime/interop_js/call/call.h"
17 #include "plugins/ets/runtime/interop_js/js_convert.h"
18 #include "plugins/ets/runtime/interop_js/interop_common.h"
19 #include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h"
20 #include "plugins/ets/runtime/interop_js/code_scopes.h"
21 #include "plugins/ets/runtime/interop_js/logger.h"
22 #include "plugins/ets/runtime/types/ets_string.h"
23 #include "runtime/include/class_linker-inl.h"
24 #include "runtime/coroutines/stackful_coroutine.h"
25 
26 namespace ark::ets::interop::js {
27 
JSRuntimeFinalizationRegistryCallback(EtsObject * cbarg)28 void JSRuntimeFinalizationRegistryCallback(EtsObject *cbarg)
29 {
30     auto coro = EtsCoroutine::GetCurrent();
31     auto ctx = InteropCtx::Current(coro);
32     if (cbarg->GetClass()->GetRuntimeClass() == ctx->GetJSValueClass()) {
33         return JSValue::FinalizeETSWeak(ctx, cbarg);
34     }
35     return ets_proxy::SharedReference::FinalizeETSWeak(ctx, cbarg);
36 }
37 
JSRuntimeNewJSValueDouble(double v)38 JSValue *JSRuntimeNewJSValueDouble(double v)
39 {
40     auto coro = EtsCoroutine::GetCurrent();
41     auto ctx = InteropCtx::Current(coro);
42     return JSValue::CreateNumber(coro, ctx, v);
43 }
44 
JSRuntimeNewJSValueBoolean(uint8_t v)45 JSValue *JSRuntimeNewJSValueBoolean(uint8_t v)
46 {
47     auto coro = EtsCoroutine::GetCurrent();
48     auto ctx = InteropCtx::Current(coro);
49     return JSValue::CreateBoolean(coro, ctx, static_cast<bool>(v));
50 }
51 
JSRuntimeNewJSValueString(EtsString * v)52 JSValue *JSRuntimeNewJSValueString(EtsString *v)
53 {
54     auto coro = EtsCoroutine::GetCurrent();
55     auto ctx = InteropCtx::Current(coro);
56     std::string str;
57     if (v->IsUtf16()) {
58         InteropCtx::Fatal("not implemented");
59     } else {
60         str = std::string(utf::Mutf8AsCString(v->GetDataMUtf8()), v->GetLength());
61     }
62     return JSValue::CreateString(coro, ctx, std::move(str));
63 }
64 
JSRuntimeNewJSValueObject(EtsObject * v)65 JSValue *JSRuntimeNewJSValueObject(EtsObject *v)
66 {
67     if (v == nullptr) {
68         return nullptr;
69     }
70 
71     auto coro = EtsCoroutine::GetCurrent();
72     auto ctx = InteropCtx::Current(coro);
73     INTEROP_CODE_SCOPE_ETS(coro);
74     auto env = ctx->GetJSEnv();
75     NapiScope jsHandleScope(env);
76 
77     auto refconv = JSRefConvertResolve(ctx, v->GetClass()->GetRuntimeClass());
78     auto result = refconv->Wrap(ctx, v);
79 
80     auto res = JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result);
81     if (!res) {
82         ctx->ForwardJSException(coro);
83         return {};
84     }
85 
86     return res.value();
87 }
88 
JSRuntimeGetValueDouble(JSValue * etsJsValue)89 double JSRuntimeGetValueDouble(JSValue *etsJsValue)
90 {
91     return etsJsValue->GetNumber();
92 }
93 
JSRuntimeGetValueBoolean(JSValue * etsJsValue)94 uint8_t JSRuntimeGetValueBoolean(JSValue *etsJsValue)
95 {
96     return static_cast<uint8_t>(etsJsValue->GetBoolean());
97 }
98 
JSRuntimeGetValueString(JSValue * etsJsValue)99 EtsString *JSRuntimeGetValueString(JSValue *etsJsValue)
100 {
101     ASSERT(etsJsValue != nullptr);
102 
103     auto coro = EtsCoroutine::GetCurrent();
104     auto ctx = InteropCtx::Current(coro);
105     auto env = ctx->GetJSEnv();
106 
107     NapiScope jsHandleScope(env);
108 
109     napi_value jsVal = etsJsValue->GetNapiValue(env);
110     auto res = JSConvertString::Unwrap(ctx, env, jsVal);
111     if (UNLIKELY(!res)) {
112         if (NapiIsExceptionPending(env)) {
113             ctx->ForwardJSException(coro);
114         }
115         ASSERT(ctx->SanityETSExceptionPending());
116         return {};
117     }
118 
119     return res.value();
120 }
121 
JSRuntimeGetValueObject(JSValue * etsJsValue,EtsClass * clsObj)122 EtsObject *JSRuntimeGetValueObject(JSValue *etsJsValue, EtsClass *clsObj)
123 {
124     auto coro = EtsCoroutine::GetCurrent();
125     auto ctx = InteropCtx::Current(coro);
126     auto cls = clsObj->GetRuntimeClass();
127 
128     if (etsJsValue == nullptr) {
129         return nullptr;
130     }
131 
132     // NOTE(kprokopenko): awful solution, see #14765
133     if (etsJsValue->AsObject() == ctx->GetUndefinedObject()) {
134         return etsJsValue->AsObject();
135     }
136 
137     INTEROP_CODE_SCOPE_ETS(coro);
138     auto env = ctx->GetJSEnv();
139     NapiScope jsHandleScope(env);
140     napi_value jsVal = etsJsValue->GetNapiValue(env);
141 
142     auto refconv = JSRefConvertResolve<true>(ctx, cls);
143     if (UNLIKELY(refconv == nullptr)) {
144         if (NapiIsExceptionPending(env)) {
145             ctx->ForwardJSException(coro);
146         }
147         ASSERT(ctx->SanityETSExceptionPending());
148         return nullptr;
149     }
150 
151     return refconv->Unwrap(ctx, jsVal);
152 }
153 
JSRuntimeGetUndefined()154 JSValue *JSRuntimeGetUndefined()
155 {
156     auto coro = EtsCoroutine::GetCurrent();
157     auto ctx = InteropCtx::Current(coro);
158     return JSValue::CreateUndefined(coro, ctx);
159 }
160 
JSRuntimeGetNull()161 JSValue *JSRuntimeGetNull()
162 {
163     auto coro = EtsCoroutine::GetCurrent();
164     auto ctx = InteropCtx::Current(coro);
165     return JSValue::CreateNull(coro, ctx);
166 }
167 
JSRuntimeGetGlobal()168 JSValue *JSRuntimeGetGlobal()
169 {
170     auto coro = EtsCoroutine::GetCurrent();
171     auto ctx = InteropCtx::Current(coro);
172     INTEROP_CODE_SCOPE_ETS(coro);
173     auto env = ctx->GetJSEnv();
174     NapiScope jsHandleScope(env);
175 
176     return JSValue::CreateRefValue(coro, ctx, GetGlobal(env), napi_object);
177 }
178 
JSRuntimeCreateObject()179 JSValue *JSRuntimeCreateObject()
180 {
181     auto coro = EtsCoroutine::GetCurrent();
182     auto ctx = InteropCtx::Current(coro);
183     INTEROP_CODE_SCOPE_ETS(coro);
184     auto env = ctx->GetJSEnv();
185     NapiScope jsHandleScope(env);
186 
187     napi_value obj;
188     NAPI_CHECK_FATAL(napi_create_object(env, &obj));
189     return JSValue::CreateRefValue(coro, ctx, obj, napi_object);
190 }
191 
JSRuntimeInstanceOfDynamic(JSValue * object,JSValue * ctor)192 uint8_t JSRuntimeInstanceOfDynamic(JSValue *object, JSValue *ctor)
193 {
194     auto coro = EtsCoroutine::GetCurrent();
195     auto ctx = InteropCtx::Current(coro);
196     INTEROP_CODE_SCOPE_ETS(coro);
197     auto env = ctx->GetJSEnv();
198     NapiScope jsHandleScope(env);
199 
200     auto jsObj = object->GetNapiValue(env);
201     auto jsCtor = ctor->GetNapiValue(env);
202     bool res;
203     napi_status rc = napi_instanceof(env, jsObj, jsCtor, &res);
204     if (UNLIKELY(NapiThrownGeneric(rc))) {
205         ctx->ForwardJSException(coro);
206         return 0;
207     }
208     return static_cast<uint8_t>(res);
209 }
210 
JSRuntimeInstanceOfStatic(JSValue * etsJsValue,EtsClass * etsCls)211 uint8_t JSRuntimeInstanceOfStatic(JSValue *etsJsValue, EtsClass *etsCls)
212 {
213     ASSERT(etsCls != nullptr);
214 
215     if (etsJsValue == nullptr) {
216         return 0;
217     }
218 
219     auto coro = EtsCoroutine::GetCurrent();
220     auto ctx = InteropCtx::Current(coro);
221     auto env = ctx->GetJSEnv();
222 
223     Class *cls = etsCls->GetRuntimeClass();
224     if (!cls->IsInitialized() && !IsStdClass(cls)) {
225         // 'js_value' haven't been created from the uninitialized class
226         return 0;
227     }
228 
229     // Check if object has SharedReference
230     ets_proxy::SharedReference *sharedRef = [=] {
231         NapiScope jsHandleScope(env);
232         napi_value jsValue = etsJsValue->GetNapiValue(env);
233         return ctx->GetSharedRefStorage()->GetReference(env, jsValue);
234     }();
235 
236     if (sharedRef != nullptr) {
237         EtsObject *etsObject = sharedRef->GetEtsObject(ctx);
238         return static_cast<uint8_t>(etsCls->IsAssignableFrom(etsObject->GetClass()));
239     }
240 
241     if (IsStdClass(cls)) {
242         // NOTE(v.cherkashin): Add support compat types, #13577
243         INTEROP_LOG(FATAL) << __func__ << " doesn't support compat types";
244     }
245 
246     return 0;
247 }
248 
ResolveModuleName(std::string_view module)249 std::pair<std::string_view, std::string_view> ResolveModuleName(std::string_view module)
250 {
251     static const std::unordered_set<std::string_view> NATIVE_MODULE_LIST = {
252         "@system.app",  "@ohos.app",       "@system.router", "@system.curves",
253         "@ohos.curves", "@system.matrix4", "@ohos.matrix4"};
254 
255     constexpr std::string_view REQUIRE = "require";
256     constexpr std::string_view REQUIRE_NAPI = "requireNapi";
257     constexpr std::string_view REQUIRE_NATIVE_MODULE = "requireNativeModule";
258     constexpr std::string_view SYSTEM_PLUGIN_PREFIX = "@system.";
259     constexpr std::string_view OHOS_PLUGIN_PREFIX = "@ohos.";
260 
261     if (NATIVE_MODULE_LIST.count(module) != 0) {
262         return {module.substr(1), REQUIRE_NATIVE_MODULE};
263     }
264 
265     if (module.compare(0, SYSTEM_PLUGIN_PREFIX.size(), SYSTEM_PLUGIN_PREFIX) == 0) {
266         return {module.substr(SYSTEM_PLUGIN_PREFIX.size()), REQUIRE_NAPI};
267     }
268 
269     if (module.compare(0, OHOS_PLUGIN_PREFIX.size(), OHOS_PLUGIN_PREFIX) == 0) {
270         return {module.substr(OHOS_PLUGIN_PREFIX.size()), REQUIRE_NAPI};
271     }
272 
273     return {module, REQUIRE};
274 }
275 
JSRuntimeLoadModule(EtsString * module)276 JSValue *JSRuntimeLoadModule(EtsString *module)
277 {
278     auto coro = EtsCoroutine::GetCurrent();
279     auto ctx = InteropCtx::Current(coro);
280     INTEROP_CODE_SCOPE_ETS(coro);
281     auto env = ctx->GetJSEnv();
282 
283     PandaString moduleName = module->GetMutf8();
284     auto [mod, func] = ResolveModuleName(moduleName);
285 
286     napi_value modObj;
287     {
288         ScopedNativeCodeThread etsNativeScope(coro);
289         NapiScope jsHandleScope(env);
290 
291         napi_value requireFn;
292         NAPI_CHECK_FATAL(napi_get_named_property(env, GetGlobal(env), func.data(), &requireFn));
293 
294         INTEROP_FATAL_IF(GetValueType(env, requireFn) != napi_function);
295         {
296             napi_value jsName;
297             NAPI_CHECK_FATAL(napi_create_string_utf8(env, mod.data(), NAPI_AUTO_LENGTH, &jsName));
298             std::array<napi_value, 1> args = {jsName};
299             napi_value recv;
300             NAPI_CHECK_FATAL(napi_get_undefined(env, &recv));
301             auto status = (napi_call_function(env, recv, requireFn, args.size(), args.data(), &modObj));
302             if (status == napi_pending_exception) {
303                 napi_value exp;
304                 NAPI_CHECK_FATAL(napi_get_and_clear_last_exception(env, &exp));
305                 NAPI_CHECK_FATAL(napi_fatal_exception(env, exp));
306                 INTEROP_LOG(FATAL) << "Unable to load module due to exception";
307                 UNREACHABLE();
308             }
309             INTEROP_FATAL_IF(status != napi_ok);
310         }
311         INTEROP_FATAL_IF(IsUndefined(env, modObj));
312     }
313 
314     return JSValue::CreateRefValue(coro, ctx, modObj, napi_object);
315 }
316 
JSRuntimeStrictEqual(JSValue * lhs,JSValue * rhs)317 uint8_t JSRuntimeStrictEqual([[maybe_unused]] JSValue *lhs, [[maybe_unused]] JSValue *rhs)
318 {
319     auto coro = EtsCoroutine::GetCurrent();
320     auto ctx = InteropCtx::Current(coro);
321     INTEROP_CODE_SCOPE_ETS(coro);
322     auto env = ctx->GetJSEnv();
323     NapiScope jsHandleScope(env);
324 
325     bool result = true;
326     NAPI_CHECK_FATAL(napi_strict_equals(env, lhs->GetNapiValue(env), rhs->GetNapiValue(env), &result));
327     return static_cast<uint8_t>(result);
328 }
329 
JSValueToString(JSValue * object)330 EtsString *JSValueToString(JSValue *object)
331 {
332     ASSERT(object != nullptr);
333 
334     auto coro = EtsCoroutine::GetCurrent();
335     auto ctx = InteropCtx::Current(coro);
336     napi_env env = ctx->GetJSEnv();
337 
338     napi_value strObj;
339     napi_status status;
340     {
341         auto napiValue = object->GetNapiValue(env);
342 
343         ScopedNativeCodeThread nativeScope(coro);  // NOTE: Scope(Native/Managed)CodeThread should be optional here
344         status = napi_coerce_to_string(env, napiValue, &strObj);
345     }
346     if (UNLIKELY(status != napi_ok)) {
347         INTEROP_FATAL_IF(status != napi_string_expected && status != napi_pending_exception);
348         ctx->ForwardJSException(coro);
349         return nullptr;
350     }
351 
352     auto res = JSConvertString::UnwrapWithNullCheck(ctx, env, strObj);
353     if (UNLIKELY(!res.has_value())) {
354         if (NapiIsExceptionPending(env)) {
355             ctx->ForwardJSException(coro);
356         }
357         ASSERT(ctx->SanityETSExceptionPending());
358         return nullptr;
359     }
360     return res.value();
361 }
362 
ToLocal(void * value)363 napi_value ToLocal(void *value)
364 {
365     return reinterpret_cast<napi_value>(value);
366 }
367 
CompilerGetJSNamedProperty(void * val,char * propStr)368 void *CompilerGetJSNamedProperty(void *val, char *propStr)
369 {
370     auto coro = EtsCoroutine::GetCurrent();
371     auto ctx = InteropCtx::Current(coro);
372     INTEROP_CODE_SCOPE_ETS(coro);
373     napi_env env = ctx->GetJSEnv();
374     auto jsVal = ToLocal(val);
375     napi_value res;
376     napi_status rc = napi_get_named_property(env, jsVal, propStr, &res);
377     if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
378         ctx->ForwardJSException(coro);
379         ASSERT(NapiIsExceptionPending(env));
380         return nullptr;
381     }
382     return res;
383 }
384 
CompilerGetJSProperty(void * val,void * prop)385 void *CompilerGetJSProperty(void *val, void *prop)
386 {
387     auto coro = EtsCoroutine::GetCurrent();
388     auto ctx = InteropCtx::Current(coro);
389     INTEROP_CODE_SCOPE_ETS(coro);
390     napi_env env = ctx->GetJSEnv();
391     auto jsVal = ToLocal(val);
392     napi_value res;
393     napi_status rc = napi_get_property(env, jsVal, ToLocal(prop), &res);
394     if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
395         ctx->ForwardJSException(coro);
396         ASSERT(NapiIsExceptionPending(env));
397         return nullptr;
398     }
399     return res;
400 }
401 
CompilerGetJSElement(void * val,int32_t index)402 void *CompilerGetJSElement(void *val, int32_t index)
403 {
404     auto coro = EtsCoroutine::GetCurrent();
405     auto ctx = InteropCtx::Current(coro);
406     auto env = ctx->GetJSEnv();
407 
408     napi_value res;
409     auto rc = napi_get_element(env, ToLocal(val), index, &res);
410     if (UNLIKELY(NapiThrownGeneric(rc))) {
411         ctx->ForwardJSException(coro);
412         return {};
413     }
414     return res;
415 }
416 
CompilerJSCallCheck(void * fn)417 void *CompilerJSCallCheck(void *fn)
418 {
419     auto jsFn = ToLocal(fn);
420 
421     auto coro = EtsCoroutine::GetCurrent();
422     auto ctx = InteropCtx::Current(coro);
423     INTEROP_CODE_SCOPE_ETS(coro);
424     napi_env env = ctx->GetJSEnv();
425     if (UNLIKELY(GetValueType(env, jsFn) != napi_function)) {
426         ctx->ThrowJSTypeError(env, "call target is not a function");
427         ctx->ForwardJSException(coro);
428         return nullptr;
429     }
430     return fn;
431 }
432 
CompilerJSNewInstance(void * fn,uint32_t argc,void * args)433 void *CompilerJSNewInstance(void *fn, uint32_t argc, void *args)
434 {
435     auto jsFn = ToLocal(fn);
436     auto jsArgs = reinterpret_cast<napi_value *>(args);
437 
438     auto coro = EtsCoroutine::GetCurrent();
439     auto ctx = InteropCtx::Current(coro);
440     INTEROP_CODE_SCOPE_ETS(coro);
441     napi_env env = ctx->GetJSEnv();
442 
443     napi_value jsRet;
444     napi_status jsStatus;
445     {
446         ScopedNativeCodeThread nativeScope(coro);
447         jsStatus = napi_new_instance(env, jsFn, argc, jsArgs, &jsRet);
448     }
449 
450     if (UNLIKELY(jsStatus != napi_ok)) {
451         INTEROP_FATAL_IF(jsStatus != napi_pending_exception);
452         ctx->ForwardJSException(coro);
453         INTEROP_LOG(DEBUG) << "JSValueJSCall: exit with pending exception";
454         return nullptr;
455     }
456     return jsRet;
457 }
458 
CreateLocalScope()459 void CreateLocalScope()
460 {
461     auto coro = EtsCoroutine::GetCurrent();
462     auto ctx = InteropCtx::Current(coro);
463     napi_env env = ctx->GetJSEnv();
464     auto scopesStorage = ctx->GetLocalScopesStorage();
465     ASSERT(coro->IsCurrentFrameCompiled());
466     scopesStorage->CreateLocalScope(env, coro->GetCurrentFrame());
467 }
468 
CompilerDestroyLocalScope()469 void CompilerDestroyLocalScope()
470 {
471     auto coro = EtsCoroutine::GetCurrent();
472     auto ctx = InteropCtx::Current(coro);
473     napi_env env = ctx->GetJSEnv();
474     auto scopesStorage = ctx->GetLocalScopesStorage();
475     ASSERT(coro->IsCurrentFrameCompiled());
476     scopesStorage->DestroyTopLocalScope(env, coro->GetCurrentFrame());
477 }
478 
CompilerLoadJSConstantPool()479 void *CompilerLoadJSConstantPool()
480 {
481     auto coro = EtsCoroutine::GetCurrent();
482     auto ctx = InteropCtx::Current(coro);
483     return ctx->GetConstStringStorage()->GetConstantPool();
484 }
485 
CompilerInitJSCallClassForCtx(void * klassPtr)486 void CompilerInitJSCallClassForCtx(void *klassPtr)
487 {
488     auto coro = EtsCoroutine::GetCurrent();
489     auto ctx = InteropCtx::Current(coro);
490     auto klass = reinterpret_cast<Class *>(klassPtr);
491     ctx->GetConstStringStorage()->LoadDynamicCallClass(klass);
492 }
493 
CompilerConvertVoidToLocal()494 void *CompilerConvertVoidToLocal()
495 {
496     auto coro = EtsCoroutine::GetCurrent();
497     auto ctx = InteropCtx::Current(coro);
498     napi_env env = ctx->GetJSEnv();
499     return GetUndefined(env);
500 }
501 
CompilerConvertRefTypeToLocal(EtsObject * etsValue)502 void *CompilerConvertRefTypeToLocal(EtsObject *etsValue)
503 {
504     auto coro = EtsCoroutine::GetCurrent();
505     auto ctx = InteropCtx::Current(coro);
506     INTEROP_CODE_SCOPE_ETS(coro);
507     napi_env env = ctx->GetJSEnv();
508 
509     auto ref = etsValue->GetCoreType();
510     if (UNLIKELY(ref == nullptr)) {
511         return GetNull(env);
512     }
513 
514     auto klass = ref->template ClassAddr<Class>();
515     // start fastpath
516     if (klass == ctx->GetJSValueClass()) {
517         auto value = JSValue::FromEtsObject(EtsObject::FromCoreType(ref));
518         napi_value res = JSConvertJSValue::Wrap(env, value);
519         if (UNLIKELY(res == nullptr)) {
520             ctx->ForwardJSException(coro);
521         }
522         return res;
523     }
524     if (klass == ctx->GetStringClass()) {
525         auto value = EtsString::FromEtsObject(EtsObject::FromCoreType(ref));
526         napi_value res = JSConvertString::Wrap(env, value);
527         if (UNLIKELY(res == nullptr)) {
528             ctx->ForwardJSException(coro);
529         }
530         return res;
531     }
532     // start slowpath
533     auto refconv = JSRefConvertResolve(ctx, klass);
534     auto res = refconv->Wrap(ctx, EtsObject::FromCoreType(ref));
535     if (UNLIKELY(res == nullptr)) {
536         ctx->ForwardJSException(coro);
537     }
538     return res;
539 }
540 
CompilerConvertLocalToString(void * value)541 EtsString *CompilerConvertLocalToString(void *value)
542 {
543     auto jsVal = ToLocal(value);
544 
545     auto coro = EtsCoroutine::GetCurrent();
546     auto ctx = InteropCtx::Current(coro);
547     napi_env env = ctx->GetJSEnv();
548     // NOTE(kprokopenko): can't assign undefined to EtsString *, see #14765
549     if (UNLIKELY(IsNullOrUndefined(env, jsVal))) {
550         return nullptr;
551     }
552 
553     auto res = JSConvertString::Unwrap(ctx, env, jsVal);
554     if (UNLIKELY(!res.has_value())) {
555         if (NapiIsExceptionPending(env)) {
556             ctx->ForwardJSException(coro);
557         }
558         ASSERT(ctx->SanityETSExceptionPending());
559         return nullptr;
560     }
561     return res.value();
562 }
563 
CompilerConvertLocalToRefType(void * klassPtr,void * value)564 EtsObject *CompilerConvertLocalToRefType(void *klassPtr, void *value)
565 {
566     auto jsVal = ToLocal(value);
567     auto klass = reinterpret_cast<Class *>(klassPtr);
568 
569     auto coro = EtsCoroutine::GetCurrent();
570     auto ctx = InteropCtx::Current(coro);
571     INTEROP_CODE_SCOPE_ETS(coro);
572     napi_env env = ctx->GetJSEnv();
573     if (UNLIKELY(IsNull(env, jsVal))) {
574         return nullptr;
575     }
576 
577     if (UNLIKELY(IsUndefined(env, jsVal))) {
578         return ctx->GetUndefinedObject();
579     }
580 
581     // start slowpath
582     auto refconv = JSRefConvertResolve<true>(ctx, klass);
583     if (UNLIKELY(refconv == nullptr)) {
584         if (NapiIsExceptionPending(env)) {
585             ctx->ForwardJSException(coro);
586         }
587         ASSERT(ctx->SanityETSExceptionPending());
588         return nullptr;
589     }
590     ObjectHeader *res = refconv->Unwrap(ctx, jsVal)->GetCoreType();
591     if (UNLIKELY(res == nullptr)) {
592         if (NapiIsExceptionPending(env)) {
593             ctx->ForwardJSException(coro);
594         }
595         ASSERT(ctx->SanityETSExceptionPending());
596         return nullptr;
597     }
598     return EtsObject::FromCoreType(res);
599 }
600 
JSONStringify(JSValue * jsvalue)601 EtsString *JSONStringify(JSValue *jsvalue)
602 {
603     ASSERT(jsvalue != nullptr);
604 
605     auto coro = EtsCoroutine::GetCurrent();
606     auto ctx = InteropCtx::Current(coro);
607     auto env = ctx->GetJSEnv();
608     NapiScope jsHandleScope(env);
609 
610     auto global = GetGlobal(env);
611 
612     napi_value jsonInstance;
613     NAPI_CHECK_FATAL(napi_get_named_property(env, global, "JSON", &jsonInstance));
614 
615     napi_value stringify;
616     NAPI_CHECK_FATAL(napi_get_named_property(env, jsonInstance, "stringify", &stringify));
617 
618     auto object = jsvalue->GetNapiValue(env);
619     std::array<napi_value, 1> args = {object};
620 
621     napi_value result;
622     napi_status jsStatus;
623     {
624         ScopedNativeCodeThread nativeScope(coro);
625         jsStatus = napi_call_function(env, jsonInstance, stringify, args.size(), args.data(), &result);
626     }
627 
628     if (UNLIKELY(jsStatus != napi_ok)) {
629         INTEROP_FATAL_IF(jsStatus != napi_pending_exception);
630         ctx->ForwardJSException(coro);
631         return nullptr;
632     }
633     auto res = JSConvertString::Unwrap(ctx, env, result);
634     return res.value_or(nullptr);
635 }
636 
SettleJsPromise(EtsObject * value,napi_deferred deferred,EtsInt state)637 void SettleJsPromise(EtsObject *value, napi_deferred deferred, EtsInt state)
638 {
639     auto *coro = EtsCoroutine::GetCurrent();
640     ASSERT(coro == coro->GetPandaVM()->GetCoroutineManager()->GetMainThread());
641     auto *ctx = InteropCtx::Current(coro);
642     napi_env env = ctx->GetJSEnv();
643     napi_value completionValue;
644 
645     NapiScope napiScope(env);
646     if (value == nullptr) {
647         napi_get_null(env, &completionValue);
648     } else {
649         auto refconv = JSRefConvertResolve(ctx, value->GetClass()->GetRuntimeClass());
650         completionValue = refconv->Wrap(ctx, value);
651     }
652     napi_status status = state == EtsPromise::STATE_RESOLVED ? napi_resolve_deferred(env, deferred, completionValue)
653                                                              : napi_reject_deferred(env, deferred, completionValue);
654     if (status != napi_ok) {
655         napi_throw_error(env, nullptr, "Cannot resolve promise");
656     }
657 }
658 
PromiseInteropResolve(EtsObject * value,EtsLong deferred)659 void PromiseInteropResolve(EtsObject *value, EtsLong deferred)
660 {
661     SettleJsPromise(value, reinterpret_cast<napi_deferred>(deferred), EtsPromise::STATE_RESOLVED);
662 }
663 
PromiseInteropReject(EtsObject * value,EtsLong deferred)664 void PromiseInteropReject(EtsObject *value, EtsLong deferred)
665 {
666     SettleJsPromise(value, reinterpret_cast<napi_deferred>(deferred), EtsPromise::STATE_REJECTED);
667 }
668 
669 }  // namespace ark::ets::interop::js
670