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