• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 "napi_remote_object_holder.h"
17 
18 #include <uv.h>
19 #include <string_ex.h>
20 #include "ipc_debug.h"
21 #include "log_tags.h"
22 
23 namespace OHOS {
24 static constexpr OHOS::HiviewDFX::HiLogLabel LOG_LABEL = { LOG_CORE, LOG_ID_IPC_NAPI, "napi_remoteObject_holder" };
25 
OnEnvCleanUp(void * data)26 static void OnEnvCleanUp(void *data)
27 {
28     if (data == nullptr) {
29         ZLOGE(LOG_LABEL, "data is null");
30         return;
31     }
32     NAPIRemoteObjectHolder *holder = reinterpret_cast<NAPIRemoteObjectHolder *>(data);
33     // js env has been destrcted, clear saved env info, and check befor use it
34     holder->CleanJsEnv();
35 }
36 
NAPIRemoteObjectHolder(napi_env env,const std::u16string & descriptor,napi_value thisVar)37 NAPIRemoteObjectHolder::NAPIRemoteObjectHolder(napi_env env, const std::u16string &descriptor, napi_value thisVar)
38     : env_(env), descriptor_(descriptor), sptrCachedObject_(nullptr), wptrCachedObject_(nullptr),
39       localInterfaceRef_(nullptr), attachCount_(1), jsObjectRef_(nullptr)
40 {
41     jsThreadId_ = std::this_thread::get_id();
42     // create weak ref, need call napi_delete_reference to release memory,
43     // increase ref count when the JS object will transfer to another thread or process.
44     napi_create_reference(env, thisVar, 0, &jsObjectRef_);
45 
46     // register listener for env destruction
47     napi_status status = napi_add_env_cleanup_hook(env, OnEnvCleanUp, this);
48     if (status != napi_ok) {
49         ZLOGE(LOG_LABEL, "add cleanup hook failed");
50     }
51 }
52 
DeleteJsObjectRefInUvWork()53 void NAPIRemoteObjectHolder::DeleteJsObjectRefInUvWork()
54 {
55     uv_loop_s *loop = nullptr;
56     napi_get_uv_event_loop(env_, &loop);
57     uv_work_t *work = new(std::nothrow) uv_work_t;
58     if (work == nullptr) {
59         ZLOGE(LOG_LABEL, "failed to new work");
60         return;
61     }
62     OperateJsRefParam *param = new OperateJsRefParam {
63         .env = env_,
64         .thisVarRef = jsObjectRef_
65     };
66     if (param == nullptr) {
67         ZLOGE(LOG_LABEL, "failed to new param");
68         delete work;
69         return;
70     }
71     work->data = reinterpret_cast<void *>(param);
72     int uvRet = uv_queue_work(loop, work, [](uv_work_t *work) {
73         ZLOGD(LOG_LABEL, "enter work pool.");
74     }, [](uv_work_t *work, int status) {
75         OperateJsRefParam *param = reinterpret_cast<OperateJsRefParam *>(work->data);
76         napi_handle_scope scope = nullptr;
77         napi_open_handle_scope(param->env, &scope);
78         napi_status napiStatus = napi_delete_reference(param->env, param->thisVarRef);
79         if (napiStatus != napi_ok) {
80             ZLOGE(LOG_LABEL, "failed to delete ref on uv work");
81         }
82         napi_close_handle_scope(param->env, scope);
83         delete param;
84         delete work;
85     });
86     if (uvRet != 0) {
87         ZLOGE(LOG_LABEL, "uv_queue_work failed, ret %{public}d", uvRet);
88     }
89 }
90 
~NAPIRemoteObjectHolder()91 NAPIRemoteObjectHolder::~NAPIRemoteObjectHolder()
92 {
93     if (env_ == nullptr) {
94         ZLOGE(LOG_LABEL, "js env has been destructed");
95         return;
96     }
97 
98     napi_status status = napi_remove_env_cleanup_hook(env_, OnEnvCleanUp, this);
99     if (status != napi_ok) {
100         ZLOGE(LOG_LABEL, "remove cleanup hook failed");
101     }
102 
103     if (localInterfaceRef_ != nullptr) {
104         status = napi_delete_reference(env_, localInterfaceRef_);
105         if (status != napi_ok) {
106             ZLOGE(LOG_LABEL, "failed to delete ref");
107         }
108     }
109 
110     if (jsObjectRef_ != nullptr) {
111         if (jsThreadId_ == std::this_thread::get_id()) {
112             status = napi_delete_reference(env_, jsObjectRef_);
113             if (status != napi_ok) {
114                 ZLOGE(LOG_LABEL, "failed to delete ref");
115             }
116         } else {
117             DeleteJsObjectRefInUvWork();
118         }
119     }
120 }
121 
Get()122 sptr<IRemoteObject> NAPIRemoteObjectHolder::Get()
123 {
124     std::lock_guard<std::mutex> lockGuard(mutex_);
125     // grab an strong reference to the object,
126     // so it will not be freed util this reference released.
127     if (sptrCachedObject_ != nullptr) {
128         return sptrCachedObject_;
129     }
130 
131     sptr<IRemoteObject> tmp = wptrCachedObject_.promote();
132     if (tmp == nullptr && env_ != nullptr) {
133         tmp = new NAPIRemoteObject(jsThreadId_, env_, jsObjectRef_, descriptor_);
134         wptrCachedObject_ = tmp;
135     }
136     return tmp;
137 }
138 
Set(sptr<IRemoteObject> object)139 void NAPIRemoteObjectHolder::Set(sptr<IRemoteObject> object)
140 {
141     std::lock_guard<std::mutex> lockGuard(mutex_);
142     IPCObjectStub *tmp = static_cast<IPCObjectStub *>(object.GetRefPtr());
143     if (tmp->GetObjectType() == IPCObjectStub::OBJECT_TYPE_JAVASCRIPT) {
144         wptrCachedObject_ = object;
145     } else {
146         sptrCachedObject_ = object;
147     }
148 }
149 
GetJsObjectRef() const150 napi_ref NAPIRemoteObjectHolder::GetJsObjectRef() const
151 {
152     return jsObjectRef_;
153 }
154 
GetJsObjectEnv() const155 napi_env NAPIRemoteObjectHolder::GetJsObjectEnv() const
156 {
157     return env_;
158 }
159 
CleanJsEnv()160 void NAPIRemoteObjectHolder::CleanJsEnv()
161 {
162     env_ = nullptr;
163     jsObjectRef_ = nullptr;
164     sptr<IRemoteObject> tmp = wptrCachedObject_.promote();
165     if (tmp != nullptr) {
166         NAPIRemoteObject *object = static_cast<NAPIRemoteObject *>(tmp.GetRefPtr());
167         ZLOGI(LOG_LABEL, "reset env and napi_ref");
168         object->ResetJsEnv();
169     }
170 }
171 
attachLocalInterface(napi_value localInterface,std::string & descriptor)172 void NAPIRemoteObjectHolder::attachLocalInterface(napi_value localInterface, std::string &descriptor)
173 {
174     if (env_ == nullptr) {
175         ZLOGE(LOG_LABEL, "Js env has been destructed");
176         return;
177     }
178     if (localInterfaceRef_ != nullptr) {
179         napi_delete_reference(env_, localInterfaceRef_);
180     }
181     napi_create_reference(env_, localInterface, 0, &localInterfaceRef_);
182     descriptor_ = Str8ToStr16(descriptor);
183 }
184 
queryLocalInterface(std::string & descriptor)185 napi_value NAPIRemoteObjectHolder::queryLocalInterface(std::string &descriptor)
186 {
187     if (env_ == nullptr) {
188         ZLOGE(LOG_LABEL, "Js env has been destructed");
189         return nullptr;
190     }
191     if (!descriptor_.empty() && strcmp(Str16ToStr8(descriptor_).c_str(), descriptor.c_str()) == 0) {
192         napi_value ret = nullptr;
193         napi_get_reference_value(env_, localInterfaceRef_, &ret);
194         return ret;
195     }
196     napi_value result = nullptr;
197     napi_get_null(env_, &result);
198     return result;
199 }
200 } // namespace OHOS