• 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 #ifndef PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_CONTEXT_H_
17 #define PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_CONTEXT_H_
18 
19 #include "plugins/ets/runtime/ets_coroutine.h"
20 #include "plugins/ets/runtime/ets_vm.h"
21 #include "plugins/ets/runtime/interop_js/ets_proxy/ets_class_wrapper.h"
22 #include "plugins/ets/runtime/interop_js/ets_proxy/ets_method_wrapper.h"
23 #include "plugins/ets/runtime/interop_js/ets_proxy/shared_reference_storage.h"
24 #include "plugins/ets/runtime/interop_js/js_job_queue.h"
25 #include "plugins/ets/runtime/interop_js/js_refconvert.h"
26 #include "plugins/ets/runtime/interop_js/interop_stacks.h"
27 #include "plugins/ets/runtime/interop_js/intrinsics/std_js_jsruntime.h"
28 #include "runtime/include/value.h"
29 
30 #include <node_api.h>
31 #include <unordered_map>
32 
33 namespace ark {
34 
35 class Class;
36 class ClassLinkerContext;
37 
38 namespace mem {
39 class GlobalObjectStorage;
40 class Reference;
41 }  // namespace mem
42 
43 }  // namespace ark
44 
45 namespace ark::ets::interop::js {
46 
47 class JSValue;
48 
49 // Work-around for String JSValue and node_api
50 class JSValueStringStorage {
51 public:
52     class CachedEntry {
53     public:
Data()54         std::string const *Data()
55         {
56             return data_;
57         }
58 
59     private:
60         friend class JSValue;
61         friend class JSValueStringStorage;
62 
CachedEntry(std::string const * data)63         explicit CachedEntry(std::string const *data) : data_(data) {}
64 
65         std::string const *data_ {};
66     };
67 
68     explicit JSValueStringStorage() = default;
69 
Get(std::string && str)70     CachedEntry Get(std::string &&str)
71     {
72         auto [it, inserted] = stringTab_.try_emplace(std::move(str), 0);
73         it->second++;
74         return CachedEntry(&it->first);
75     }
76 
Release(CachedEntry str)77     void Release(CachedEntry str)
78     {
79         auto it = stringTab_.find(*str.Data());
80         ASSERT(it != stringTab_.end());
81         if (--(it->second) == 0) {
82             stringTab_.erase(it);
83         }
84     }
85 
86 private:
87     std::unordered_map<std::string, uint64_t> stringTab_;
88 };
89 
90 class ConstStringStorage {
91 public:
92     void LoadDynamicCallClass(Class *klass);
93 
94     template <typename Callback>
95     bool EnumerateStrings(size_t startFrom, size_t count, Callback cb);
96 
97     napi_value GetConstantPool();
98 
99 private:
100     napi_value InitBuffer(size_t length);
101     InteropCtx *Ctx();
102 
103 private:
104     std::vector<napi_value> stringBuffer_ {};
105     napi_ref jsStringBufferRef_ {};
106     Class *lastLoadedClass_ {};
107 };
108 
109 class InteropCtx {
110 public:
111     PANDA_PUBLIC_API static void Init(EtsCoroutine *coro, napi_env env);
112 
Destroy(void * ptr)113     static void Destroy(void *ptr)
114     {
115         reinterpret_cast<InteropCtx *>(ptr)->~InteropCtx();
116     }
117 
Current(PandaEtsVM * etsVm)118     static InteropCtx *Current(PandaEtsVM *etsVm)
119     {
120         static_assert(sizeof(PandaEtsVM::ExternalData) >= sizeof(InteropCtx));
121         static_assert(alignof(PandaEtsVM::ExternalData) >= alignof(InteropCtx));
122         return reinterpret_cast<InteropCtx *>(etsVm->GetExternalData());
123     }
124 
Current(EtsCoroutine * coro)125     static InteropCtx *Current(EtsCoroutine *coro)
126     {
127         return Current(coro->GetPandaVM());
128     }
129 
Current()130     static InteropCtx *Current()
131     {
132         return Current(EtsCoroutine::GetCurrent());
133     }
134 
GetPandaEtsVM()135     PandaEtsVM *GetPandaEtsVM()
136     {
137         return PandaEtsVM::FromExternalData(reinterpret_cast<void *>(this));
138     }
139 
GetJSEnv()140     napi_env GetJSEnv() const
141     {
142         ASSERT(jsEnv_ != nullptr);
143         return jsEnv_;
144     }
145 
SetJSEnv(napi_env env)146     void SetJSEnv(napi_env env)
147     {
148         jsEnv_ = env;
149     }
150 
Refstor()151     mem::GlobalObjectStorage *Refstor() const
152     {
153         return refstor_;
154     }
155 
GetClassLinker()156     ClassLinker *GetClassLinker() const
157     {
158         return classLinker_;
159     }
160 
LinkerCtx()161     ClassLinkerContext *LinkerCtx() const
162     {
163         return linkerCtx_;
164     }
165 
GetStringStor()166     JSValueStringStorage *GetStringStor()
167     {
168         return &jsValueStringStor_;
169     }
170 
GetLocalScopesStorage()171     LocalScopesStorage *GetLocalScopesStorage()
172     {
173         return &localScopesStorage_;
174     }
175 
DestroyLocalScopeForTopFrame(Frame * frame)176     void DestroyLocalScopeForTopFrame(Frame *frame)
177     {
178         GetLocalScopesStorage()->DestroyLocalScopeForTopFrame(jsEnv_, frame);
179     }
180 
GetJSValueFinalizationRegistry()181     mem::Reference *GetJSValueFinalizationRegistry() const
182     {
183         return jsvalueFregistryRef_;
184     }
185 
GetRegisterFinalizerMethod()186     Method *GetRegisterFinalizerMethod() const
187     {
188         return jsvalueFregistryRegister_;
189     }
190 
191     // NOTE(vpukhov): implement in native code
PushOntoFinalizationRegistry(EtsCoroutine * coro,EtsObject * obj,EtsObject * cbarg)192     [[nodiscard]] bool PushOntoFinalizationRegistry(EtsCoroutine *coro, EtsObject *obj, EtsObject *cbarg)
193     {
194         auto queue = Refstor()->Get(jsvalueFregistryRef_);
195         std::array<Value, 4U> args = {Value(queue), Value(obj->GetCoreType()), Value(cbarg->GetCoreType()),
196                                       Value(static_cast<ObjectHeader *>(nullptr))};
197         jsvalueFregistryRegister_->Invoke(coro, args.data());
198         return !coro->HasPendingException();
199     }
200 
201     // NOTE(vpukhov): replace with stack-like allocator
202     template <typename T, size_t OPT_SZ>
203     struct TempArgs {
204     public:
TempArgsTempArgs205         explicit TempArgs(size_t sz)
206         {
207             if (LIKELY(sz <= OPT_SZ)) {
208                 sp_ = {new (inlArr_.data()) T[sz], sz};
209             } else {
210                 sp_ = {new T[sz], sz};
211             }
212         }
213 
~TempArgsTempArgs214         ~TempArgs()
215         {
216             if (UNLIKELY(static_cast<void *>(sp_.data()) != inlArr_.data())) {
217                 delete[] sp_.data();
218             } else {
219                 static_assert(std::is_trivially_destructible_v<T>);
220             }
221         }
222 
223         NO_COPY_SEMANTIC(TempArgs);
224 #if defined(__clang__)
225         NO_MOVE_SEMANTIC(TempArgs);
226 #elif defined(__GNUC__)  // RVO bug
227         NO_MOVE_OPERATOR(TempArgs);
TempArgsTempArgs228         TempArgs(TempArgs &&other) : sp_(other.sp_), inlArr_(other.inlArr_)
229         {
230             if (LIKELY(sp_.size() <= OPT_SZ)) {
231                 sp_ = Span<T>(reinterpret_cast<T *>(inlArr_.data()), sp_.size());
232             }
233         }
234 #endif
235 
236         Span<T> &operator*()
237         {
238             return sp_;
239         }
240         Span<T> *operator->()
241         {
242             return &sp_;
243         }
244         T &operator[](size_t idx)
245         {
246             return sp_[idx];
247         }
248 
249     private:
250         Span<T> sp_;
251         alignas(T) std::array<uint8_t, sizeof(T) * OPT_SZ> inlArr_;
252     };
253 
254     template <typename T, size_t OPT_SZ = 8U>
GetTempArgs(size_t sz)255     ALWAYS_INLINE static auto GetTempArgs(size_t sz)
256     {
257         return TempArgs<T, OPT_SZ>(sz);
258     }
259 
CallStack()260     InteropCallStack &CallStack()
261     {
262         return interopStk_;
263     }
264 
GetRefConvertCache()265     JSRefConvertCache *GetRefConvertCache()
266     {
267         return &refconvertCache_;
268     }
269 
GetJSValueClass()270     Class *GetJSValueClass() const
271     {
272         return jsValueClass_;
273     }
274 
GetJSErrorClass()275     Class *GetJSErrorClass() const
276     {
277         return jsErrorClass_;
278     }
279 
GetObjectClass()280     Class *GetObjectClass() const
281     {
282         return objectClass_;
283     }
284 
GetStringClass()285     Class *GetStringClass() const
286     {
287         return stringClass_;
288     }
289 
GetUndefinedClass()290     Class *GetUndefinedClass() const
291     {
292         return undefinedClass_;
293     }
294 
GetPromiseClass()295     Class *GetPromiseClass() const
296     {
297         return promiseClass_;
298     }
299 
GetPromiseInteropConnectMethod()300     Method *GetPromiseInteropConnectMethod() const
301     {
302         return promiseInteropConnectMethod_;
303     }
304 
GetErrorClass()305     Class *GetErrorClass() const
306     {
307         return errorClass_;
308     }
309 
GetExceptionClass()310     Class *GetExceptionClass() const
311     {
312         return exceptionClass_;
313     }
314 
GetTypeClass()315     Class *GetTypeClass() const
316     {
317         return typeClass_;
318     }
319 
GetBoxIntClass()320     Class *GetBoxIntClass() const
321     {
322         return boxIntClass_;
323     }
324 
GetBoxLongClass()325     Class *GetBoxLongClass() const
326     {
327         return boxLongClass_;
328     }
329 
GetArrayClass()330     Class *GetArrayClass() const
331     {
332         return arrayClass_;
333     }
334 
GetArrayBufferClass()335     Class *GetArrayBufferClass() const
336     {
337         return arraybufClass_;
338     }
339 
IsFunctionalInterface(Class * klass)340     bool IsFunctionalInterface(Class *klass) const
341     {
342         return functionalInterfaces_.count(klass) > 0;
343     }
344 
345     EtsObject *CreateETSCoreJSError(EtsCoroutine *coro, JSValue *jsvalue);
346 
347     static void ThrowETSError(EtsCoroutine *coro, napi_value val);
348     static void ThrowETSError(EtsCoroutine *coro, const char *msg);
ThrowETSError(EtsCoroutine * coro,const std::string & msg)349     static void ThrowETSError(EtsCoroutine *coro, const std::string &msg)
350     {
351         ThrowETSError(coro, msg.c_str());
352     }
353 
354     PANDA_PUBLIC_API static void ThrowJSError(napi_env env, const std::string &msg);
355     static void ThrowJSTypeError(napi_env env, const std::string &msg);
356     static void ThrowJSValue(napi_env env, napi_value val);
357 
358     void ForwardEtsException(EtsCoroutine *coro);
359     void ForwardJSException(EtsCoroutine *coro);
360 
SanityETSExceptionPending()361     [[nodiscard]] static bool SanityETSExceptionPending()
362     {
363         auto coro = EtsCoroutine::GetCurrent();
364         auto env = InteropCtx::Current(coro)->GetJSEnv();
365         return coro->HasPendingException() && !NapiIsExceptionPending(env);
366     }
367 
SanityJSExceptionPending()368     [[nodiscard]] static bool SanityJSExceptionPending()
369     {
370         auto coro = EtsCoroutine::GetCurrent();
371         auto env = InteropCtx::Current(coro)->GetJSEnv();
372         return !coro->HasPendingException() && NapiIsExceptionPending(env);
373     }
374 
375     // Die and print execution stacks
376     [[noreturn]] PANDA_PUBLIC_API static void Fatal(const char *msg);
Fatal(const std::string & msg)377     [[noreturn]] static void Fatal(const std::string &msg)
378     {
379         Fatal(msg.c_str());
380     }
381 
SetPendingNewInstance(EtsObject * newInstance)382     void SetPendingNewInstance(EtsObject *newInstance)
383     {
384         pendingNewInstance_ = newInstance;
385     }
386 
AcquirePendingNewInstance()387     EtsObject *AcquirePendingNewInstance()
388     {
389         auto res = pendingNewInstance_;
390         pendingNewInstance_ = nullptr;
391         return res;
392     }
393 
GetEtsMethodWrappersCache()394     ets_proxy::EtsMethodWrappersCache *GetEtsMethodWrappersCache()
395     {
396         return &etsMethodWrappersCache_;
397     }
398 
GetEtsClassWrappersCache()399     ets_proxy::EtsClassWrappersCache *GetEtsClassWrappersCache()
400     {
401         return &etsClassWrappersCache_;
402     }
403 
GetSharedRefStorage()404     ets_proxy::SharedReferenceStorage *GetSharedRefStorage()
405     {
406         return etsProxyRefStorage_.get();
407     }
408 
GetUndefinedObject()409     EtsObject *GetUndefinedObject()
410     {
411         return EtsObject::FromCoreType(GetPandaEtsVM()->GetUndefinedObject());
412     }
413 
GetConstStringStorage()414     ConstStringStorage *GetConstStringStorage()
415     {
416         return &constStringStorage_;
417     }
418 
AllocateSlotsInStringBuffer(uint32_t count)419     uint32_t AllocateSlotsInStringBuffer(uint32_t count)
420     {
421         // Atomic with relaxed order reason: ordering constraints are not required
422         return qnameBufferSize_.fetch_add(count, std::memory_order_relaxed);
423     }
424 
GetStringBufferSize()425     uint32_t GetStringBufferSize()
426     {
427         // Atomic with relaxed order reason: ordering constraints are not required
428         return qnameBufferSize_.load(std::memory_order_relaxed);
429     }
430 
FromConstStringStorage(ConstStringStorage * constStringStorage)431     static InteropCtx *FromConstStringStorage(ConstStringStorage *constStringStorage)
432     {
433         return reinterpret_cast<InteropCtx *>(reinterpret_cast<uintptr_t>(constStringStorage) -
434                                               MEMBER_OFFSET(InteropCtx, constStringStorage_));
435     }
436 
437 private:
438     explicit InteropCtx(EtsCoroutine *coro, napi_env env);
439     void CacheClasses(EtsClassLinker *etsClassLinker);
440 
441     napi_env jsEnv_ {};
442 
443     mem::GlobalObjectStorage *refstor_ {};
444     ClassLinker *classLinker_ {};
445     ClassLinkerContext *linkerCtx_ {};
446     JSValueStringStorage jsValueStringStor_ {};
447     ConstStringStorage constStringStorage_ {};
448 
449     LocalScopesStorage localScopesStorage_ {};
450     mem::Reference *jsvalueFregistryRef_ {};
451 
452     InteropCallStack interopStk_ {};
453 
454     JSRefConvertCache refconvertCache_;
455 
456     Class *jsRuntimeClass_ {};
457     Class *jsValueClass_ {};
458     Class *jsErrorClass_ {};
459     Class *objectClass_ {};
460     Class *stringClass_ {};
461     Class *undefinedClass_ {};
462     Class *promiseClass_ {};
463     Class *errorClass_ {};
464     Class *exceptionClass_ {};
465     Class *typeClass_ {};
466     Class *arrayClass_ {};
467     Class *arraybufClass_ {};
468 
469     Class *boxIntClass_ {};
470     Class *boxLongClass_ {};
471 
472     std::set<Class *> functionalInterfaces_ {};
473 
474     Method *jsvalueFregistryRegister_ {};
475     Method *promiseInteropConnectMethod_ = nullptr;
476 
477     // ets_proxy data
478     EtsObject *pendingNewInstance_ {};
479     ets_proxy::EtsMethodWrappersCache etsMethodWrappersCache_ {};
480     ets_proxy::EtsClassWrappersCache etsClassWrappersCache_ {};
481     std::unique_ptr<ets_proxy::SharedReferenceStorage> etsProxyRefStorage_ {};
482 
483     // should be one per VM when we will have multiple InteropContexts
484     std::atomic_uint32_t qnameBufferSize_ {};
485 
486     friend class JSNapiEnvScope;
487 };
488 
RefConvertCacheFromInteropCtx(InteropCtx * ctx)489 inline JSRefConvertCache *RefConvertCacheFromInteropCtx(InteropCtx *ctx)
490 {
491     return ctx->GetRefConvertCache();
492 }
JSEnvFromInteropCtx(InteropCtx * ctx)493 inline napi_env JSEnvFromInteropCtx(InteropCtx *ctx)
494 {
495     return ctx->GetJSEnv();
496 }
RefstorFromInteropCtx(InteropCtx * ctx)497 inline mem::GlobalObjectStorage *RefstorFromInteropCtx(InteropCtx *ctx)
498 {
499     return ctx->Refstor();
500 }
501 
502 template <typename Callback>
EnumerateStrings(size_t startFrom,size_t count,Callback cb)503 bool ConstStringStorage::EnumerateStrings(size_t startFrom, size_t count, Callback cb)
504 {
505     auto jsArr = GetReferenceValue(Ctx()->GetJSEnv(), jsStringBufferRef_);
506     for (size_t index = startFrom; index < startFrom + count; index++) {
507         napi_value jsStr;
508         napi_get_element(Ctx()->GetJSEnv(), jsArr, index, &jsStr);
509         ASSERT(GetValueType(Ctx()->GetJSEnv(), jsStr) == napi_string);
510         if (!cb(jsStr)) {
511             return false;
512         }
513     }
514     return true;
515 }
516 
517 }  // namespace ark::ets::interop::js
518 
519 #endif  // !PANDA_PLUGINS_ETS_RUNTIME_INTEROP_JS_INTEROP_CONTEXT_H_
520