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 if (env_ == nullptr) {
56 ZLOGE(LOG_LABEL, "js env has been destructed");
57 return;
58 }
59 uv_loop_s *loop = nullptr;
60 napi_get_uv_event_loop(env_, &loop);
61 if (loop == nullptr) {
62 ZLOGE(LOG_LABEL, "loop is nullptr");
63 return;
64 }
65 uv_work_t *work = new (std::nothrow) uv_work_t;
66 if (work == nullptr) {
67 ZLOGE(LOG_LABEL, "failed to new work");
68 return;
69 }
70 OperateJsRefParam *param = new (std::nothrow) OperateJsRefParam {
71 .env = env_,
72 .thisVarRef = jsObjectRef_
73 };
74 if (param == nullptr) {
75 ZLOGE(LOG_LABEL, "new OperateJsRefParam failed");
76 delete work;
77 return;
78 }
79 work->data = reinterpret_cast<void *>(param);
80 int uvRet = uv_queue_work(loop, work, [](uv_work_t *work) {
81 ZLOGD(LOG_LABEL, "enter work pool.");
82 }, [](uv_work_t *work, int status) {
83 OperateJsRefParam *param = reinterpret_cast<OperateJsRefParam *>(work->data);
84 napi_handle_scope scope = nullptr;
85 napi_open_handle_scope(param->env, &scope);
86 napi_status napiStatus = napi_delete_reference(param->env, param->thisVarRef);
87 if (napiStatus != napi_ok) {
88 ZLOGE(LOG_LABEL, "failed to delete ref on uv work");
89 }
90 napi_close_handle_scope(param->env, scope);
91 delete param;
92 delete work;
93 });
94 if (uvRet != 0) {
95 delete param;
96 delete work;
97 ZLOGE(LOG_LABEL, "uv_queue_work failed, ret %{public}d", uvRet);
98 }
99 }
100
~NAPIRemoteObjectHolder()101 NAPIRemoteObjectHolder::~NAPIRemoteObjectHolder()
102 {
103 if (env_ == nullptr) {
104 ZLOGE(LOG_LABEL, "js env has been destructed");
105 return;
106 }
107
108 napi_status status = napi_remove_env_cleanup_hook(env_, OnEnvCleanUp, this);
109 if (status != napi_ok) {
110 ZLOGE(LOG_LABEL, "remove cleanup hook failed");
111 }
112
113 if (localInterfaceRef_ != nullptr) {
114 status = napi_delete_reference(env_, localInterfaceRef_);
115 if (status != napi_ok) {
116 ZLOGE(LOG_LABEL, "failed to delete ref");
117 }
118 }
119
120 if (jsObjectRef_ != nullptr) {
121 if (jsThreadId_ == std::this_thread::get_id()) {
122 status = napi_delete_reference(env_, jsObjectRef_);
123 if (status != napi_ok) {
124 ZLOGE(LOG_LABEL, "failed to delete ref");
125 }
126 } else {
127 DeleteJsObjectRefInUvWork();
128 }
129 }
130 }
131
Get()132 sptr<IRemoteObject> NAPIRemoteObjectHolder::Get()
133 {
134 std::lock_guard<std::mutex> lockGuard(mutex_);
135 // grab an strong reference to the object,
136 // so it will not be freed util this reference released.
137 if (sptrCachedObject_ != nullptr) {
138 return sptrCachedObject_;
139 }
140
141 sptr<IRemoteObject> tmp = wptrCachedObject_.promote();
142 if (tmp == nullptr && env_ != nullptr) {
143 tmp = new (std::nothrow) NAPIRemoteObject(jsThreadId_, env_, jsObjectRef_, descriptor_);
144 if (tmp == nullptr) {
145 ZLOGE(LOG_LABEL, "new NAPIRemoteObject failed");
146 return nullptr;
147 }
148 wptrCachedObject_ = tmp;
149 }
150 return tmp;
151 }
152
Set(sptr<IRemoteObject> object)153 void NAPIRemoteObjectHolder::Set(sptr<IRemoteObject> object)
154 {
155 std::lock_guard<std::mutex> lockGuard(mutex_);
156 IPCObjectStub *tmp = static_cast<IPCObjectStub *>(object.GetRefPtr());
157 if (tmp->GetObjectType() == IPCObjectStub::OBJECT_TYPE_JAVASCRIPT) {
158 wptrCachedObject_ = object;
159 } else {
160 sptrCachedObject_ = object;
161 }
162 }
163
GetJsObjectRef() const164 napi_ref NAPIRemoteObjectHolder::GetJsObjectRef() const
165 {
166 return jsObjectRef_;
167 }
168
GetJsObjectEnv() const169 napi_env NAPIRemoteObjectHolder::GetJsObjectEnv() const
170 {
171 return env_;
172 }
173
CleanJsEnv()174 void NAPIRemoteObjectHolder::CleanJsEnv()
175 {
176 env_ = nullptr;
177 jsObjectRef_ = nullptr;
178 sptr<IRemoteObject> tmp = wptrCachedObject_.promote();
179 if (tmp != nullptr) {
180 NAPIRemoteObject *object = static_cast<NAPIRemoteObject *>(tmp.GetRefPtr());
181 ZLOGI(LOG_LABEL, "reset env and napi_ref");
182 object->ResetJsEnv();
183 }
184 }
185
attachLocalInterface(napi_value localInterface,std::string & descriptor)186 void NAPIRemoteObjectHolder::attachLocalInterface(napi_value localInterface, std::string &descriptor)
187 {
188 if (env_ == nullptr) {
189 ZLOGE(LOG_LABEL, "Js env has been destructed");
190 return;
191 }
192 if (localInterfaceRef_ != nullptr) {
193 napi_delete_reference(env_, localInterfaceRef_);
194 }
195 napi_create_reference(env_, localInterface, 0, &localInterfaceRef_);
196 descriptor_ = Str8ToStr16(descriptor);
197 }
198
queryLocalInterface(std::string & descriptor)199 napi_value NAPIRemoteObjectHolder::queryLocalInterface(std::string &descriptor)
200 {
201 if (env_ == nullptr) {
202 ZLOGE(LOG_LABEL, "Js env has been destructed");
203 return nullptr;
204 }
205 if (!descriptor_.empty() && strcmp(Str16ToStr8(descriptor_).c_str(), descriptor.c_str()) == 0) {
206 napi_value ret = nullptr;
207 napi_get_reference_value(env_, localInterfaceRef_, &ret);
208 return ret;
209 }
210 napi_value result = nullptr;
211 napi_get_null(env_, &result);
212 return result;
213 }
214 } // namespace OHOS