• 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_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