• 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/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