• 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/ets_proxy/shared_reference.h"
17 #include "plugins/ets/runtime/interop_js/interop_context.h"
18 #include "plugins/ets/runtime/interop_js/js_convert.h"
19 #include "plugins/ets/runtime/types/ets_box_primitive.h"
20 #include "runtime/mem/local_object_handle.h"
21 
22 #include <node_api.h>
23 
24 namespace ark::ets::interop::js::ets_proxy {
25 
CBDoNothing(napi_env env,void * data,void * hint)26 static void CBDoNothing([[maybe_unused]] napi_env env, [[maybe_unused]] void *data, [[maybe_unused]] void *hint) {}
27 
InitETSObject(InteropCtx * ctx,EtsObject * etsObject,napi_value jsObject,uint32_t refIdx)28 bool SharedReference::InitETSObject(InteropCtx *ctx, EtsObject *etsObject, napi_value jsObject, uint32_t refIdx)
29 {
30     SetFlags(HasETSObject::Encode(true) | HasJSObject::Encode(false));
31 
32     auto env = ctx->GetJSEnv();
33     if (UNLIKELY(napi_ok != NapiWrap(env, jsObject, this, FinalizeJSWeak, nullptr, &jsRef_))) {
34         return false;
35     }
36 
37     etsObject->SetInteropHash(refIdx);
38     etsRef_ = ctx->Refstor()->Add(etsObject->GetCoreType(), mem::Reference::ObjectType::GLOBAL);
39     if (UNLIKELY(etsRef_ == nullptr)) {
40         INTEROP_LOG(ERROR) << "REFERENCE STORAGE OVERFLOW";
41         ctx->ThrowJSError(env, "ets refstor overflow");
42         return false;
43     }
44     return true;
45 }
46 
InitJSObject(InteropCtx * ctx,EtsObject * etsObject,napi_value jsObject,uint32_t refIdx)47 bool SharedReference::InitJSObject(InteropCtx *ctx, EtsObject *etsObject, napi_value jsObject, uint32_t refIdx)
48 {
49     SetFlags(HasETSObject::Encode(false) | HasJSObject::Encode(true));
50 
51     auto coro = EtsCoroutine::GetCurrent();
52     auto env = ctx->GetJSEnv();
53     if (UNLIKELY(napi_ok != NapiWrap(env, jsObject, this, CBDoNothing, nullptr, &jsRef_))) {
54         return false;
55     }
56     NAPI_CHECK_FATAL(napi_reference_ref(env, jsRef_, nullptr));
57 
58     LocalObjectHandle<EtsObject> handle(coro, etsObject);  // object may have no strong refs, so create one
59     handle->SetInteropHash(refIdx);
60     // NOTE(vpukhov): reuse weakref from finalizationRegistry
61     etsRef_ = ctx->Refstor()->Add(etsObject->GetCoreType(), mem::Reference::ObjectType::WEAK);
62 
63     auto boxLong = EtsBoxPrimitive<EtsLong>::Create(EtsCoroutine::GetCurrent(), ToUintPtr(this));
64     if (UNLIKELY(boxLong == nullptr ||
65                  !ctx->PushOntoFinalizationRegistry(EtsCoroutine::GetCurrent(), handle.GetPtr(), boxLong))) {
66         NAPI_CHECK_FATAL(napi_delete_reference(env, jsRef_));
67         return false;
68     }
69     return true;
70 }
71 
72 // NOTE(vpukhov): Circular interop references
73 //                Present solution is dummy and consists of two strong refs
InitHybridObject(InteropCtx * ctx,EtsObject * etsObject,napi_value jsObject,uint32_t refIdx)74 bool SharedReference::InitHybridObject(InteropCtx *ctx, EtsObject *etsObject, napi_value jsObject, uint32_t refIdx)
75 {
76     SetFlags(HasETSObject::Encode(true) | HasJSObject::Encode(true));
77 
78     auto env = ctx->GetJSEnv();
79     if (UNLIKELY(napi_ok != NapiWrap(env, jsObject, this, CBDoNothing, nullptr, &jsRef_))) {
80         return false;
81     }
82     NAPI_CHECK_FATAL(napi_reference_ref(env, jsRef_, nullptr));
83 
84     etsObject->SetInteropHash(refIdx);
85     etsRef_ = ctx->Refstor()->Add(etsObject->GetCoreType(), mem::Reference::ObjectType::GLOBAL);
86     if (UNLIKELY(etsRef_ == nullptr)) {
87         INTEROP_LOG(ERROR) << "REFERENCE STORAGE OVERFLOW";
88         ctx->ThrowJSError(env, "ets refstor overflow");
89         NAPI_CHECK_FATAL(napi_delete_reference(env, jsRef_));
90         return false;
91     }
92     return true;
93 }
94 
95 /*static*/
FinalizeJSWeak(napi_env env,void * data,void * hint)96 void SharedReference::FinalizeJSWeak([[maybe_unused]] napi_env env, void *data, [[maybe_unused]] void *hint)
97 {
98     if (UNLIKELY(Runtime::GetCurrent() == nullptr)) {
99         // Runtime was destroyed, no need to cleanup
100         return;
101     }
102     EtsCoroutine *coro = EtsCoroutine::GetCurrent();
103     ASSERT(coro != nullptr);
104     InteropCtx *ctx = InteropCtx::Current(coro);
105     ScopedManagedCodeThread scope(coro);
106 
107     auto ref = reinterpret_cast<SharedReference *>(data);
108     ASSERT(ref->etsRef_ != nullptr);
109 
110     ref->GetEtsObject(ctx)->DropInteropHash();
111     ctx->Refstor()->Remove(ref->etsRef_);
112     ctx->GetSharedRefStorage()->RemoveReference(ref);
113 }
114 
115 /*static*/
FinalizeETSWeak(InteropCtx * ctx,EtsObject * cbarg)116 void SharedReference::FinalizeETSWeak(InteropCtx *ctx, EtsObject *cbarg)
117 {
118     ASSERT(cbarg->GetClass()->GetRuntimeClass() == ctx->GetBoxLongClass());
119     auto boxLong = FromEtsObject<EtsBoxPrimitive<EtsLong>>(cbarg);
120 
121     auto sharedRef = ToNativePtr<SharedReference>(static_cast<uintptr_t>(boxLong->GetValue()));
122 
123     NAPI_CHECK_FATAL(napi_delete_reference(ctx->GetJSEnv(), sharedRef->jsRef_));
124     ctx->GetSharedRefStorage()->RemoveReference(sharedRef);
125 }
126 
127 }  // namespace ark::ets::interop::js::ets_proxy
128