• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-2025 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 #ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_CONTEXT_H_
17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_CONTEXT_H_
18 
19 #include "ets_platform_types.h"
20 #include "plugins/ets/runtime/ets_coroutine.h"
21 #include "plugins/ets/runtime/ets_vm.h"
22 #include "plugins/ets/runtime/interop_js/app_state_manager.h"
23 #include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h"
24 #include "plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h"
25 #include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h"
26 #include "plugins/ets/runtime/interop_js/js_job_queue.h"
27 #include "plugins/ets/runtime/interop_js/js_refconvert.h"
28 #include "plugins/ets/runtime/interop_js/interop_stacks.h"
29 #include "plugins/ets/runtime/interop_js/intrinsics/std_js_jsruntime.h"
30 #include "runtime/include/value.h"
31 
32 #include "plugins/ets/runtime/interop_js/stack_info.h"
33 
34 #include "hybrid/sts_vm_interface.h"
35 #include "hybrid/ecma_vm_interface.h"
36 
37 #include "plugins/ets/runtime/interop_js/xgc/xgc_vm_adaptor.h"
38 
39 #include <node_api.h>
40 #include <unordered_map>
41 
42 namespace ark {
43 
44 class Class;
45 class ClassLinkerContext;
46 
47 namespace mem {
48 class GlobalObjectStorage;
49 class Reference;
50 }  // namespace mem
51 
52 }  // namespace ark
53 
54 namespace ark::ets::interop::js {
55 
56 namespace testing {
57 class SharedReferenceStorage1GTest;
58 }  // namespace testing
59 
60 class JSValue;
61 
62 // Work-around for String JSValue and node_api
63 class JSValueStringStorage {
64 public:
65     class CachedEntry {
66     public:
Data()67         std::string const *Data()
68         {
69             return data_;
70         }
71 
72     private:
73         friend class JSValue;
74         friend class JSValueStringStorage;
75 
CachedEntry(std::string const * data)76         explicit CachedEntry(std::string const *data) : data_(data) {}
77 
78         std::string const *data_ {};
79     };
80 
81     explicit JSValueStringStorage() = default;
82 
Get(std::string && str)83     CachedEntry Get(std::string &&str)
84     {
85         auto [it, inserted] = stringTab_.try_emplace(std::move(str), 0);
86         it->second++;
87         return CachedEntry(&it->first);
88     }
89 
Release(CachedEntry str)90     void Release(CachedEntry str)
91     {
92         auto it = stringTab_.find(*str.Data());
93         ASSERT(it != stringTab_.end());
94         if (--(it->second) == 0) {
95             stringTab_.erase(it);
96         }
97     }
98 
99 private:
100     std::unordered_map<std::string, uint64_t> stringTab_;
101 };
102 
103 class ConstStringStorage {
104 public:
ConstStringStorage(InteropCtx * ctx)105     explicit ConstStringStorage(InteropCtx *ctx) : ctx_(ctx) {}
106 
107     void LoadDynamicCallClass(Class *klass);
108 
109     template <typename Callback>
110     bool EnumerateStrings(size_t startFrom, size_t count, Callback cb);
111 
112     napi_value GetConstantPool();
113 
AllocateSlotsInStringBuffer(uint32_t count)114     uint32_t AllocateSlotsInStringBuffer(uint32_t count)
115     {
116         // Atomic with relaxed order reason: ordering constraints are not required
117         return qnameBufferSize_.fetch_add(count, std::memory_order_relaxed);
118     }
119 
120 protected:
GetStringBufferSize()121     uint32_t GetStringBufferSize()
122     {
123         // Atomic with relaxed order reason: ordering constraints are not required
124         return qnameBufferSize_.load(std::memory_order_relaxed);
125     }
126 
127 private:
128     napi_value InitBuffer(size_t length);
129     InteropCtx *Ctx();
130 
131 private:
132     std::vector<napi_value> stringBuffer_ {};
133     napi_ref jsStringBufferRef_ {};
134     Class *lastLoadedClass_ {};
135     InteropCtx *ctx_ = nullptr;
136     static std::atomic_uint32_t qnameBufferSize_;
137 };
138 
139 class CommonJSObjectCache {  // NOLINT(cppcoreguidelines-special-member-functions)
140 public:
141     explicit CommonJSObjectCache(InteropCtx *ctx);
142     ~CommonJSObjectCache();
143 
144     napi_value GetProxy() const;
145 
146 private:
147     void InitializeCache();
148 
149     InteropCtx *ctx_ = nullptr;
150     napi_ref proxyRef_ {};
151 };
152 
153 class InteropCtx final {
154 public:
155     NO_COPY_SEMANTIC(InteropCtx);
156     NO_MOVE_SEMANTIC(InteropCtx);
157 
158     PANDA_PUBLIC_API static void Init(EtsCoroutine *coro, napi_env env);
159     virtual ~InteropCtx();
160 
161     static void Destroy(void *ptr);
162 
Current(Coroutine * coro)163     static InteropCtx *Current(Coroutine *coro)
164     {
165         // NOTE(konstanting): we may want to optimize this and take the cached pointer from the coroutine
166         // itself. Just need to make sure that the worker's interop context ptr state is coherent
167         // with coroutine's.
168         ASSERT(coro != nullptr);
169         auto *w = coro->GetWorker();
170         return Current(w);
171     }
172 
Current()173     static InteropCtx *Current()
174     {
175         return Current(Coroutine::GetCurrent());
176     }
177 
GetPandaEtsVM()178     const PandaEtsVM *GetPandaEtsVM() const
179     {
180         return sharedEtsVmState_->pandaEtsVm;
181     }
182 
GetJSEnv()183     napi_env GetJSEnv() const
184     {
185         ASSERT(jsEnv_ != nullptr);
186         return jsEnv_;
187     }
188 
Refstor()189     mem::GlobalObjectStorage *Refstor() const
190     {
191         return sharedEtsVmState_->pandaEtsVm->GetGlobalObjectStorage();
192     }
193 
GetClassLinker()194     ClassLinker *GetClassLinker() const
195     {
196         // NOTE(konstanting): do we REALLY need this method here?
197         return Runtime::GetCurrent()->GetClassLinker();
198     }
199 
LinkerCtx()200     ClassLinkerContext *LinkerCtx() const
201     {
202         return SharedEtsVmState::linkerCtx_;
203     }
204 
GetStringStor()205     JSValueStringStorage *GetStringStor()
206     {
207         return &jsValueStringStor_;
208     }
209 
GetLocalScopesStorage()210     LocalScopesStorage *GetLocalScopesStorage()
211     {
212         return &localScopesStorage_;
213     }
214 
DestroyLocalScopeForTopFrame(Frame * frame)215     void DestroyLocalScopeForTopFrame(Frame *frame)
216     {
217         GetLocalScopesStorage()->DestroyLocalScopeForTopFrame(jsEnv_, frame);
218     }
219 
GetJSValueFinalizationRegistry()220     mem::Reference *GetJSValueFinalizationRegistry() const
221     {
222         return jsvalueFregistryRef_;
223     }
224 
GetRegisterFinalizerMethod()225     Method *GetRegisterFinalizerMethod() const
226     {
227         return jsvalueFregistryRegister_;
228     }
229 
GetCommonJSObjectCache()230     CommonJSObjectCache *GetCommonJSObjectCache()
231     {
232         return &commonJSObjectCache_;
233     }
234 
235     // NOTE(vpukhov): implement in native code
PushOntoFinalizationRegistry(EtsCoroutine * coro,EtsObject * obj,EtsObject * cbarg)236     [[nodiscard]] bool PushOntoFinalizationRegistry(EtsCoroutine *coro, EtsObject *obj, EtsObject *cbarg)
237     {
238         auto queue = Refstor()->Get(jsvalueFregistryRef_);
239         std::array<Value, 4U> args = {Value(queue), Value(obj->GetCoreType()), Value(cbarg->GetCoreType()),
240                                       Value(static_cast<ObjectHeader *>(nullptr))};
241         jsvalueFregistryRegister_->Invoke(coro, args.data());
242         return !coro->HasPendingException();
243     }
244 
245     // NOTE(vpukhov): replace with stack-like allocator
246     template <typename T, size_t OPT_SZ>
247     struct TempArgs {
248     public:
TempArgsTempArgs249         explicit TempArgs(size_t sz)
250         {
251             if (LIKELY(sz <= OPT_SZ)) {
252                 sp_ = {new (inlArr_.data()) T[sz], sz};
253             } else {
254                 sp_ = {new T[sz], sz};
255             }
256         }
257 
~TempArgsTempArgs258         ~TempArgs()
259         {
260             if (UNLIKELY(static_cast<void *>(sp_.data()) != inlArr_.data())) {
261                 delete[] sp_.data();
262             } else {
263                 static_assert(std::is_trivially_destructible_v<T>);
264             }
265         }
266 
267         NO_COPY_SEMANTIC(TempArgs);
268 #if defined(__clang__)
269         NO_MOVE_SEMANTIC(TempArgs);
270 #elif defined(__GNUC__)  // RVO bug
271         NO_MOVE_OPERATOR(TempArgs);
TempArgsTempArgs272         TempArgs(TempArgs &&other) : sp_(other.sp_), inlArr_(other.inlArr_)
273         {
274             if (LIKELY(sp_.size() <= OPT_SZ)) {
275                 sp_ = Span<T>(reinterpret_cast<T *>(inlArr_.data()), sp_.size());
276             }
277         }
278 #endif
279 
280         Span<T> &operator*()
281         {
282             return sp_;
283         }
284         Span<T> *operator->()
285         {
286             return &sp_;
287         }
288         T &operator[](size_t idx)
289         {
290             return sp_[idx];
291         }
292 
293     private:
294         Span<T> sp_;
295         alignas(T) std::array<uint8_t, sizeof(T) * OPT_SZ> inlArr_;
296     };
297 
298     template <typename T, size_t OPT_SZ = 8U>
GetTempArgs(size_t sz)299     ALWAYS_INLINE static auto GetTempArgs(size_t sz)
300     {
301         return TempArgs<T, OPT_SZ>(sz);
302     }
303 
CallStack()304     InteropCallStack &CallStack()
305     {
306         return interopStk_;
307     }
308 
GetRefConvertCache()309     JSRefConvertCache *GetRefConvertCache()
310     {
311         return &refconvertCache_;
312     }
313 
GetJSRuntimeClass()314     Class *GetJSRuntimeClass() const
315     {
316         return sharedEtsVmState_->jsRuntimeClass;
317     }
318 
GetJSValueClass()319     Class *GetJSValueClass() const
320     {
321         return sharedEtsVmState_->jsValueClass;
322     }
323 
GetESErrorClass()324     Class *GetESErrorClass() const
325     {
326         return sharedEtsVmState_->esErrorClass;
327     }
328 
GetObjectClass()329     Class *GetObjectClass() const
330     {
331         return sharedEtsVmState_->objectClass;
332     }
333 
GetStringClass()334     Class *GetStringClass() const
335     {
336         return sharedEtsVmState_->stringClass;
337     }
338 
GetBigIntClass()339     Class *GetBigIntClass() const
340     {
341         return sharedEtsVmState_->bigintClass;
342     }
343 
GetArrayAsListIntClass()344     Class *GetArrayAsListIntClass() const
345     {
346         return sharedEtsVmState_->arrayAsListIntClass;
347     }
348 
GetNullValueClass()349     Class *GetNullValueClass() const
350     {
351         return sharedEtsVmState_->nullValueClass;
352     }
353 
GetPromiseClass()354     Class *GetPromiseClass() const
355     {
356         return sharedEtsVmState_->promiseClass;
357     }
358 
GetPromiseInteropConnectMethod()359     Method *GetPromiseInteropConnectMethod() const
360     {
361         return sharedEtsVmState_->promiseInteropConnectMethod;
362     }
363 
GetErrorClass()364     Class *GetErrorClass() const
365     {
366         return sharedEtsVmState_->errorClass;
367     }
368 
GetExceptionClass()369     Class *GetExceptionClass() const
370     {
371         return sharedEtsVmState_->exceptionClass;
372     }
373 
GetTypeClass()374     Class *GetTypeClass() const
375     {
376         return sharedEtsVmState_->typeClass;
377     }
378 
GetBoxIntClass()379     Class *GetBoxIntClass() const
380     {
381         return sharedEtsVmState_->boxIntClass;
382     }
383 
GetBoxLongClass()384     Class *GetBoxLongClass() const
385     {
386         return sharedEtsVmState_->boxLongClass;
387     }
388 
GetArrayClass()389     Class *GetArrayClass() const
390     {
391         return sharedEtsVmState_->arrayClass;
392     }
393 
IsFunctionalInterface(Class * klass)394     bool IsFunctionalInterface(Class *klass) const
395     {
396         return sharedEtsVmState_->functionalInterfaces.count(klass) > 0;
397     }
398 
GetJsProxyInstance(EtsClass * cls)399     js_proxy::JSProxy *GetJsProxyInstance(EtsClass *cls) const
400     {
401         return sharedEtsVmState_->GetJsProxyInstance(cls);
402     }
403 
SetJsProxyInstance(EtsClass * cls,js_proxy::JSProxy * proxy)404     void SetJsProxyInstance(EtsClass *cls, js_proxy::JSProxy *proxy)
405     {
406         sharedEtsVmState_->SetJsProxyInstance(cls, proxy);
407     }
408 
GetInterfaceProxyInstance(std::string & interfaceName)409     js_proxy::JSProxy *GetInterfaceProxyInstance(std::string &interfaceName) const
410     {
411         return sharedEtsVmState_->GetInterfaceProxyInstance(interfaceName);
412     }
413 
SetInterfaceProxyInstance(std::string & interfaceName,js_proxy::JSProxy * proxy)414     void SetInterfaceProxyInstance(std::string &interfaceName, js_proxy::JSProxy *proxy)
415     {
416         sharedEtsVmState_->SetInterfaceProxyInstance(interfaceName, proxy);
417     }
418 
419     EtsObject *CreateETSCoreESError(EtsCoroutine *coro, JSValue *jsvalue);
420 
421     static void ThrowETSError(EtsCoroutine *coro, napi_value val);
422     static void ThrowETSError(EtsCoroutine *coro, const char *msg);
ThrowETSError(EtsCoroutine * coro,const std::string & msg)423     static void ThrowETSError(EtsCoroutine *coro, const std::string &msg)
424     {
425         ThrowETSError(coro, msg.c_str());
426     }
427 
428     PANDA_PUBLIC_API static void ThrowJSError(napi_env env, const std::string &msg);
429     static void ThrowJSTypeError(napi_env env, const std::string &msg);
430     static void ThrowJSValue(napi_env env, napi_value val);
431     static napi_value CreateJSTypeError(napi_env env, const std::string &code, const std::string &msg);
432 
433     static void InitializeDefaultLinkerCtxIfNeeded(EtsRuntimeLinker *linker);
434     static void SetDefaultLinkerContext(EtsRuntimeLinker *linker);
435 
436     void ForwardEtsException(EtsCoroutine *coro);
437     void ForwardJSException(EtsCoroutine *coro);
438 
SanityETSExceptionPending()439     [[nodiscard]] static bool SanityETSExceptionPending()
440     {
441         auto coro = EtsCoroutine::GetCurrent();
442         auto env = InteropCtx::Current(coro)->GetJSEnv();
443         return coro->HasPendingException() && !NapiIsExceptionPending(env);
444     }
445 
SanityJSExceptionPending()446     [[nodiscard]] static bool SanityJSExceptionPending()
447     {
448         auto coro = EtsCoroutine::GetCurrent();
449         auto env = InteropCtx::Current(coro)->GetJSEnv();
450         return !coro->HasPendingException() && NapiIsExceptionPending(env);
451     }
452 
453     // Die and print execution stacks
454     [[noreturn]] PANDA_PUBLIC_API static void Fatal(const char *msg);
Fatal(const std::string & msg)455     [[noreturn]] static void Fatal(const std::string &msg)
456     {
457         Fatal(msg.c_str());
458     }
459 
SetPendingNewInstance(EtsHandle<EtsObject> handle)460     void SetPendingNewInstance(EtsHandle<EtsObject> handle)
461     {
462         pendingNewInstance_ = handle;
463     }
464 
AcquirePendingNewInstance()465     EtsObject *AcquirePendingNewInstance()
466     {
467         auto res = pendingNewInstance_.GetPtr();
468         pendingNewInstance_ = EtsHandle<EtsObject>();
469         return res;
470     }
471 
GetEtsMethodWrappersCache()472     ets_proxy::EtsMethodWrappersCache *GetEtsMethodWrappersCache()
473     {
474         return &etsMethodWrappersCache_;
475     }
476 
GetEtsClassWrappersCache()477     ets_proxy::EtsClassWrappersCache *GetEtsClassWrappersCache()
478     {
479         return &etsClassWrappersCache_;
480     }
481 
GetSharedRefStorage()482     ets_proxy::SharedReferenceStorage *GetSharedRefStorage()
483     {
484         return sharedEtsVmState_->etsProxyRefStorage.get();
485     }
486 
GetNullValue()487     EtsObject *GetNullValue() const
488     {
489         return EtsObject::FromCoreType(GetPandaEtsVM()->GetNullValue());
490     }
491 
GetConstStringStorage()492     ConstStringStorage *GetConstStringStorage()
493     {
494         return &constStringStorage_;
495     }
496 
497     // NOTE(konstanting, #23205): revert to ALWAYS_INLINE once the migration of ets_vm_plugin.cpp to ANI is completed
UpdateInteropStackInfoIfNeeded()498     PANDA_PUBLIC_API void UpdateInteropStackInfoIfNeeded()
499     {
500         stackInfoManager_.UpdateStackInfoIfNeeded();
501     }
502 
GetXGCVmAdaptor()503     XGCVmAdaptor *GetXGCVmAdaptor() const
504     {
505         return ecmaVMIterfaceAdaptor_.get();
506     }
507 
508     template <class EcmaVMAdaptorClass, class... Args>
CreateXGCVmAdaptor(Args &&...args)509     void CreateXGCVmAdaptor(Args &&...args)
510     {
511         static_assert(std::is_base_of_v<XGCVmAdaptor, EcmaVMAdaptorClass>);
512         static_assert(std::is_constructible_v<EcmaVMAdaptorClass, Args...>);
513         ecmaVMIterfaceAdaptor_ = MakePandaUnique<EcmaVMAdaptorClass>(std::forward<Args>(args)...);
514     }
515 
GetSTSVMInterface()516     arkplatform::STSVMInterface *GetSTSVMInterface() const
517     {
518         return sharedEtsVmState_->stsVMInterface.get();
519     }
520 
521 protected:
Current(CoroutineWorker * worker)522     static InteropCtx *Current(CoroutineWorker *worker)
523     {
524         return worker->GetLocalStorage().Get<CoroutineWorker::DataIdx::INTEROP_CTX_PTR, InteropCtx *>();
525     }
526 
527 private:
528     explicit InteropCtx(EtsCoroutine *coro, napi_env env);
529     void InitJsValueFinalizationRegistry(EtsCoroutine *coro);
530     void InitExternalInterfaces();
531 
532     void VmHandshake(napi_env env, EtsCoroutine *coro, arkplatform::STSVMInterface *stsVmIface);
533 
SetJSEnv(napi_env env)534     void SetJSEnv(napi_env env)
535     {
536         jsEnv_ = env;
537     }
538 
539     // per-EtsVM data, should be shared between contexts.
540     // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
541     struct SharedEtsVmState {
542         NO_COPY_SEMANTIC(SharedEtsVmState);
543         NO_MOVE_SEMANTIC(SharedEtsVmState);
544 
545         static std::shared_ptr<SharedEtsVmState> GetInstance(PandaEtsVM *vm);
546         // should be called when we would like to check if there are no more InteropCtx instances left
547         static void TryReleaseInstance();
548         ~SharedEtsVmState();
549 
550         js_proxy::JSProxy *GetJsProxyInstance(EtsClass *cls) const;
551         void SetJsProxyInstance(EtsClass *cls, js_proxy::JSProxy *proxy);
552         js_proxy::JSProxy *GetInterfaceProxyInstance(std::string &interfaceName) const;
553         void SetInterfaceProxyInstance(std::string &interfaceName, js_proxy::JSProxy *proxy);
554 
555         // Intentionally leaving these members public to avoid duplicating the InteropCtx's accessors.
556         // Maybe its worth to add some e.g. VmState() method to the InteropCtx and move all its accessors here
557         Class *jsRuntimeClass {};
558         Class *jsValueClass {};
559         Class *esErrorClass {};
560         Class *objectClass {};
561         Class *stringClass {};
562         Class *bigintClass {};
563         Class *arrayAsListIntClass {};
564         Class *nullValueClass {};
565         Class *promiseClass {};
566         Class *errorClass {};
567         Class *exceptionClass {};
568         Class *typeClass {};
569         Class *arrayClass {};
570         Class *boxIntClass {};
571         Class *boxLongClass {};
572         PandaSet<Class *> functionalInterfaces {};
573         Method *promiseInteropConnectMethod = nullptr;
574         PandaEtsVM *pandaEtsVm = nullptr;
575         PandaUniquePtr<ets_proxy::SharedReferenceStorage> etsProxyRefStorage {};
576         PandaUniquePtr<arkplatform::STSVMInterface> stsVMInterface {};
577 
578     private:
579         explicit SharedEtsVmState(PandaEtsVM *vm);
580         void CacheClasses(EtsClassLinker *etsClassLinker);
581 
582         // class -> proxy instance, should be accessed under a mutex, hence private
583         PandaMap<EtsClass *, PandaUniquePtr<js_proxy::JSProxy>> jsProxies_;
584         PandaMap<std::string, PandaUniquePtr<js_proxy::JSProxy>> interfaceProxies_;
585 
586         static std::shared_ptr<SharedEtsVmState> instance_;
587         static ClassLinkerContext *linkerCtx_;
588         static ark::mem::Reference *refToDefaultLinker_;
589         static os::memory::Mutex mutex_;
590 
591         // Allocator calls our private ctor
592         friend class mem::Allocator;
593         friend class InteropCtx;
594     };
595     // NOLINTEND(misc-non-private-member-variables-in-classes)
596     std::shared_ptr<SharedEtsVmState> sharedEtsVmState_;
597 
598     // JSVM context
599     napi_env jsEnv_ {};
600 
601     // various per-JSVM interfaces
602     ExternalIfaceTable interfaceTable_;
603 
604     // caches
605     JSValueStringStorage jsValueStringStor_ {};
606     ConstStringStorage constStringStorage_;
607     LocalScopesStorage localScopesStorage_ {};
608     JSRefConvertCache refconvertCache_;
609     CommonJSObjectCache commonJSObjectCache_;
610 
611     // finalization registry for JSValues
612     mem::Reference *jsvalueFregistryRef_ {};
613     Method *jsvalueFregistryRegister_ {};
614 
615     // ets_proxy data
616     EtsHandle<EtsObject> pendingNewInstance_ {};
617     ets_proxy::EtsMethodWrappersCache etsMethodWrappersCache_ {};
618     ets_proxy::EtsClassWrappersCache etsClassWrappersCache_ {};
619 
620     // hybrid call stack support
621     InteropCallStack interopStk_ {};
622 
623     StackInfoManager stackInfoManager_;
624 
625     PandaUniquePtr<XGCVmAdaptor> ecmaVMIterfaceAdaptor_;
626 
627     // Allocator calls our protected ctor
628     friend class mem::Allocator;
629 
630     friend class testing::SharedReferenceStorage1GTest;
631 };
632 
RefConvertCacheFromInteropCtx(InteropCtx * ctx)633 inline JSRefConvertCache *RefConvertCacheFromInteropCtx(InteropCtx *ctx)
634 {
635     return ctx->GetRefConvertCache();
636 }
JSEnvFromInteropCtx(InteropCtx * ctx)637 inline napi_env JSEnvFromInteropCtx(InteropCtx *ctx)
638 {
639     return ctx->GetJSEnv();
640 }
RefstorFromInteropCtx(InteropCtx * ctx)641 inline mem::GlobalObjectStorage *RefstorFromInteropCtx(InteropCtx *ctx)
642 {
643     return ctx->Refstor();
644 }
645 
646 template <typename Callback>
EnumerateStrings(size_t startFrom,size_t count,Callback cb)647 bool ConstStringStorage::EnumerateStrings(size_t startFrom, size_t count, Callback cb)
648 {
649     auto jsArr = GetReferenceValue(Ctx()->GetJSEnv(), jsStringBufferRef_);
650     for (size_t index = startFrom; index < startFrom + count; index++) {
651         napi_value jsStr;
652         napi_get_element(Ctx()->GetJSEnv(), jsArr, index, &jsStr);
653         ASSERT(GetValueType(Ctx()->GetJSEnv(), jsStr) == napi_string);
654         if (!cb(jsStr)) {
655             return false;
656         }
657     }
658     return true;
659 }
660 
661 }  // namespace ark::ets::interop::js
662 
663 #endif  // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_CONTEXT_H_
664