• 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,EtsObject * clsObj)122 EtsObject *JSRuntimeGetValueObject(JSValue *etsJsValue, EtsObject *clsObj)
123 {
124     auto coro = EtsCoroutine::GetCurrent();
125     auto ctx = InteropCtx::Current(coro);
126 
127     if (!clsObj->GetClass()->IsClassClass()) {
128         ctx->Fatal("GetValueObject parameter is not a ClassClass instance");
129     }
130     auto const cls = reinterpret_cast<EtsClass *>(clsObj);
131 
132     if (etsJsValue == nullptr) {
133         return nullptr;
134     }
135 
136     // NOTE(kprokopenko): awful solution, see #14765
137     if (etsJsValue->AsObject() == ctx->GetUndefinedObject()) {
138         return etsJsValue->AsObject();
139     }
140 
141     INTEROP_CODE_SCOPE_ETS(coro);
142     auto env = ctx->GetJSEnv();
143     NapiScope jsHandleScope(env);
144     napi_value jsVal = etsJsValue->GetNapiValue(env);
145 
146     auto refconv = JSRefConvertResolve<true>(ctx, cls->GetRuntimeClass());
147     if (UNLIKELY(refconv == nullptr)) {
148         if (NapiIsExceptionPending(env)) {
149             ctx->ForwardJSException(coro);
150         }
151         ASSERT(ctx->SanityETSExceptionPending());
152         return nullptr;
153     }
154 
155     return refconv->Unwrap(ctx, jsVal);
156 }
157 
JSRuntimeGetUndefined()158 JSValue *JSRuntimeGetUndefined()
159 {
160     auto coro = EtsCoroutine::GetCurrent();
161     auto ctx = InteropCtx::Current(coro);
162     return JSValue::CreateUndefined(coro, ctx);
163 }
164 
JSRuntimeGetNull()165 JSValue *JSRuntimeGetNull()
166 {
167     auto coro = EtsCoroutine::GetCurrent();
168     auto ctx = InteropCtx::Current(coro);
169     return JSValue::CreateNull(coro, ctx);
170 }
171 
JSRuntimeGetGlobal()172 JSValue *JSRuntimeGetGlobal()
173 {
174     auto coro = EtsCoroutine::GetCurrent();
175     auto ctx = InteropCtx::Current(coro);
176     INTEROP_CODE_SCOPE_ETS(coro);
177     auto env = ctx->GetJSEnv();
178     NapiScope jsHandleScope(env);
179 
180     return JSValue::CreateRefValue(coro, ctx, GetGlobal(env), napi_object);
181 }
182 
JSRuntimeCreateObject()183 JSValue *JSRuntimeCreateObject()
184 {
185     auto coro = EtsCoroutine::GetCurrent();
186     auto ctx = InteropCtx::Current(coro);
187     INTEROP_CODE_SCOPE_ETS(coro);
188     auto env = ctx->GetJSEnv();
189     NapiScope jsHandleScope(env);
190 
191     napi_value obj;
192     NAPI_CHECK_FATAL(napi_create_object(env, &obj));
193     return JSValue::CreateRefValue(coro, ctx, obj, napi_object);
194 }
195 
JSRuntimeInstanceOfDynamic(JSValue * object,JSValue * ctor)196 uint8_t JSRuntimeInstanceOfDynamic(JSValue *object, JSValue *ctor)
197 {
198     auto coro = EtsCoroutine::GetCurrent();
199     auto ctx = InteropCtx::Current(coro);
200     INTEROP_CODE_SCOPE_ETS(coro);
201     auto env = ctx->GetJSEnv();
202     NapiScope jsHandleScope(env);
203 
204     auto jsObj = object->GetNapiValue(env);
205     auto jsCtor = ctor->GetNapiValue(env);
206     bool res;
207     napi_status rc = napi_instanceof(env, jsObj, jsCtor, &res);
208     if (UNLIKELY(NapiThrownGeneric(rc))) {
209         ctx->ForwardJSException(coro);
210         return 0;
211     }
212     return static_cast<uint8_t>(res);
213 }
214 
JSRuntimeInstanceOfStatic(JSValue * etsJsValue,EtsClass * etsClass)215 uint8_t JSRuntimeInstanceOfStatic(JSValue *etsJsValue, EtsClass *etsClass)
216 {
217     ASSERT(etsClass != nullptr);
218 
219     if (etsJsValue == nullptr) {
220         return 0;
221     }
222 
223     auto coro = EtsCoroutine::GetCurrent();
224     auto ctx = InteropCtx::Current(coro);
225     auto env = ctx->GetJSEnv();
226 
227     Class *cls = etsClass->GetRuntimeClass();
228     if (!cls->IsInitialized() && !IsStdClass(cls)) {
229         // 'js_value' haven't been created from the uninitialized class
230         return 0;
231     }
232 
233     // Check if object has SharedReference
234     ets_proxy::SharedReference *sharedRef = [=] {
235         NapiScope jsHandleScope(env);
236         napi_value jsValue = etsJsValue->GetNapiValue(env);
237         return ctx->GetSharedRefStorage()->GetReference(env, jsValue);
238     }();
239 
240     if (sharedRef != nullptr) {
241         EtsObject *etsObject = sharedRef->GetEtsObject(ctx);
242         return static_cast<uint8_t>(etsClass->IsAssignableFrom(etsObject->GetClass()));
243     }
244 
245     if (IsStdClass(cls)) {
246         // NOTE(v.cherkashin): Add support compat types, #13577
247         INTEROP_LOG(FATAL) << __func__ << " doesn't support compat types";
248     }
249 
250     return 0;
251 }
252 
ResolveModuleName(std::string_view module)253 std::pair<std::string_view, std::string_view> ResolveModuleName(std::string_view module)
254 {
255     static const std::unordered_set<std::string_view> NATIVE_MODULE_LIST = {
256         "@system.app",  "@ohos.app",       "@system.router", "@system.curves",
257         "@ohos.curves", "@system.matrix4", "@ohos.matrix4"};
258 
259     constexpr std::string_view REQUIRE = "require";
260     constexpr std::string_view REQUIRE_NAPI = "requireNapi";
261     constexpr std::string_view REQUIRE_NATIVE_MODULE = "requireNativeModule";
262     constexpr std::string_view SYSTEM_PLUGIN_PREFIX = "@system.";
263     constexpr std::string_view OHOS_PLUGIN_PREFIX = "@ohos.";
264 
265     if (NATIVE_MODULE_LIST.count(module) != 0) {
266         return {module.substr(1), REQUIRE_NATIVE_MODULE};
267     }
268 
269     if (module.compare(0, SYSTEM_PLUGIN_PREFIX.size(), SYSTEM_PLUGIN_PREFIX) == 0) {
270         return {module.substr(SYSTEM_PLUGIN_PREFIX.size()), REQUIRE_NAPI};
271     }
272 
273     if (module.compare(0, OHOS_PLUGIN_PREFIX.size(), OHOS_PLUGIN_PREFIX) == 0) {
274         return {module.substr(OHOS_PLUGIN_PREFIX.size()), REQUIRE_NAPI};
275     }
276 
277     return {module, REQUIRE};
278 }
279 
JSRuntimeLoadModule(EtsString * module)280 JSValue *JSRuntimeLoadModule(EtsString *module)
281 {
282     auto coro = EtsCoroutine::GetCurrent();
283     auto ctx = InteropCtx::Current(coro);
284     INTEROP_CODE_SCOPE_ETS(coro);
285     auto env = ctx->GetJSEnv();
286 
287     PandaString moduleName = module->GetMutf8();
288     auto [mod, func] = ResolveModuleName(moduleName);
289 
290     napi_value modObj;
291     {
292         ScopedNativeCodeThread etsNativeScope(coro);
293         NapiScope jsHandleScope(env);
294 
295         napi_value requireFn;
296         NAPI_CHECK_FATAL(napi_get_named_property(env, GetGlobal(env), func.data(), &requireFn));
297 
298         INTEROP_FATAL_IF(GetValueType(env, requireFn) != napi_function);
299         {
300             napi_value jsName;
301             NAPI_CHECK_FATAL(napi_create_string_utf8(env, mod.data(), NAPI_AUTO_LENGTH, &jsName));
302             std::array<napi_value, 1> args = {jsName};
303             napi_value recv;
304             NAPI_CHECK_FATAL(napi_get_undefined(env, &recv));
305             auto status = (napi_call_function(env, recv, requireFn, args.size(), args.data(), &modObj));
306             if (status == napi_pending_exception) {
307                 napi_value exp;
308                 NAPI_CHECK_FATAL(napi_get_and_clear_last_exception(env, &exp));
309                 NAPI_CHECK_FATAL(napi_fatal_exception(env, exp));
310                 INTEROP_LOG(FATAL) << "Unable to load module due to exception";
311                 UNREACHABLE();
312             }
313             INTEROP_FATAL_IF(status != napi_ok);
314         }
315         INTEROP_FATAL_IF(IsUndefined(env, modObj));
316     }
317 
318     return JSValue::CreateRefValue(coro, ctx, modObj, napi_object);
319 }
320 
JSRuntimeStrictEqual(JSValue * lhs,JSValue * rhs)321 uint8_t JSRuntimeStrictEqual([[maybe_unused]] JSValue *lhs, [[maybe_unused]] JSValue *rhs)
322 {
323     auto coro = EtsCoroutine::GetCurrent();
324     auto ctx = InteropCtx::Current(coro);
325     INTEROP_CODE_SCOPE_ETS(coro);
326     auto env = ctx->GetJSEnv();
327     NapiScope jsHandleScope(env);
328 
329     bool result = true;
330     NAPI_CHECK_FATAL(napi_strict_equals(env, lhs->GetNapiValue(env), rhs->GetNapiValue(env), &result));
331     return static_cast<uint8_t>(result);
332 }
333 
JSValueToString(JSValue * object)334 EtsString *JSValueToString(JSValue *object)
335 {
336     ASSERT(object != nullptr);
337 
338     auto coro = EtsCoroutine::GetCurrent();
339     auto ctx = InteropCtx::Current(coro);
340     napi_env env = ctx->GetJSEnv();
341 
342     napi_value strObj;
343     napi_status status;
344     {
345         auto napiValue = object->GetNapiValue(env);
346 
347         ScopedNativeCodeThread nativeScope(coro);  // NOTE: Scope(Native/Managed)CodeThread should be optional here
348         status = napi_coerce_to_string(env, napiValue, &strObj);
349     }
350     if (UNLIKELY(status != napi_ok)) {
351         INTEROP_FATAL_IF(status != napi_string_expected && status != napi_pending_exception);
352         ctx->ForwardJSException(coro);
353         return nullptr;
354     }
355 
356     auto res = JSConvertString::UnwrapWithNullCheck(ctx, env, strObj);
357     if (UNLIKELY(!res.has_value())) {
358         if (NapiIsExceptionPending(env)) {
359             ctx->ForwardJSException(coro);
360         }
361         ASSERT(ctx->SanityETSExceptionPending());
362         return nullptr;
363     }
364     return res.value();
365 }
366 
ToLocal(void * value)367 napi_value ToLocal(void *value)
368 {
369     return reinterpret_cast<napi_value>(value);
370 }
371 
CompilerGetJSNamedProperty(void * val,char * propStr)372 void *CompilerGetJSNamedProperty(void *val, char *propStr)
373 {
374     auto coro = EtsCoroutine::GetCurrent();
375     auto ctx = InteropCtx::Current(coro);
376     INTEROP_CODE_SCOPE_ETS(coro);
377     napi_env env = ctx->GetJSEnv();
378     auto jsVal = ToLocal(val);
379     napi_value res;
380     napi_status rc = napi_get_named_property(env, jsVal, propStr, &res);
381     if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
382         ctx->ForwardJSException(coro);
383         ASSERT(NapiIsExceptionPending(env));
384         return nullptr;
385     }
386     return res;
387 }
388 
CompilerGetJSProperty(void * val,void * prop)389 void *CompilerGetJSProperty(void *val, void *prop)
390 {
391     auto coro = EtsCoroutine::GetCurrent();
392     auto ctx = InteropCtx::Current(coro);
393     INTEROP_CODE_SCOPE_ETS(coro);
394     napi_env env = ctx->GetJSEnv();
395     auto jsVal = ToLocal(val);
396     napi_value res;
397     napi_status rc = napi_get_property(env, jsVal, ToLocal(prop), &res);
398     if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
399         ctx->ForwardJSException(coro);
400         ASSERT(NapiIsExceptionPending(env));
401         return nullptr;
402     }
403     return res;
404 }
405 
CompilerGetJSElement(void * val,int32_t index)406 void *CompilerGetJSElement(void *val, int32_t index)
407 {
408     auto coro = EtsCoroutine::GetCurrent();
409     auto ctx = InteropCtx::Current(coro);
410     auto env = ctx->GetJSEnv();
411 
412     napi_value res;
413     auto rc = napi_get_element(env, ToLocal(val), index, &res);
414     if (UNLIKELY(NapiThrownGeneric(rc))) {
415         ctx->ForwardJSException(coro);
416         return {};
417     }
418     return res;
419 }
420 
CompilerJSCallCheck(void * fn)421 void *CompilerJSCallCheck(void *fn)
422 {
423     auto jsFn = ToLocal(fn);
424 
425     auto coro = EtsCoroutine::GetCurrent();
426     auto ctx = InteropCtx::Current(coro);
427     INTEROP_CODE_SCOPE_ETS(coro);
428     napi_env env = ctx->GetJSEnv();
429     if (UNLIKELY(GetValueType(env, jsFn) != napi_function)) {
430         ctx->ThrowJSTypeError(env, "call target is not a function");
431         ctx->ForwardJSException(coro);
432         return nullptr;
433     }
434     return fn;
435 }
436 
CompilerJSNewInstance(void * fn,uint32_t argc,void * args)437 void *CompilerJSNewInstance(void *fn, uint32_t argc, void *args)
438 {
439     auto jsFn = ToLocal(fn);
440     auto jsArgs = reinterpret_cast<napi_value *>(args);
441 
442     auto coro = EtsCoroutine::GetCurrent();
443     auto ctx = InteropCtx::Current(coro);
444     INTEROP_CODE_SCOPE_ETS(coro);
445     napi_env env = ctx->GetJSEnv();
446 
447     napi_value jsRet;
448     napi_status jsStatus;
449     {
450         ScopedNativeCodeThread nativeScope(coro);
451         jsStatus = napi_new_instance(env, jsFn, argc, jsArgs, &jsRet);
452     }
453 
454     if (UNLIKELY(jsStatus != napi_ok)) {
455         INTEROP_FATAL_IF(jsStatus != napi_pending_exception);
456         ctx->ForwardJSException(coro);
457         INTEROP_LOG(DEBUG) << "JSValueJSCall: exit with pending exception";
458         return nullptr;
459     }
460     return jsRet;
461 }
462 
CreateLocalScope()463 void CreateLocalScope()
464 {
465     auto coro = EtsCoroutine::GetCurrent();
466     auto ctx = InteropCtx::Current(coro);
467     napi_env env = ctx->GetJSEnv();
468     auto scopesStorage = ctx->GetLocalScopesStorage();
469     ASSERT(coro->IsCurrentFrameCompiled());
470     scopesStorage->CreateLocalScope(env, coro->GetCurrentFrame());
471 }
472 
CompilerDestroyLocalScope()473 void CompilerDestroyLocalScope()
474 {
475     auto coro = EtsCoroutine::GetCurrent();
476     auto ctx = InteropCtx::Current(coro);
477     napi_env env = ctx->GetJSEnv();
478     auto scopesStorage = ctx->GetLocalScopesStorage();
479     ASSERT(coro->IsCurrentFrameCompiled());
480     scopesStorage->DestroyTopLocalScope(env, coro->GetCurrentFrame());
481 }
482 
CompilerLoadJSConstantPool()483 void *CompilerLoadJSConstantPool()
484 {
485     auto coro = EtsCoroutine::GetCurrent();
486     auto ctx = InteropCtx::Current(coro);
487     return ctx->GetConstStringStorage()->GetConstantPool();
488 }
489 
CompilerInitJSCallClassForCtx(void * klassPtr)490 void CompilerInitJSCallClassForCtx(void *klassPtr)
491 {
492     auto coro = EtsCoroutine::GetCurrent();
493     auto ctx = InteropCtx::Current(coro);
494     auto klass = reinterpret_cast<Class *>(klassPtr);
495     ctx->GetConstStringStorage()->LoadDynamicCallClass(klass);
496 }
497 
CompilerConvertVoidToLocal()498 void *CompilerConvertVoidToLocal()
499 {
500     auto coro = EtsCoroutine::GetCurrent();
501     auto ctx = InteropCtx::Current(coro);
502     napi_env env = ctx->GetJSEnv();
503     return GetUndefined(env);
504 }
505 
CompilerConvertRefTypeToLocal(EtsObject * etsValue)506 void *CompilerConvertRefTypeToLocal(EtsObject *etsValue)
507 {
508     auto coro = EtsCoroutine::GetCurrent();
509     auto ctx = InteropCtx::Current(coro);
510     INTEROP_CODE_SCOPE_ETS(coro);
511     napi_env env = ctx->GetJSEnv();
512 
513     auto ref = etsValue->GetCoreType();
514     if (UNLIKELY(ref == nullptr)) {
515         return GetNull(env);
516     }
517 
518     auto klass = ref->template ClassAddr<Class>();
519     // start fastpath
520     if (klass == ctx->GetJSValueClass()) {
521         auto value = JSValue::FromEtsObject(EtsObject::FromCoreType(ref));
522         napi_value res = JSConvertJSValue::Wrap(env, value);
523         if (UNLIKELY(res == nullptr)) {
524             ctx->ForwardJSException(coro);
525         }
526         return res;
527     }
528     if (klass == ctx->GetStringClass()) {
529         auto value = EtsString::FromEtsObject(EtsObject::FromCoreType(ref));
530         napi_value res = JSConvertString::Wrap(env, value);
531         if (UNLIKELY(res == nullptr)) {
532             ctx->ForwardJSException(coro);
533         }
534         return res;
535     }
536     // start slowpath
537     auto refconv = JSRefConvertResolve(ctx, klass);
538     auto res = refconv->Wrap(ctx, EtsObject::FromCoreType(ref));
539     if (UNLIKELY(res == nullptr)) {
540         ctx->ForwardJSException(coro);
541     }
542     return res;
543 }
544 
CompilerConvertLocalToString(void * value)545 EtsString *CompilerConvertLocalToString(void *value)
546 {
547     auto jsVal = ToLocal(value);
548 
549     auto coro = EtsCoroutine::GetCurrent();
550     auto ctx = InteropCtx::Current(coro);
551     napi_env env = ctx->GetJSEnv();
552     // NOTE(kprokopenko): can't assign undefined to EtsString *, see #14765
553     if (UNLIKELY(IsNullOrUndefined(env, jsVal))) {
554         return nullptr;
555     }
556 
557     auto res = JSConvertString::Unwrap(ctx, env, jsVal);
558     if (UNLIKELY(!res.has_value())) {
559         if (NapiIsExceptionPending(env)) {
560             ctx->ForwardJSException(coro);
561         }
562         ASSERT(ctx->SanityETSExceptionPending());
563         return nullptr;
564     }
565     return res.value();
566 }
567 
CompilerConvertLocalToRefType(void * klassPtr,void * value)568 EtsObject *CompilerConvertLocalToRefType(void *klassPtr, void *value)
569 {
570     auto jsVal = ToLocal(value);
571     auto klass = reinterpret_cast<Class *>(klassPtr);
572 
573     auto coro = EtsCoroutine::GetCurrent();
574     auto ctx = InteropCtx::Current(coro);
575     INTEROP_CODE_SCOPE_ETS(coro);
576     napi_env env = ctx->GetJSEnv();
577     if (UNLIKELY(IsNull(env, jsVal))) {
578         return nullptr;
579     }
580 
581     if (UNLIKELY(IsUndefined(env, jsVal))) {
582         return ctx->GetUndefinedObject();
583     }
584 
585     // start slowpath
586     auto refconv = JSRefConvertResolve<true>(ctx, klass);
587     if (UNLIKELY(refconv == nullptr)) {
588         if (NapiIsExceptionPending(env)) {
589             ctx->ForwardJSException(coro);
590         }
591         ASSERT(ctx->SanityETSExceptionPending());
592         return nullptr;
593     }
594     ObjectHeader *res = refconv->Unwrap(ctx, jsVal)->GetCoreType();
595     if (UNLIKELY(res == nullptr)) {
596         if (NapiIsExceptionPending(env)) {
597             ctx->ForwardJSException(coro);
598         }
599         ASSERT(ctx->SanityETSExceptionPending());
600     }
601     return EtsObject::FromCoreType(res);
602 }
603 
JSONStringify(JSValue * jsvalue)604 EtsString *JSONStringify(JSValue *jsvalue)
605 {
606     ASSERT(jsvalue != nullptr);
607 
608     auto coro = EtsCoroutine::GetCurrent();
609     auto ctx = InteropCtx::Current(coro);
610     auto env = ctx->GetJSEnv();
611     NapiScope jsHandleScope(env);
612 
613     auto global = GetGlobal(env);
614 
615     napi_value jsonInstance;
616     NAPI_CHECK_FATAL(napi_get_named_property(env, global, "JSON", &jsonInstance));
617 
618     napi_value stringify;
619     NAPI_CHECK_FATAL(napi_get_named_property(env, jsonInstance, "stringify", &stringify));
620 
621     auto object = jsvalue->GetNapiValue(env);
622     std::array<napi_value, 1> args = {object};
623 
624     napi_value result;
625     napi_status jsStatus;
626     {
627         ScopedNativeCodeThread nativeScope(coro);
628         jsStatus = napi_call_function(env, jsonInstance, stringify, args.size(), args.data(), &result);
629     }
630 
631     if (UNLIKELY(jsStatus != napi_ok)) {
632         INTEROP_FATAL_IF(jsStatus != napi_pending_exception);
633         ctx->ForwardJSException(coro);
634         return nullptr;
635     }
636     auto res = JSConvertString::Unwrap(ctx, env, result);
637     return res.value_or(nullptr);
638 }
639 
SettleJsPromise(EtsObject * value,napi_deferred deferred,EtsInt state)640 void SettleJsPromise(EtsObject *value, napi_deferred deferred, EtsInt state)
641 {
642     auto *coro = EtsCoroutine::GetCurrent();
643     ASSERT(coro == coro->GetPandaVM()->GetCoroutineManager()->GetMainThread());
644     auto *ctx = InteropCtx::Current(coro);
645     napi_env env = ctx->GetJSEnv();
646     napi_value completionValue;
647     if (value == nullptr) {
648         napi_get_null(env, &completionValue);
649     } else {
650         auto refconv = JSRefConvertResolve(ctx, value->GetClass()->GetRuntimeClass());
651         completionValue = refconv->Wrap(ctx, value);
652     }
653     napi_status status = state == EtsPromise::STATE_RESOLVED ? napi_resolve_deferred(env, deferred, completionValue)
654                                                              : napi_reject_deferred(env, deferred, completionValue);
655     if (status != napi_ok) {
656         napi_throw_error(env, nullptr, "Cannot resolve promise");
657     }
658 }
659 
PromiseInteropResolve(EtsObject * value,EtsLong deferred)660 void PromiseInteropResolve(EtsObject *value, EtsLong deferred)
661 {
662     SettleJsPromise(value, reinterpret_cast<napi_deferred>(deferred), EtsPromise::STATE_RESOLVED);
663 }
664 
PromiseInteropReject(EtsObject * value,EtsLong deferred)665 void PromiseInteropReject(EtsObject *value, EtsLong deferred)
666 {
667     SettleJsPromise(value, reinterpret_cast<napi_deferred>(deferred), EtsPromise::STATE_REJECTED);
668 }
669 
670 }  // namespace ark::ets::interop::js
671