• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 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 "macros.h"
17 #include "runtime/mem/local_object_handle.h"
18 #include "plugins/ets/runtime/interop_js/call/call.h"
19 #include "plugins/ets/runtime/interop_js/call/arg_convertors.h"
20 #include "plugins/ets/runtime/interop_js/call/proto_reader.h"
21 #include "plugins/ets/runtime/interop_js/code_scopes.h"
22 #include "plugins/ets/runtime/ets_stubs-inl.h"
23 
24 namespace ark::ets::interop::js {
25 
CreateProxyBridgeArgReader(uint8_t * args,uint8_t * inStackArgs)26 static ALWAYS_INLINE inline arch::ArgReader<RUNTIME_ARCH> CreateProxyBridgeArgReader(uint8_t *args,
27                                                                                      uint8_t *inStackArgs)
28 {
29     Span<uint8_t> inGprArgs(args, arch::ExtArchTraits<RUNTIME_ARCH>::GP_ARG_NUM_BYTES);
30     Span<uint8_t> inFprArgs(inGprArgs.end(), arch::ExtArchTraits<RUNTIME_ARCH>::FP_ARG_NUM_BYTES);
31     return arch::ArgReader<RUNTIME_ARCH>(inGprArgs, inFprArgs, inStackArgs);
32 }
33 
34 class CallJSHandler {
35 public:
CallJSHandler(EtsCoroutine * coro,Method * method,uint8_t * args,uint8_t * inStackArgs)36     ALWAYS_INLINE CallJSHandler(EtsCoroutine *coro, Method *method, uint8_t *args, uint8_t *inStackArgs)
37         : coro_(coro),
38           ctx_(InteropCtx::Current(coro_)),
39           protoReader_(method, ctx_->GetClassLinker(), ctx_->LinkerCtx()),
40           argReader_(CreateProxyBridgeArgReader(args, inStackArgs))
41     {
42     }
43 
SetupArgreader(bool isInstance)44     ALWAYS_INLINE ObjectHeader *SetupArgreader(bool isInstance)
45     {
46         auto method = protoReader_.GetMethod();
47         protoReader_.Advance();                // skip return type
48         argReader_.template Read<Method *>();  // skip method
49         ASSERT(isInstance == !method->IsStatic());
50         numArgs_ = method->GetNumArgs() - static_cast<uint32_t>(isInstance);
51         return isInstance ? argReader_.Read<ObjectHeader *>() : nullptr;
52     }
53 
54     template <panda_file::Type::TypeId PF_TYPEID, typename T>
ReadFixedArg()55     ALWAYS_INLINE T ReadFixedArg()
56     {
57         ASSERT(protoReader_.GetType() == panda_file::Type(PF_TYPEID));
58         protoReader_.Advance();
59         numArgs_--;
60         return argReader_.Read<T>();
61     }
62 
63     template <typename T>
ReadFixedRefArg(Class * expected)64     ALWAYS_INLINE T *ReadFixedRefArg([[maybe_unused]] Class *expected)
65     {
66         ASSERT(expected == nullptr || protoReader_.GetClass() == expected);
67         return ReadFixedArg<panda_file::Type::TypeId::REFERENCE, T *>();
68     }
69 
SetupJSCallee(napi_value jsThis,napi_value jsFn)70     ALWAYS_INLINE void SetupJSCallee(napi_value jsThis, napi_value jsFn)
71     {
72         jsThis_ = jsThis;
73         jsFn_ = jsFn;
74     }
75 
76     template <bool IS_NEWCALL, typename ArgSetup>
HandleImpl(Method * method,uint8_t * args,uint8_t * inStackArgs)77     static ALWAYS_INLINE uint64_t HandleImpl(Method *method, uint8_t *args, uint8_t *inStackArgs)
78     {
79         CallJSHandler st(EtsCoroutine::GetCurrent(), method, args, inStackArgs);
80         return st.Handle<IS_NEWCALL, ArgSetup>();
81     }
82 
GetMethod()83     ALWAYS_INLINE Method *GetMethod()
84     {
85         return protoReader_.GetMethod();
86     }
87 
88     ~CallJSHandler() = default;
89 
90 private:
ForwardException(InteropCtx * ctx,EtsCoroutine * coro)91     static uint64_t __attribute__((noinline)) ForwardException(InteropCtx *ctx, EtsCoroutine *coro)
92     {
93         if (NapiIsExceptionPending(ctx->GetJSEnv())) {
94             ctx->ForwardJSException(coro);
95         }
96         ASSERT(ctx->SanityETSExceptionPending());
97         return 0;
98     }
99 
100     template <bool IS_NEWCALL, typename ArgSetup>
101     ALWAYS_INLINE uint64_t Handle();
102 
103     template <bool IS_NEWCALL>
104     ALWAYS_INLINE std::optional<napi_value> ConvertArgsAndCall();
105 
106     template <bool IS_NEWCALL, typename FRead>
107     ALWAYS_INLINE inline std::optional<napi_value> ConvertVarargsAndCall(FRead &readVal, Span<napi_value> jsargs);
108 
109     template <bool IS_NEWCALL>
110     ALWAYS_INLINE std::optional<napi_value> CallConverted(Span<napi_value> jsargs);
111 
112     template <bool IS_NEWCALL>
113     ALWAYS_INLINE std::optional<Value> ConvertRetval(napi_value jsRet);
114 
115     NO_COPY_SEMANTIC(CallJSHandler);
116     NO_MOVE_SEMANTIC(CallJSHandler);
117 
118     EtsCoroutine *const coro_;
119     InteropCtx *const ctx_;
120 
121     ProtoReader protoReader_;
122     arch::ArgReader<RUNTIME_ARCH> argReader_;
123     uint32_t numArgs_ {};
124     napi_value jsThis_ {};
125     napi_value jsFn_ {};
126 };
127 
128 template <bool IS_NEWCALL, typename ArgSetup>
Handle()129 ALWAYS_INLINE inline uint64_t CallJSHandler::Handle()
130 {
131     [[maybe_unused]] InteropCodeScopeETS codeScope(coro_, __PRETTY_FUNCTION__);
132     napi_env env = ctx_->GetJSEnv();
133     NapiScope jsHandleScope(env);
134 
135     if (UNLIKELY(!ArgSetup()(ctx_, this))) {
136         return ForwardException(ctx_, coro_);
137     }
138     if (UNLIKELY(GetValueType(env, jsFn_) != napi_function)) {
139         ctx_->ThrowJSTypeError(env, "call target is not a function");
140         return ForwardException(ctx_, coro_);
141     }
142 
143     std::optional<napi_value> jsRes = ConvertArgsAndCall<IS_NEWCALL>();
144     if (UNLIKELY(!jsRes.has_value())) {
145         return ForwardException(ctx_, coro_);
146     }
147     std::optional<Value> etsRes = ConvertRetval<IS_NEWCALL>(jsRes.value());
148     if (UNLIKELY(!etsRes.has_value())) {
149         return ForwardException(ctx_, coro_);
150     }
151     return static_cast<uint64_t>(etsRes.value().GetAsLong());
152 }
153 
154 template <bool IS_NEWCALL, typename FRead>
ConvertVarargsAndCall(FRead & readVal,Span<napi_value> jsargs)155 ALWAYS_INLINE inline std::optional<napi_value> CallJSHandler::ConvertVarargsAndCall(FRead &readVal,
156                                                                                     Span<napi_value> jsargs)
157 {
158     auto *ref = readVal(helpers::TypeIdentity<ObjectHeader *>());
159     auto *klass = ref->template ClassAddr<Class>();
160     ASSERT(klass->IsArrayClass());
161     LocalObjectHandle<coretypes::Array> etsArr(coro_, ref);
162     auto compTy = klass->GetComponentType();
163     auto refConv = JSRefConvertResolve(ctx_, compTy);
164 
165     auto allJsArgs = ctx_->GetTempArgs<napi_value>(etsArr->GetLength() + jsargs.size());
166     for (uint32_t el = 0; el < jsargs.size(); ++el) {
167         allJsArgs[el] = jsargs[el];
168     }
169     for (uint32_t el = 0; el < etsArr->GetLength(); ++el) {
170         auto *etsElem = EtsObject::FromCoreType(etsArr->Get<ObjectHeader *>(el));
171         allJsArgs[el + jsargs.size()] = refConv->Wrap(ctx_, etsElem);
172     }
173     return CallConverted<IS_NEWCALL>(*allJsArgs);
174 }
175 
176 template <bool IS_NEWCALL>
ConvertArgsAndCall()177 ALWAYS_INLINE inline std::optional<napi_value> CallJSHandler::ConvertArgsAndCall()
178 {
179     bool isVarArgs = UNLIKELY(protoReader_.GetMethod()->HasVarArgs());
180     auto readVal = [this](auto typeTag) { return argReader_.template Read<typename decltype(typeTag)::type>(); };
181     auto const numNonRest = numArgs_ - (isVarArgs ? 1 : 0);
182     auto jsargs = ctx_->GetTempArgs<napi_value>(numNonRest);
183 
184     for (uint32_t argIdx = 0; argIdx < numNonRest; ++argIdx, protoReader_.Advance()) {
185         if (UNLIKELY(!ConvertArgToJS(ctx_, protoReader_, &jsargs[argIdx], readVal))) {
186             return std::nullopt;
187         }
188     }
189 
190     if (isVarArgs) {
191         return ConvertVarargsAndCall<IS_NEWCALL>(readVal, *jsargs);
192     }
193 
194     return CallConverted<IS_NEWCALL>(*jsargs);
195 }
196 
197 template <bool IS_NEWCALL>
CallConverted(Span<napi_value> jsargs)198 ALWAYS_INLINE inline std::optional<napi_value> CallJSHandler::CallConverted(Span<napi_value> jsargs)
199 {
200     napi_env env = ctx_->GetJSEnv();
201     napi_value jsRetval;
202     napi_status jsStatus;
203     {
204         ScopedNativeCodeThread nativeScope(coro_);
205         if constexpr (IS_NEWCALL) {
206             jsStatus = napi_new_instance(env, jsFn_, jsargs.size(), jsargs.data(), &jsRetval);
207         } else {
208             jsStatus = napi_call_function(env, jsThis_, jsFn_, jsargs.size(), jsargs.data(), &jsRetval);
209         }
210     }
211     if (UNLIKELY(jsStatus != napi_ok)) {
212         INTEROP_FATAL_IF(jsStatus != napi_pending_exception);
213         return std::nullopt;
214     }
215     return jsRetval;
216 }
217 
218 template <bool IS_NEWCALL>
ConvertRetval(napi_value jsRet)219 ALWAYS_INLINE inline std::optional<Value> CallJSHandler::ConvertRetval(napi_value jsRet)
220 {
221     napi_env env = ctx_->GetJSEnv();
222     Value etsRet;
223     protoReader_.Reset();
224 
225     if constexpr (IS_NEWCALL) {
226         ASSERT(protoReader_.GetClass() == ctx_->GetJSValueClass());
227         auto res = JSConvertJSValue::Unwrap(ctx_, env, jsRet);
228         if (UNLIKELY(!res.has_value())) {
229             return std::nullopt;
230         }
231         etsRet = Value(res.value()->GetCoreType());
232     } else {
233         auto store = [&etsRet](auto val) { etsRet = Value(val); };
234         if (UNLIKELY(!ConvertArgToEts(ctx_, protoReader_, store, jsRet))) {
235             return std::nullopt;
236         }
237     }
238     return etsRet;
239 }
240 
ResolveQualifiedReceiverTarget(napi_env env,napi_value jsVal,coretypes::String * qnameStr)241 static std::optional<std::pair<napi_value, napi_value>> ResolveQualifiedReceiverTarget(napi_env env, napi_value jsVal,
242                                                                                        coretypes::String *qnameStr)
243 {
244     napi_value jsThis {};
245     ASSERT(qnameStr->IsMUtf8());
246     auto qname = std::string(utf::Mutf8AsCString(qnameStr->GetDataMUtf8()), qnameStr->GetMUtf8Length());
247 
248     auto resolveName = [&jsThis, &jsVal, &env](const std::string &name) -> bool {
249         jsThis = jsVal;
250         INTEROP_LOG(DEBUG) << "JSRuntimeCallJS: resolve name: " << name;
251         napi_status rc = napi_get_named_property(env, jsVal, name.c_str(), &jsVal);
252         if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
253             ASSERT(NapiIsExceptionPending(env));
254             return false;
255         }
256         return true;
257     };
258     jsThis = jsVal;
259     if (UNLIKELY(!WalkQualifiedName(qname, resolveName))) {
260         return std::nullopt;
261     }
262     return std::make_pair(jsThis, jsVal);
263 }
264 
265 template <bool IS_NEWCALL>
JSRuntimeCallJSQNameBase(Method * method,uint8_t * args,uint8_t * inStackArgs)266 static ALWAYS_INLINE inline uint64_t JSRuntimeCallJSQNameBase(Method *method, uint8_t *args, uint8_t *inStackArgs)
267 {
268     struct ArgSetup {
269         ALWAYS_INLINE bool operator()(InteropCtx *ctx, CallJSHandler *st)
270         {
271             st->SetupArgreader(false);
272             napi_env env = ctx->GetJSEnv();
273 
274             napi_value jsVal = JSConvertJSValue::Wrap(env, st->ReadFixedRefArg<JSValue>(ctx->GetJSValueClass()));
275             auto qnameStr = st->ReadFixedRefArg<coretypes::String>(ctx->GetStringClass());
276 
277             auto res = ResolveQualifiedReceiverTarget(env, jsVal, qnameStr);
278             if (UNLIKELY(!res.has_value())) {
279                 ASSERT(NapiIsExceptionPending(env));
280                 return false;
281             }
282 
283             st->SetupJSCallee(res->first, res->second);
284             return true;
285         }
286     };
287     return CallJSHandler::HandleImpl<IS_NEWCALL, ArgSetup>(method, args, inStackArgs);
288 }
289 
JSRuntimeCallJSQName(Method * method,uint8_t * args,uint8_t * inStackArgs)290 extern "C" uint64_t JSRuntimeCallJSQName(Method *method, uint8_t *args, uint8_t *inStackArgs)
291 {
292     return JSRuntimeCallJSQNameBase<false>(method, args, inStackArgs);  // IS_NEWCALL is false
293 }
294 extern "C" void JSRuntimeCallJSQNameBridge(Method *method, ...);
295 
JSRuntimeNewCallJSQName(Method * method,uint8_t * args,uint8_t * inStackArgs)296 extern "C" uint64_t JSRuntimeNewCallJSQName(Method *method, uint8_t *args, uint8_t *inStackArgs)
297 {
298     return JSRuntimeCallJSQNameBase<true>(method, args, inStackArgs);  // IS_NEWCALL is true
299 }
300 extern "C" void JSRuntimeNewCallJSQNameBridge(Method *method, ...);
301 
GetClassQnameOffset(InteropCtx * ctx,Method * method)302 static uint32_t GetClassQnameOffset(InteropCtx *ctx, Method *method)
303 {
304     auto klass = method->GetClass();
305     ctx->GetConstStringStorage()->LoadDynamicCallClass(klass);
306     auto fields = klass->GetStaticFields();
307     ASSERT(fields.size() == 1);
308     return klass->GetFieldPrimitive<uint32_t>(fields[0]);
309 }
310 
311 template <bool IS_NEWCALL>
JSRuntimeCallJSBase(Method * method,uint8_t * args,uint8_t * inStackArgs)312 static ALWAYS_INLINE inline uint64_t JSRuntimeCallJSBase(Method *method, uint8_t *args, uint8_t *inStackArgs)
313 {
314     struct ArgSetup {
315         ALWAYS_INLINE bool operator()(InteropCtx *ctx, CallJSHandler *st)
316         {
317             st->SetupArgreader(false);
318             napi_env env = ctx->GetJSEnv();
319 
320             napi_value jsVal = JSConvertJSValue::Wrap(env, st->ReadFixedRefArg<JSValue>(ctx->GetJSValueClass()));
321 
322             auto classQnameOffset = GetClassQnameOffset(ctx, st->GetMethod());
323             auto qnameStart = st->ReadFixedArg<panda_file::Type::TypeId::I32, int32_t>() + classQnameOffset;
324             auto qnameLen = st->ReadFixedArg<panda_file::Type::TypeId::I32, int32_t>();
325             napi_value jsThis {};
326 
327             auto success = ctx->GetConstStringStorage()->EnumerateStrings(
328                 qnameStart, qnameLen, [&jsThis, &jsVal, env](napi_value jsStr) {
329                     jsThis = jsVal;
330                     napi_status rc = napi_get_property(env, jsVal, jsStr, &jsVal);
331                     if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
332                         ASSERT(NapiIsExceptionPending(env));
333                         return false;
334                     }
335                     return true;
336                 });
337 
338             if (!success) {
339                 ASSERT(NapiIsExceptionPending(env));
340                 return false;
341             }
342             st->SetupJSCallee(jsThis, jsVal);
343             return true;
344         }
345     };
346     return CallJSHandler::HandleImpl<IS_NEWCALL, ArgSetup>(method, args, inStackArgs);
347 }
348 
JSRuntimeCallJS(Method * method,uint8_t * args,uint8_t * inStackArgs)349 extern "C" uint64_t JSRuntimeCallJS(Method *method, uint8_t *args, uint8_t *inStackArgs)
350 {
351     return JSRuntimeCallJSBase<false>(method, args, inStackArgs);  // IS_NEWCALL is false
352 }
353 extern "C" void JSRuntimeCallJSBridge(Method *method, ...);
354 
JSRuntimeNewCallJS(Method * method,uint8_t * args,uint8_t * inStackArgs)355 extern "C" uint64_t JSRuntimeNewCallJS(Method *method, uint8_t *args, uint8_t *inStackArgs)
356 {
357     return JSRuntimeCallJSBase<true>(method, args, inStackArgs);  // IS_NEWCALL is true
358 }
359 extern "C" void JSRuntimeNewCallJSBridge(Method *method, ...);
360 
JSRuntimeCallJSByValue(Method * method,uint8_t * args,uint8_t * inStackArgs)361 extern "C" uint64_t JSRuntimeCallJSByValue(Method *method, uint8_t *args, uint8_t *inStackArgs)
362 {
363     struct ArgSetup {
364         ALWAYS_INLINE bool operator()(InteropCtx *ctx, CallJSHandler *st)
365         {
366             st->SetupArgreader(false);
367             napi_env env = ctx->GetJSEnv();
368 
369             napi_value jsFn = JSConvertJSValue::Wrap(env, st->ReadFixedRefArg<JSValue>(ctx->GetJSValueClass()));
370             napi_value jsThis = JSConvertJSValue::Wrap(env, st->ReadFixedRefArg<JSValue>(ctx->GetJSValueClass()));
371 
372             st->SetupJSCallee(jsThis, jsFn);
373             return true;
374         }
375     };
376     return CallJSHandler::HandleImpl<false, ArgSetup>(method, args, inStackArgs);
377 }
378 extern "C" void JSRuntimeCallJSByValueBridge(Method *method, ...);
379 
CallJSProxy(Method * method,uint8_t * args,uint8_t * inStackArgs)380 extern "C" uint64_t CallJSProxy(Method *method, uint8_t *args, uint8_t *inStackArgs)
381 {
382     struct ArgSetup {
383         ALWAYS_INLINE bool operator()(InteropCtx *ctx, CallJSHandler *st)
384         {
385             ObjectHeader *etsThis = st->SetupArgreader(true);
386             napi_env env = ctx->GetJSEnv();
387 
388             auto refconv = JSRefConvertResolve(ctx, etsThis->ClassAddr<Class>());
389             napi_value jsThis = refconv->Wrap(ctx, EtsObject::FromCoreType(etsThis));
390             ASSERT(GetValueType(env, jsThis) == napi_object);
391             const char *methodName = utf::Mutf8AsCString(st->GetMethod()->GetName().data);
392             napi_value jsFn;
393             napi_status rc = napi_get_named_property(env, jsThis, methodName, &jsFn);
394             if (UNLIKELY(rc == napi_object_expected || NapiThrownGeneric(rc))) {
395                 ASSERT(NapiIsExceptionPending(env));
396                 return false;
397             }
398 
399             st->SetupJSCallee(jsThis, jsFn);
400             return true;
401         }
402     };
403     return CallJSHandler::HandleImpl<false, ArgSetup>(method, args, inStackArgs);
404 }
405 extern "C" void CallJSProxyBridge(Method *method, ...);
406 
SelectCallJSEntrypoint(InteropCtx * ctx,Method * method)407 static void *SelectCallJSEntrypoint(InteropCtx *ctx, Method *method)
408 {
409     ASSERT(method->IsStatic());
410     ProtoReader protoReader(method, ctx->GetClassLinker(), ctx->LinkerCtx());
411 
412     // skip return type
413     protoReader.Advance();
414 
415     ASSERT(protoReader.GetClass() == ctx->GetJSValueClass());
416     protoReader.Advance();
417 
418     if (protoReader.GetType().IsPrimitive()) {
419         if (protoReader.GetType().GetId() != panda_file::Type::TypeId::I32) {
420             return nullptr;
421         }
422         protoReader.Advance();
423 
424         if (protoReader.GetType().GetId() != panda_file::Type::TypeId::I32) {
425             return nullptr;
426         }
427         return reinterpret_cast<void *>(JSRuntimeCallJSBridge);
428     }
429     if (protoReader.GetClass() == ctx->GetStringClass()) {
430         return reinterpret_cast<void *>(JSRuntimeCallJSQNameBridge);
431     }
432     if (protoReader.GetClass() == ctx->GetJSValueClass()) {
433         return reinterpret_cast<void *>(JSRuntimeCallJSByValueBridge);
434     }
435     InteropFatal("Bad jscall signature");
436 }
437 
SelectNewCallJSEntrypoint(InteropCtx * ctx,Method * method)438 static void *SelectNewCallJSEntrypoint(InteropCtx *ctx, Method *method)
439 {
440     ASSERT(method->IsStatic());
441     ProtoReader protoReader(method, ctx->GetClassLinker(), ctx->LinkerCtx());
442 
443     if (protoReader.GetClass() != ctx->GetJSValueClass()) {
444         return nullptr;
445     }
446     protoReader.Advance();
447 
448     if (protoReader.GetClass() != ctx->GetJSValueClass()) {
449         return nullptr;
450     }
451     protoReader.Advance();
452 
453     if (protoReader.GetType().IsPrimitive()) {
454         if (protoReader.GetType().GetId() != panda_file::Type::TypeId::I32) {
455             return nullptr;
456         }
457         protoReader.Advance();
458 
459         if (protoReader.GetType().GetId() != panda_file::Type::TypeId::I32) {
460             return nullptr;
461         }
462         return reinterpret_cast<void *>(JSRuntimeNewCallJSBridge);
463     }
464     if (protoReader.GetClass() != ctx->GetStringClass()) {
465         return nullptr;
466     }
467     return reinterpret_cast<void *>(JSRuntimeNewCallJSQNameBridge);
468 }
469 
GetQnameCount(Class * klass)470 static std::optional<uint32_t> GetQnameCount(Class *klass)
471 {
472     auto pf = klass->GetPandaFile();
473     panda_file::ClassDataAccessor cda(*pf, klass->GetFileId());
474     auto qnameCount =
475         cda.EnumerateAnnotation("Lets/annotation/DynamicCall;", [pf](panda_file::AnnotationDataAccessor &ada) {
476             for (uint32_t i = 0; i < ada.GetCount(); i++) {
477                 auto adae = ada.GetElement(i);
478                 auto *elemName = pf->GetStringData(adae.GetNameId()).data;
479                 if (utf::IsEqual(utf::CStringAsMutf8("value"), elemName)) {
480                     return adae.GetArrayValue().GetCount();
481                 }
482             }
483             UNREACHABLE();
484         });
485     return qnameCount;
486 }
487 
InitCallJSClass(bool isNewCall)488 static uint8_t InitCallJSClass(bool isNewCall)
489 {
490     auto coro = EtsCoroutine::GetCurrent();
491     auto ctx = InteropCtx::Current(coro);
492     auto *klass = GetMethodOwnerClassInFrames(coro, 0)->GetRuntimeClass();
493     INTEROP_LOG(DEBUG) << "Bind bridge call methods for " << utf::Mutf8AsCString(klass->GetDescriptor());
494 
495     for (auto &method : klass->GetMethods()) {
496         if (method.IsConstructor()) {
497             continue;
498         }
499         void *ep = nullptr;
500         if (method.IsStatic()) {
501             ep = isNewCall ? SelectNewCallJSEntrypoint(ctx, &method) : SelectCallJSEntrypoint(ctx, &method);
502         }
503         if (ep == nullptr) {
504             InteropFatal("Bad interop call bridge signature");
505         }
506         method.SetCompiledEntryPoint(ep);
507         method.SetNativePointer(nullptr);
508     }
509 
510     auto qnameCount = GetQnameCount(klass);
511     // JSCallClass which was manually created in test will not have the required annotation and field
512     if (qnameCount.has_value()) {
513         auto fields = klass->GetStaticFields();
514         INTEROP_FATAL_IF(fields.Size() != 1);
515         INTEROP_FATAL_IF(klass->GetFieldPrimitive<uint32_t>(fields[0]) != 0);
516         klass->SetFieldPrimitive<uint32_t>(fields[0], ctx->AllocateSlotsInStringBuffer(*qnameCount));
517     }
518     return 1;
519 }
520 
JSRuntimeInitJSCallClass()521 uint8_t JSRuntimeInitJSCallClass()
522 {
523     return InitCallJSClass(false);
524 }
525 
JSRuntimeInitJSNewClass()526 uint8_t JSRuntimeInitJSNewClass()
527 {
528     return InitCallJSClass(true);
529 }
530 
531 }  // namespace ark::ets::interop::js
532