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