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