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