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/js_value_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.h"
20 #include "plugins/ets/runtime/interop_js/intrinsics_api_impl.h"
21 #include "plugins/ets/runtime/interop_js/napi_env_scope.h"
22 #include "plugins/ets/runtime/types/ets_string.h"
23 #include "plugins/ets/runtime/types/ets_void.h"
24 #include "runtime/include/class_linker-inl.h"
25
26 namespace panda::ets::interop::js {
27
JSRuntimeFinalizationQueueCallback(EtsObject * cbarg)28 static void JSRuntimeFinalizationQueueCallback(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 static 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
JSRuntimeNewJSValueString(EtsString * v)45 static JSValue *JSRuntimeNewJSValueString(EtsString *v)
46 {
47 auto coro = EtsCoroutine::GetCurrent();
48 auto ctx = InteropCtx::Current(coro);
49 std::string str;
50 if (v->IsUtf16()) {
51 InteropCtx::Fatal("not implemented");
52 } else {
53 str = std::string(utf::Mutf8AsCString(v->GetDataMUtf8()));
54 }
55 return JSValue::CreateString(coro, ctx, std::move(str));
56 }
57
JSRuntimeNewJSValueObject(EtsObject * v)58 static JSValue *JSRuntimeNewJSValueObject(EtsObject *v)
59 {
60 if (v == nullptr) {
61 return nullptr;
62 }
63
64 auto coro = EtsCoroutine::GetCurrent();
65 auto ctx = InteropCtx::Current(coro);
66 auto env = ctx->GetJSEnv();
67 NapiScope jsHandleScope(env);
68
69 auto refconv = JSRefConvertResolve(ctx, v->GetClass()->GetRuntimeClass());
70 auto result = refconv->Wrap(ctx, v);
71
72 auto res = JSConvertJSValue::UnwrapWithNullCheck(ctx, env, result);
73 if (!res) {
74 ctx->ForwardJSException(coro);
75 return {};
76 }
77
78 return res.value();
79 }
80
JSRuntimeGetValueDouble(JSValue * etsJsValue)81 static double JSRuntimeGetValueDouble(JSValue *etsJsValue)
82 {
83 return etsJsValue->GetNumber();
84 }
85
JSRuntimeGetValueBoolean(JSValue * etsJsValue)86 static uint8_t JSRuntimeGetValueBoolean(JSValue *etsJsValue)
87 {
88 return static_cast<uint8_t>(etsJsValue->GetBoolean());
89 }
90
JSRuntimeGetValueString(JSValue * etsJsValue)91 static EtsString *JSRuntimeGetValueString(JSValue *etsJsValue)
92 {
93 auto coro = EtsCoroutine::GetCurrent();
94 auto ctx = InteropCtx::Current(coro);
95 auto env = ctx->GetJSEnv();
96
97 NapiScope jsHandleScope(env);
98
99 napi_value jsVal = etsJsValue->GetNapiValue(env);
100 auto res = JSConvertString::Unwrap(ctx, env, jsVal);
101 if (UNLIKELY(!res)) {
102 if (NapiIsExceptionPending(env)) {
103 ctx->ForwardJSException(coro);
104 }
105 ASSERT(ctx->SanityETSExceptionPending());
106 return {};
107 }
108
109 return res.value();
110 }
111
JSRuntimeGetValueObject(JSValue * etsJsValue,EtsObject * clsObj)112 static EtsObject *JSRuntimeGetValueObject(JSValue *etsJsValue, EtsObject *clsObj)
113 {
114 auto coro = EtsCoroutine::GetCurrent();
115 auto ctx = InteropCtx::Current(coro);
116
117 if (!clsObj->GetClass()->GetRuntimeClass()->IsClassClass()) {
118 ctx->Fatal("GetValueObject parameter is not a ClassClass instance");
119 }
120 auto const cls = reinterpret_cast<EtsClass *>(clsObj);
121
122 if (etsJsValue == nullptr) {
123 return nullptr;
124 }
125
126 // NOTE(kprokopenko): awful solution, see #14765
127 if (etsJsValue->AsObject() == ctx->GetUndefinedObject()) {
128 if (Class::FromClassObject(clsObj->GetCoreType()) == ctx->GetVoidClass()) {
129 return reinterpret_cast<EtsObject *>(EtsVoid::GetInstance());
130 }
131 return etsJsValue->AsObject();
132 }
133
134 auto env = ctx->GetJSEnv();
135 NapiScope jsHandleScope(env);
136 napi_value jsVal = etsJsValue->GetNapiValue(env);
137
138 auto refconv = JSRefConvertResolve<true>(ctx, cls->GetRuntimeClass());
139 if (UNLIKELY(refconv == nullptr)) {
140 if (NapiIsExceptionPending(env)) {
141 ctx->ForwardJSException(coro);
142 }
143 ASSERT(ctx->SanityETSExceptionPending());
144 return nullptr;
145 }
146
147 return refconv->Unwrap(ctx, jsVal);
148 }
149
150 template <typename T>
JSValueNamedGetter(JSValue * etsJsValue,EtsString * etsPropName)151 static typename T::cpptype JSValueNamedGetter(JSValue *etsJsValue, EtsString *etsPropName)
152 {
153 auto coro = EtsCoroutine::GetCurrent();
154 auto ctx = InteropCtx::Current(coro);
155 auto env = ctx->GetJSEnv();
156 NapiScope jsHandleScope(env);
157
158 PandaString propName = etsPropName->GetMutf8();
159 auto res = JSValueGetByName<T>(ctx, etsJsValue, propName.c_str());
160 if (UNLIKELY(!res)) {
161 ctx->ForwardJSException(coro);
162 return {};
163 }
164 return res.value();
165 }
166
167 template <typename T>
JSValueNamedSetter(JSValue * etsJsValue,EtsString * etsPropName,typename T::cpptype etsPropVal)168 static void JSValueNamedSetter(JSValue *etsJsValue, EtsString *etsPropName, typename T::cpptype etsPropVal)
169 {
170 auto coro = EtsCoroutine::GetCurrent();
171 auto ctx = InteropCtx::Current(coro);
172 auto env = ctx->GetJSEnv();
173 NapiScope jsHandleScope(env);
174
175 PandaString propName = etsPropName->GetMutf8();
176 bool res = JSValueSetByName<T>(ctx, etsJsValue, propName.c_str(), etsPropVal);
177 if (UNLIKELY(!res)) {
178 ctx->ForwardJSException(coro);
179 }
180 }
181
182 template <typename T>
JSValueIndexedGetter(JSValue * etsJsValue,int32_t index)183 static typename T::cpptype JSValueIndexedGetter(JSValue *etsJsValue, int32_t index)
184 {
185 auto coro = EtsCoroutine::GetCurrent();
186 auto ctx = InteropCtx::Current(coro);
187 auto env = ctx->GetJSEnv();
188 NapiScope jsHandleScope(env);
189
190 napi_value result;
191 napi_value jsVal = etsJsValue->GetNapiValue(env);
192 auto rc = napi_get_element(env, jsVal, index, &result);
193 if (UNLIKELY(NapiThrownGeneric(rc))) {
194 ctx->ForwardJSException(coro);
195 return {};
196 }
197
198 auto res = T::UnwrapWithNullCheck(ctx, env, result);
199 if (!res) {
200 ctx->ForwardJSException(coro);
201 return {};
202 }
203
204 return res.value();
205 }
206
JSRuntimeGetUndefined()207 static JSValue *JSRuntimeGetUndefined()
208 {
209 auto coro = EtsCoroutine::GetCurrent();
210 auto ctx = InteropCtx::Current(coro);
211 return JSValue::CreateUndefined(coro, ctx);
212 }
213
JSRuntimeGetNull()214 static JSValue *JSRuntimeGetNull()
215 {
216 auto coro = EtsCoroutine::GetCurrent();
217 auto ctx = InteropCtx::Current(coro);
218 return JSValue::CreateNull(coro, ctx);
219 }
220
JSRuntimeGetGlobal()221 static JSValue *JSRuntimeGetGlobal()
222 {
223 auto coro = EtsCoroutine::GetCurrent();
224 auto ctx = InteropCtx::Current(coro);
225 auto env = ctx->GetJSEnv();
226 NapiScope jsHandleScope(env);
227
228 return JSValue::CreateRefValue(coro, ctx, GetGlobal(env), napi_object);
229 }
230
JSRuntimeCreateObject()231 static JSValue *JSRuntimeCreateObject()
232 {
233 auto coro = EtsCoroutine::GetCurrent();
234 auto ctx = InteropCtx::Current(coro);
235 auto env = ctx->GetJSEnv();
236 NapiScope jsHandleScope(env);
237
238 napi_value obj;
239 NAPI_CHECK_FATAL(napi_create_object(env, &obj));
240 return JSValue::CreateRefValue(coro, ctx, obj, napi_object);
241 }
242
JSRuntimeInstanceOf(JSValue * object,JSValue * ctor)243 uint8_t JSRuntimeInstanceOf(JSValue *object, JSValue *ctor)
244 {
245 auto coro = EtsCoroutine::GetCurrent();
246 auto ctx = InteropCtx::Current(coro);
247 auto env = ctx->GetJSEnv();
248 NapiScope jsHandleScope(env);
249
250 auto jsObj = object->GetNapiValue(env);
251 auto jsCtor = ctor->GetNapiValue(env);
252 bool res;
253 napi_status rc = napi_instanceof(env, jsObj, jsCtor, &res);
254 if (UNLIKELY(NapiThrownGeneric(rc))) {
255 ctx->ForwardJSException(coro);
256 return 0;
257 }
258 return static_cast<uint8_t>(res);
259 }
260
JSRuntimeCreateLambdaProxy(EtsObject * etsCallable)261 static JSValue *JSRuntimeCreateLambdaProxy(EtsObject *etsCallable)
262 {
263 auto coro = EtsCoroutine::GetCurrent();
264 auto ctx = InteropCtx::Current(coro);
265 auto env = ctx->GetJSEnv();
266
267 ASSERT(etsCallable->GetClass()->GetMethod("invoke") != nullptr);
268
269 NapiScope jsHandleScope(env);
270
271 ets_proxy::SharedReferenceStorage *storage = ctx->GetSharedRefStorage();
272 if (LIKELY(storage->HasReference(etsCallable))) {
273 ets_proxy::SharedReference *sharedRef = storage->GetReference(etsCallable);
274 ASSERT(sharedRef != nullptr);
275 return JSValue::CreateRefValue(coro, ctx, sharedRef->GetJsObject(env), napi_function);
276 }
277
278 napi_value jsFn;
279 ets_proxy::SharedReference *payloadSharedRef = storage->GetNextAlloc();
280 NAPI_CHECK_FATAL(
281 napi_create_function(env, "invoke", NAPI_AUTO_LENGTH, EtsLambdaProxyInvoke, payloadSharedRef, &jsFn));
282
283 ets_proxy::SharedReference *sharedRef = storage->CreateETSObjectRef(ctx, etsCallable, jsFn);
284 if (UNLIKELY(sharedRef == nullptr)) {
285 ASSERT(InteropCtx::SanityJSExceptionPending());
286 return nullptr;
287 }
288 ASSERT(payloadSharedRef == sharedRef);
289 return JSValue::CreateRefValue(coro, ctx, jsFn, napi_function);
290 }
291
ResolveModuleName(std::string_view module)292 static std::pair<std::string_view, std::string_view> ResolveModuleName(std::string_view module)
293 {
294 static const std::unordered_set<std::string_view> NATIVE_MODULE_LIST = {
295 "@system.app", "@ohos.app", "@system.router", "@system.curves",
296 "@ohos.curves", "@system.matrix4", "@ohos.matrix4"};
297
298 constexpr std::string_view REQUIRE = "require";
299 constexpr std::string_view REQUIRE_NAPI = "requireNapi";
300 constexpr std::string_view REQUIRE_NATIVE_MODULE = "requireNativeModule";
301 constexpr std::string_view SYSTEM_PLUGIN_PREFIX = "@system.";
302 constexpr std::string_view OHOS_PLUGIN_PREFIX = "@ohos.";
303
304 if (NATIVE_MODULE_LIST.count(module) != 0) {
305 return {module.substr(1), REQUIRE_NATIVE_MODULE};
306 }
307
308 if (module.compare(0, SYSTEM_PLUGIN_PREFIX.size(), SYSTEM_PLUGIN_PREFIX) == 0) {
309 return {module.substr(SYSTEM_PLUGIN_PREFIX.size()), REQUIRE_NAPI};
310 }
311
312 if (module.compare(0, OHOS_PLUGIN_PREFIX.size(), OHOS_PLUGIN_PREFIX) == 0) {
313 return {module.substr(OHOS_PLUGIN_PREFIX.size()), REQUIRE_NAPI};
314 }
315
316 return {module, REQUIRE};
317 }
318
JSRuntimeLoadModule(EtsString * module)319 static JSValue *JSRuntimeLoadModule(EtsString *module)
320 {
321 auto coro = EtsCoroutine::GetCurrent();
322 auto ctx = InteropCtx::Current(coro);
323 auto env = ctx->GetJSEnv();
324
325 PandaString moduleName = module->GetMutf8();
326 auto [mod, func] = ResolveModuleName(moduleName);
327
328 NapiScope jsHandleScope(env);
329
330 napi_value requireFn;
331 NAPI_CHECK_FATAL(napi_get_named_property(env, GetGlobal(env), func.data(), &requireFn));
332
333 INTEROP_FATAL_IF(GetValueType(env, requireFn) != napi_function);
334 napi_value modObj;
335 {
336 napi_value jsName;
337 NAPI_CHECK_FATAL(napi_create_string_utf8(env, mod.data(), NAPI_AUTO_LENGTH, &jsName));
338 std::array<napi_value, 1> args = {jsName};
339 napi_value recv;
340 NAPI_CHECK_FATAL(napi_get_undefined(env, &recv));
341 auto status = (napi_call_function(env, recv, requireFn, args.size(), args.data(), &modObj));
342 if (status == napi_pending_exception) {
343 napi_value exp;
344 NAPI_CHECK_FATAL(napi_get_and_clear_last_exception(env, &exp));
345 NAPI_CHECK_FATAL(napi_fatal_exception(env, exp));
346 std::abort();
347 }
348
349 INTEROP_FATAL_IF(status != napi_ok);
350 }
351 INTEROP_FATAL_IF(IsUndefined(env, modObj));
352
353 return JSValue::CreateRefValue(coro, ctx, modObj, napi_object);
354 }
355
JSRuntimeStrictEqual(JSValue * lhs,JSValue * rhs)356 static uint8_t JSRuntimeStrictEqual([[maybe_unused]] JSValue *lhs, [[maybe_unused]] JSValue *rhs)
357 {
358 auto ctx = InteropCtx::Current();
359 auto env = ctx->GetJSEnv();
360 NapiScope jsHandleScope(env);
361
362 bool result = true;
363 NAPI_CHECK_FATAL(napi_strict_equals(env, lhs->GetNapiValue(env), rhs->GetNapiValue(env), &result));
364 return static_cast<uint8_t>(result);
365 }
366
ToLocal(void * value)367 static inline 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 napi_env env = ctx->GetJSEnv();
377 auto jsVal = ToLocal(val);
378 napi_value res;
379 napi_status rc = napi_get_named_property(env, jsVal, propStr, &res);
380 if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
381 ctx->ForwardJSException(coro);
382 ASSERT(NapiIsExceptionPending(env));
383 return nullptr;
384 }
385 return res;
386 }
387
CompilerResolveQualifiedJSCall(void * val,EtsString * qnameStr)388 void *CompilerResolveQualifiedJSCall(void *val, EtsString *qnameStr)
389 {
390 auto coro = EtsCoroutine::GetCurrent();
391 auto ctx = InteropCtx::Current(coro);
392 napi_env env = ctx->GetJSEnv();
393
394 auto jsVal = ToLocal(val);
395 napi_value jsThis = jsVal;
396 auto qname = std::string_view(utf::Mutf8AsCString(qnameStr->GetDataMUtf8()), qnameStr->GetMUtf8Length());
397 auto resolveName = [&jsThis, &jsVal, &env, &ctx, &coro](const std::string &name) -> bool {
398 jsThis = jsVal;
399 napi_status rc = napi_get_named_property(env, jsVal, name.c_str(), &jsVal);
400 if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
401 ctx->ForwardJSException(coro);
402 ASSERT(NapiIsExceptionPending(env));
403 return false;
404 }
405 return true;
406 };
407 jsThis = jsVal;
408 if (UNLIKELY(!WalkQualifiedName(qname, resolveName))) {
409 return nullptr;
410 }
411 return jsThis;
412 }
413
CompilerJSCallCheck(void * fn)414 void *CompilerJSCallCheck(void *fn)
415 {
416 auto jsFn = ToLocal(fn);
417
418 auto coro = EtsCoroutine::GetCurrent();
419 auto ctx = InteropCtx::Current(coro);
420 napi_env env = ctx->GetJSEnv();
421
422 if (UNLIKELY(GetValueType(env, jsFn) != napi_function)) {
423 ctx->ThrowJSTypeError(env, "call target is not a function");
424 ctx->ForwardJSException(coro);
425 return nullptr;
426 }
427 return fn;
428 }
429
430 template <bool USE_RET>
CompilerJSCallFunction(void * obj,void * fn,uint32_t argc,void * args)431 std::conditional_t<USE_RET, void *, void> CompilerJSCallFunction(void *obj, void *fn, uint32_t argc, void *args)
432 {
433 auto jsThis = ToLocal(obj);
434 auto jsFn = ToLocal(fn);
435 auto jsArgs = reinterpret_cast<napi_value *>(args);
436 auto coro = EtsCoroutine::GetCurrent();
437 auto ctx = InteropCtx::Current(coro);
438 napi_env env = ctx->GetJSEnv();
439
440 [[maybe_unused]] napi_value jsRet;
441 napi_status jsStatus;
442 {
443 ctx->GetInteropFrames().push_back({coro->GetCurrentFrame(), true});
444 ScopedNativeCodeThread nativeScope(coro);
445 if constexpr (USE_RET) {
446 jsStatus = napi_call_function(env, jsThis, jsFn, argc, jsArgs, &jsRet);
447 } else {
448 jsStatus = napi_call_function(env, jsThis, jsFn, argc, jsArgs, nullptr);
449 }
450
451 ctx->GetInteropFrames().pop_back();
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 if constexpr (USE_RET) {
459 return nullptr;
460 }
461 }
462 if constexpr (USE_RET) {
463 return jsRet;
464 }
465 }
466
CompilerJSNewInstance(void * fn,uint32_t argc,void * args)467 void *CompilerJSNewInstance(void *fn, uint32_t argc, void *args)
468 {
469 auto jsFn = ToLocal(fn);
470 auto jsArgs = reinterpret_cast<napi_value *>(args);
471
472 auto coro = EtsCoroutine::GetCurrent();
473 auto ctx = InteropCtx::Current(coro);
474 napi_env env = ctx->GetJSEnv();
475
476 napi_value jsRet;
477 napi_status jsStatus;
478 {
479 ctx->GetInteropFrames().push_back({coro->GetCurrentFrame(), true});
480 ScopedNativeCodeThread nativeScope(coro);
481
482 jsStatus = napi_new_instance(env, jsFn, argc, jsArgs, &jsRet);
483
484 ctx->GetInteropFrames().pop_back();
485 }
486
487 if (UNLIKELY(jsStatus != napi_ok)) {
488 INTEROP_FATAL_IF(jsStatus != napi_pending_exception);
489 ctx->ForwardJSException(coro);
490 INTEROP_LOG(DEBUG) << "JSValueJSCall: exit with pending exception";
491 return nullptr;
492 }
493 return jsRet;
494 }
495
CreateLocalScope()496 void CreateLocalScope()
497 {
498 auto coro = EtsCoroutine::GetCurrent();
499 auto ctx = InteropCtx::Current(coro);
500 napi_env env = ctx->GetJSEnv();
501 auto scopesStorage = ctx->GetLocalScopesStorage();
502 ASSERT(coro->IsCurrentFrameCompiled());
503 scopesStorage->CreateLocalScope(env, coro->GetCurrentFrame());
504 }
505
CompilerDestroyLocalScope()506 void CompilerDestroyLocalScope()
507 {
508 auto coro = EtsCoroutine::GetCurrent();
509 auto ctx = InteropCtx::Current(coro);
510 napi_env env = ctx->GetJSEnv();
511 auto scopesStorage = ctx->GetLocalScopesStorage();
512 ASSERT(coro->IsCurrentFrameCompiled());
513 scopesStorage->DestroyTopLocalScope(env, coro->GetCurrentFrame());
514 }
515
516 template <typename CONVERTOR>
ConvertFromLocal(void * value)517 typename CONVERTOR::cpptype ConvertFromLocal(void *value)
518 {
519 auto coro = EtsCoroutine::GetCurrent();
520 auto ctx = InteropCtx::Current(coro);
521 napi_env env = ctx->GetJSEnv();
522 auto jsVal = ToLocal(value);
523 auto res = CONVERTOR::Unwrap(ctx, env, jsVal);
524 if (UNLIKELY(!res.has_value())) {
525 if (NapiIsExceptionPending(env)) {
526 ctx->ForwardJSException(coro);
527 }
528 ASSERT(ctx->SanityETSExceptionPending());
529 if constexpr (!std::is_pointer_v<typename CONVERTOR::cpptype>) {
530 return 0;
531 } else {
532 return nullptr;
533 }
534 }
535 return res.value();
536 }
537
538 template <>
ConvertFromLocal(void * value)539 JSValue *ConvertFromLocal<JSConvertJSValue>(void *value)
540 {
541 auto jsVal = ToLocal(value);
542
543 auto coro = EtsCoroutine::GetCurrent();
544 auto ctx = InteropCtx::Current(coro);
545 napi_env env = ctx->GetJSEnv();
546 if (UNLIKELY(IsNull(env, jsVal))) {
547 return nullptr;
548 }
549
550 auto res = JSConvertJSValue::Unwrap(ctx, env, jsVal);
551 if (UNLIKELY(!res.has_value())) {
552 if (NapiIsExceptionPending(env)) {
553 ctx->ForwardJSException(coro);
554 }
555 ASSERT(ctx->SanityETSExceptionPending());
556 return nullptr;
557 }
558 return res.value();
559 }
560
CompilerConvertVoidToLocal()561 void *CompilerConvertVoidToLocal()
562 {
563 auto coro = EtsCoroutine::GetCurrent();
564 auto ctx = InteropCtx::Current(coro);
565 napi_env env = ctx->GetJSEnv();
566 return GetUndefined(env);
567 }
568
569 template <typename T>
ConvertToLocal(typename T::cpptype etsValue)570 void *ConvertToLocal(typename T::cpptype etsValue)
571 {
572 auto coro = EtsCoroutine::GetCurrent();
573 auto ctx = InteropCtx::Current(coro);
574 napi_env env = ctx->GetJSEnv();
575 napi_value localJsValue = T::Wrap(env, etsValue);
576 if (UNLIKELY(localJsValue == nullptr)) {
577 ctx->ForwardJSException(coro);
578 return nullptr;
579 }
580 return localJsValue;
581 }
582
CompilerConvertRefTypeToLocal(EtsObject * etsValue)583 void *CompilerConvertRefTypeToLocal(EtsObject *etsValue)
584 {
585 auto coro = EtsCoroutine::GetCurrent();
586 auto ctx = InteropCtx::Current(coro);
587 napi_env env = ctx->GetJSEnv();
588
589 auto ref = etsValue->GetCoreType();
590 if (UNLIKELY(ref == nullptr)) {
591 return GetNull(env);
592 }
593
594 auto klass = ref->template ClassAddr<Class>();
595 // start fastpath
596 if (klass == ctx->GetJSValueClass()) {
597 auto value = JSValue::FromEtsObject(EtsObject::FromCoreType(ref));
598 napi_value res = JSConvertJSValue::Wrap(env, value);
599 if (UNLIKELY(res == nullptr)) {
600 ctx->ForwardJSException(coro);
601 }
602 return res;
603 }
604 if (klass == ctx->GetStringClass()) {
605 auto value = EtsString::FromEtsObject(EtsObject::FromCoreType(ref));
606 napi_value res = JSConvertString::Wrap(env, value);
607 if (UNLIKELY(res == nullptr)) {
608 ctx->ForwardJSException(coro);
609 }
610 return res;
611 }
612 // start slowpath
613 auto refconv = JSRefConvertResolve(ctx, klass);
614 auto res = refconv->Wrap(ctx, EtsObject::FromCoreType(ref));
615 if (UNLIKELY(res == nullptr)) {
616 ctx->ForwardJSException(coro);
617 }
618 return res;
619 }
620
CompilerConvertLocalToString(void * value)621 EtsString *CompilerConvertLocalToString(void *value)
622 {
623 auto jsVal = ToLocal(value);
624
625 auto coro = EtsCoroutine::GetCurrent();
626 auto ctx = InteropCtx::Current(coro);
627 napi_env env = ctx->GetJSEnv();
628 // NOTE(kprokopenko): can't assign undefined to EtsString *, see #14765
629 if (UNLIKELY(IsNullOrUndefined(env, jsVal))) {
630 return nullptr;
631 }
632
633 auto res = JSConvertString::Unwrap(ctx, env, jsVal);
634 if (UNLIKELY(!res.has_value())) {
635 if (NapiIsExceptionPending(env)) {
636 ctx->ForwardJSException(coro);
637 }
638 ASSERT(ctx->SanityETSExceptionPending());
639 return nullptr;
640 }
641 return res.value();
642 }
643
CompilerConvertLocalToRefType(void * klassPtr,void * value)644 EtsObject *CompilerConvertLocalToRefType(void *klassPtr, void *value)
645 {
646 auto jsVal = ToLocal(value);
647 auto klass = reinterpret_cast<Class *>(klassPtr);
648
649 auto coro = EtsCoroutine::GetCurrent();
650 auto ctx = InteropCtx::Current(coro);
651 napi_env env = ctx->GetJSEnv();
652 if (UNLIKELY(IsNull(env, jsVal))) {
653 return nullptr;
654 }
655
656 if (UNLIKELY(IsUndefined(env, jsVal))) {
657 return ctx->GetUndefinedObject();
658 }
659
660 // start slowpath
661 auto refconv = JSRefConvertResolve<true>(ctx, klass);
662 if (UNLIKELY(refconv == nullptr)) {
663 if (NapiIsExceptionPending(env)) {
664 ctx->ForwardJSException(coro);
665 }
666 ASSERT(ctx->SanityETSExceptionPending());
667 return nullptr;
668 }
669 ObjectHeader *res = refconv->Unwrap(ctx, jsVal)->GetCoreType();
670 if (UNLIKELY(res == nullptr)) {
671 if (NapiIsExceptionPending(env)) {
672 ctx->ForwardJSException(coro);
673 }
674 ASSERT(ctx->SanityETSExceptionPending());
675 }
676 return EtsObject::FromCoreType(res);
677 }
678
679 const IntrinsicsAPI G_INTRINSICS_API = {
680 JSRuntimeFinalizationQueueCallback,
681 JSRuntimeNewJSValueDouble,
682 JSRuntimeNewJSValueString,
683 JSRuntimeNewJSValueObject,
684 JSRuntimeGetValueDouble,
685 JSRuntimeGetValueBoolean,
686 JSRuntimeGetValueString,
687 JSRuntimeGetValueObject,
688 JSValueNamedGetter<JSConvertJSValue>,
689 JSValueNamedGetter<JSConvertF64>,
690 JSValueNamedGetter<JSConvertString>,
691 JSValueNamedSetter<JSConvertJSValue>,
692 JSValueNamedSetter<JSConvertF64>,
693 JSValueNamedSetter<JSConvertString>,
694 JSValueIndexedGetter<JSConvertJSValue>,
695 JSValueIndexedGetter<JSConvertF64>,
696 JSRuntimeGetUndefined,
697 JSRuntimeGetNull,
698 JSRuntimeGetGlobal,
699 JSRuntimeCreateObject,
700 JSRuntimeInstanceOf,
701 JSRuntimeInitJSCallClass,
702 JSRuntimeInitJSNewClass,
703 JSRuntimeCreateLambdaProxy,
704 JSRuntimeLoadModule,
705 JSRuntimeStrictEqual,
706 CompilerGetJSNamedProperty,
707 CompilerResolveQualifiedJSCall,
708 CompilerJSCallCheck,
709 CompilerJSCallFunction<true>,
710 CompilerJSCallFunction<false>,
711 CompilerJSNewInstance,
712 CreateLocalScope,
713 CompilerDestroyLocalScope,
714 CompilerConvertVoidToLocal,
715 ConvertToLocal<JSConvertU1>,
716 ConvertToLocal<JSConvertU8>,
717 ConvertToLocal<JSConvertI8>,
718 ConvertToLocal<JSConvertU16>,
719 ConvertToLocal<JSConvertI16>,
720 ConvertToLocal<JSConvertU32>,
721 ConvertToLocal<JSConvertI32>,
722 ConvertToLocal<JSConvertU64>,
723 ConvertToLocal<JSConvertI64>,
724 ConvertToLocal<JSConvertF32>,
725 ConvertToLocal<JSConvertF64>,
726 ConvertToLocal<JSConvertJSValue>,
727 CompilerConvertRefTypeToLocal,
728 ConvertFromLocal<JSConvertU1>,
729 ConvertFromLocal<JSConvertU8>,
730 ConvertFromLocal<JSConvertI8>,
731 ConvertFromLocal<JSConvertU16>,
732 ConvertFromLocal<JSConvertI16>,
733 ConvertFromLocal<JSConvertU32>,
734 ConvertFromLocal<JSConvertI32>,
735 ConvertFromLocal<JSConvertU64>,
736 ConvertFromLocal<JSConvertI64>,
737 ConvertFromLocal<JSConvertF32>,
738 ConvertFromLocal<JSConvertF64>,
739 ConvertFromLocal<JSConvertJSValue>,
740 ConvertFromLocal<JSConvertString>,
741 CompilerConvertLocalToRefType,
742 };
743
GetIntrinsicsAPI()744 const IntrinsicsAPI *GetIntrinsicsAPI()
745 {
746 return &G_INTRINSICS_API;
747 }
748
749 } // namespace panda::ets::interop::js
750