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