1 |
|
|
#include "main_thread_interface.h" |
2 |
|
|
|
3 |
|
|
#include "env-inl.h" |
4 |
|
|
#include "node_mutex.h" |
5 |
|
|
#include "v8-inspector.h" |
6 |
|
|
#include "util-inl.h" |
7 |
|
|
|
8 |
|
|
#include <unicode/unistr.h> |
9 |
|
|
|
10 |
|
|
#include <functional> |
11 |
|
|
#include <memory> |
12 |
|
|
|
13 |
|
|
namespace node { |
14 |
|
|
namespace inspector { |
15 |
|
|
namespace { |
16 |
|
|
|
17 |
|
|
using v8_inspector::StringBuffer; |
18 |
|
|
using v8_inspector::StringView; |
19 |
|
|
|
20 |
|
|
template <typename T> |
21 |
|
|
class DeletableWrapper : public Deletable { |
22 |
|
|
public: |
23 |
|
94 |
explicit DeletableWrapper(std::unique_ptr<T> object) |
24 |
|
94 |
: object_(std::move(object)) {} |
25 |
|
94 |
~DeletableWrapper() override = default; |
26 |
|
|
|
27 |
|
1918 |
static T* get(MainThreadInterface* thread, int id) { |
28 |
|
|
return |
29 |
|
1918 |
static_cast<DeletableWrapper<T>*>(thread->GetObject(id))->object_.get(); |
30 |
|
|
} |
31 |
|
|
|
32 |
|
|
private: |
33 |
|
|
std::unique_ptr<T> object_; |
34 |
|
|
}; |
35 |
|
|
|
36 |
|
|
template <typename T> |
37 |
|
94 |
std::unique_ptr<Deletable> WrapInDeletable(std::unique_ptr<T> object) { |
38 |
|
|
return std::unique_ptr<DeletableWrapper<T>>( |
39 |
|
94 |
new DeletableWrapper<T>(std::move(object))); |
40 |
|
|
} |
41 |
|
|
|
42 |
|
|
template <typename Factory> |
43 |
|
|
class CreateObjectRequest : public Request { |
44 |
|
|
public: |
45 |
|
32 |
CreateObjectRequest(int object_id, Factory factory) |
46 |
|
32 |
: object_id_(object_id), factory_(std::move(factory)) {} |
47 |
|
|
|
48 |
|
32 |
void Call(MainThreadInterface* thread) override { |
49 |
|
32 |
thread->AddObject(object_id_, WrapInDeletable(factory_(thread))); |
50 |
|
32 |
} |
51 |
|
|
|
52 |
|
|
private: |
53 |
|
|
int object_id_; |
54 |
|
|
Factory factory_; |
55 |
|
|
}; |
56 |
|
|
|
57 |
|
|
template <typename Factory> |
58 |
|
32 |
std::unique_ptr<Request> NewCreateRequest(int object_id, Factory factory) { |
59 |
|
|
return std::unique_ptr<Request>( |
60 |
|
32 |
new CreateObjectRequest<Factory>(object_id, std::move(factory))); |
61 |
|
|
} |
62 |
|
|
|
63 |
|
|
class DeleteRequest : public Request { |
64 |
|
|
public: |
65 |
|
47 |
explicit DeleteRequest(int object_id) : object_id_(object_id) {} |
66 |
|
|
|
67 |
|
34 |
void Call(MainThreadInterface* thread) override { |
68 |
|
34 |
thread->RemoveObject(object_id_); |
69 |
|
34 |
} |
70 |
|
|
|
71 |
|
|
private: |
72 |
|
|
int object_id_; |
73 |
|
|
}; |
74 |
|
|
|
75 |
|
|
template <typename Target, typename Fn> |
76 |
|
|
class CallRequest : public Request { |
77 |
|
|
public: |
78 |
|
1918 |
CallRequest(int id, Fn fn) : id_(id), fn_(std::move(fn)) {} |
79 |
|
|
|
80 |
|
1918 |
void Call(MainThreadInterface* thread) override { |
81 |
|
1918 |
fn_(DeletableWrapper<Target>::get(thread, id_)); |
82 |
|
1918 |
} |
83 |
|
|
|
84 |
|
|
private: |
85 |
|
|
int id_; |
86 |
|
|
Fn fn_; |
87 |
|
|
}; |
88 |
|
|
|
89 |
|
|
template <typename T> |
90 |
|
|
class AnotherThreadObjectReference { |
91 |
|
|
public: |
92 |
|
94 |
AnotherThreadObjectReference( |
93 |
|
|
std::shared_ptr<MainThreadHandle> thread, int object_id) |
94 |
|
94 |
: thread_(thread), object_id_(object_id) {} |
95 |
|
|
|
96 |
|
|
template <typename Factory> |
97 |
|
32 |
AnotherThreadObjectReference( |
98 |
|
|
std::shared_ptr<MainThreadHandle> thread, Factory factory) |
99 |
|
32 |
: AnotherThreadObjectReference(thread, thread->newObjectId()) { |
100 |
|
32 |
thread_->Post(NewCreateRequest(object_id_, std::move(factory))); |
101 |
|
32 |
} |
102 |
|
|
AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete; |
103 |
|
|
|
104 |
|
94 |
~AnotherThreadObjectReference() { |
105 |
|
|
// Disappearing thread may cause a memory leak |
106 |
|
94 |
thread_->Post(std::make_unique<DeleteRequest>(object_id_)); |
107 |
|
94 |
} |
108 |
|
|
|
109 |
|
|
template <typename Fn> |
110 |
|
1918 |
void Call(Fn fn) const { |
111 |
|
|
using Request = CallRequest<T, Fn>; |
112 |
|
5754 |
thread_->Post(std::unique_ptr<Request>( |
113 |
|
3836 |
new Request(object_id_, std::move(fn)))); |
114 |
|
1918 |
} |
115 |
|
|
|
116 |
|
|
template <typename Arg> |
117 |
|
372 |
void Call(void (T::*fn)(Arg), Arg argument) const { |
118 |
|
372 |
Call(std::bind(Apply<Arg>, std::placeholders::_1, fn, std::move(argument))); |
119 |
|
372 |
} |
120 |
|
|
|
121 |
|
|
private: |
122 |
|
|
// This has to use non-const reference to support std::bind with non-copyable |
123 |
|
|
// types |
124 |
|
|
template <typename Argument> |
125 |
|
372 |
static void Apply(T* target, void (T::*fn)(Argument), |
126 |
|
|
/* NOLINT (runtime/references) */ Argument& argument) { |
127 |
✗✓ |
372 |
(target->*fn)(std::move(argument)); |
128 |
|
372 |
} |
129 |
|
|
|
130 |
|
|
std::shared_ptr<MainThreadHandle> thread_; |
131 |
|
|
const int object_id_; |
132 |
|
|
}; |
133 |
|
|
|
134 |
|
|
class MainThreadSessionState { |
135 |
|
|
public: |
136 |
|
32 |
MainThreadSessionState(MainThreadInterface* thread, bool prevent_shutdown) |
137 |
|
32 |
: thread_(thread), |
138 |
|
32 |
prevent_shutdown_(prevent_shutdown) {} |
139 |
|
|
|
140 |
|
32 |
static std::unique_ptr<MainThreadSessionState> Create( |
141 |
|
|
MainThreadInterface* thread, bool prevent_shutdown) { |
142 |
|
32 |
return std::make_unique<MainThreadSessionState>(thread, prevent_shutdown); |
143 |
|
|
} |
144 |
|
|
|
145 |
|
32 |
void Connect(std::unique_ptr<InspectorSessionDelegate> delegate) { |
146 |
|
32 |
Agent* agent = thread_->inspector_agent(); |
147 |
✓✗ |
32 |
if (agent != nullptr) |
148 |
|
32 |
session_ = agent->Connect(std::move(delegate), prevent_shutdown_); |
149 |
|
32 |
} |
150 |
|
|
|
151 |
|
154 |
void Dispatch(std::unique_ptr<StringBuffer> message) { |
152 |
|
154 |
session_->Dispatch(message->string()); |
153 |
|
154 |
} |
154 |
|
|
|
155 |
|
|
private: |
156 |
|
|
MainThreadInterface* thread_; |
157 |
|
|
bool prevent_shutdown_; |
158 |
|
|
std::unique_ptr<InspectorSession> session_; |
159 |
|
|
}; |
160 |
|
|
|
161 |
|
|
class CrossThreadInspectorSession : public InspectorSession { |
162 |
|
|
public: |
163 |
|
32 |
CrossThreadInspectorSession( |
164 |
|
|
int id, |
165 |
|
|
std::shared_ptr<MainThreadHandle> thread, |
166 |
|
|
std::unique_ptr<InspectorSessionDelegate> delegate, |
167 |
|
|
bool prevent_shutdown) |
168 |
|
96 |
: state_(thread, std::bind(MainThreadSessionState::Create, |
169 |
|
|
std::placeholders::_1, |
170 |
|
32 |
prevent_shutdown)) { |
171 |
|
32 |
state_.Call(&MainThreadSessionState::Connect, std::move(delegate)); |
172 |
|
32 |
} |
173 |
|
|
|
174 |
|
154 |
void Dispatch(const StringView& message) override { |
175 |
|
154 |
state_.Call(&MainThreadSessionState::Dispatch, |
176 |
|
308 |
StringBuffer::create(message)); |
177 |
|
154 |
} |
178 |
|
|
|
179 |
|
|
private: |
180 |
|
|
AnotherThreadObjectReference<MainThreadSessionState> state_; |
181 |
|
|
}; |
182 |
|
|
|
183 |
|
|
class ThreadSafeDelegate : public InspectorSessionDelegate { |
184 |
|
|
public: |
185 |
|
15 |
ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread, int object_id) |
186 |
|
15 |
: thread_(thread), delegate_(thread, object_id) {} |
187 |
|
|
|
188 |
|
773 |
void SendMessageToFrontend(const v8_inspector::StringView& message) override { |
189 |
|
773 |
delegate_.Call( |
190 |
|
|
[m = StringBuffer::create(message)] |
191 |
|
773 |
(InspectorSessionDelegate* delegate) { |
192 |
|
773 |
delegate->SendMessageToFrontend(m->string()); |
193 |
|
773 |
}); |
194 |
|
773 |
} |
195 |
|
|
|
196 |
|
|
private: |
197 |
|
|
std::shared_ptr<MainThreadHandle> thread_; |
198 |
|
|
AnotherThreadObjectReference<InspectorSessionDelegate> delegate_; |
199 |
|
|
}; |
200 |
|
|
} // namespace |
201 |
|
|
|
202 |
|
|
|
203 |
|
6085 |
MainThreadInterface::MainThreadInterface(Agent* agent) : agent_(agent) {} |
204 |
|
|
|
205 |
|
5553 |
MainThreadInterface::~MainThreadInterface() { |
206 |
✓✗ |
5553 |
if (handle_) |
207 |
|
5553 |
handle_->Reset(); |
208 |
|
5553 |
} |
209 |
|
|
|
210 |
|
10703 |
void MainThreadInterface::Post(std::unique_ptr<Request> request) { |
211 |
✗✓ |
10703 |
CHECK_NOT_NULL(agent_); |
212 |
|
21406 |
Mutex::ScopedLock scoped_lock(requests_lock_); |
213 |
|
10703 |
bool needs_notify = requests_.empty(); |
214 |
|
10703 |
requests_.push_back(std::move(request)); |
215 |
✓✓ |
10703 |
if (needs_notify) { |
216 |
|
10366 |
std::weak_ptr<MainThreadInterface> weak_self {shared_from_this()}; |
217 |
|
10366 |
agent_->env()->RequestInterrupt([weak_self](Environment*) { |
218 |
✓✗ |
10361 |
if (auto iface = weak_self.lock()) iface->DispatchMessages(); |
219 |
|
10361 |
}); |
220 |
|
|
} |
221 |
|
10703 |
incoming_message_cond_.Broadcast(scoped_lock); |
222 |
|
10703 |
} |
223 |
|
|
|
224 |
|
117 |
bool MainThreadInterface::WaitForFrontendEvent() { |
225 |
|
|
// We allow DispatchMessages reentry as we enter the pause. This is important |
226 |
|
|
// to support debugging the code invoked by an inspector call, such |
227 |
|
|
// as Runtime.evaluate |
228 |
|
117 |
dispatching_messages_ = false; |
229 |
✓✗ |
117 |
if (dispatching_message_queue_.empty()) { |
230 |
|
234 |
Mutex::ScopedLock scoped_lock(requests_lock_); |
231 |
✓✓ |
234 |
while (requests_.empty()) incoming_message_cond_.Wait(scoped_lock); |
232 |
|
|
} |
233 |
|
117 |
return true; |
234 |
|
|
} |
235 |
|
|
|
236 |
|
10361 |
void MainThreadInterface::DispatchMessages() { |
237 |
✓✓ |
10361 |
if (dispatching_messages_) |
238 |
|
60 |
return; |
239 |
|
10301 |
dispatching_messages_ = true; |
240 |
|
10301 |
bool had_messages = false; |
241 |
✓✓ |
20662 |
do { |
242 |
✓✗ |
20662 |
if (dispatching_message_queue_.empty()) { |
243 |
|
41324 |
Mutex::ScopedLock scoped_lock(requests_lock_); |
244 |
|
20662 |
requests_.swap(dispatching_message_queue_); |
245 |
|
|
} |
246 |
|
20662 |
had_messages = !dispatching_message_queue_.empty(); |
247 |
✓✓ |
31342 |
while (!dispatching_message_queue_.empty()) { |
248 |
|
10680 |
MessageQueue::value_type task; |
249 |
|
10680 |
std::swap(dispatching_message_queue_.front(), task); |
250 |
|
10680 |
dispatching_message_queue_.pop_front(); |
251 |
|
|
|
252 |
|
21360 |
v8::SealHandleScope seal_handle_scope(agent_->env()->isolate()); |
253 |
|
10680 |
task->Call(this); |
254 |
|
|
} |
255 |
|
|
} while (had_messages); |
256 |
|
10301 |
dispatching_messages_ = false; |
257 |
|
|
} |
258 |
|
|
|
259 |
|
13220 |
std::shared_ptr<MainThreadHandle> MainThreadInterface::GetHandle() { |
260 |
✓✓ |
13220 |
if (handle_ == nullptr) |
261 |
|
6085 |
handle_ = std::make_shared<MainThreadHandle>(this); |
262 |
|
13220 |
return handle_; |
263 |
|
|
} |
264 |
|
|
|
265 |
|
7104 |
void MainThreadInterface::AddObject(int id, |
266 |
|
|
std::unique_ptr<Deletable> object) { |
267 |
✗✓ |
7104 |
CHECK_NOT_NULL(object); |
268 |
|
7104 |
managed_objects_[id] = std::move(object); |
269 |
|
7104 |
} |
270 |
|
|
|
271 |
|
964 |
void MainThreadInterface::RemoveObject(int id) { |
272 |
✗✓ |
964 |
CHECK_EQ(1, managed_objects_.erase(id)); |
273 |
|
964 |
} |
274 |
|
|
|
275 |
|
959 |
Deletable* MainThreadInterface::GetObject(int id) { |
276 |
|
959 |
Deletable* pointer = GetObjectIfExists(id); |
277 |
|
|
// This would mean the object is requested after it was disposed, which is |
278 |
|
|
// a coding error. |
279 |
✗✓ |
959 |
CHECK_NOT_NULL(pointer); |
280 |
|
959 |
return pointer; |
281 |
|
|
} |
282 |
|
|
|
283 |
|
963 |
Deletable* MainThreadInterface::GetObjectIfExists(int id) { |
284 |
|
963 |
auto iterator = managed_objects_.find(id); |
285 |
✗✓ |
963 |
if (iterator == managed_objects_.end()) { |
286 |
|
|
return nullptr; |
287 |
|
|
} |
288 |
|
963 |
return iterator->second.get(); |
289 |
|
|
} |
290 |
|
|
|
291 |
|
76354 |
std::unique_ptr<StringBuffer> Utf8ToStringView(const std::string& message) { |
292 |
|
|
icu::UnicodeString utf16 = icu::UnicodeString::fromUTF8( |
293 |
|
152708 |
icu::StringPiece(message.data(), message.length())); |
294 |
|
76354 |
StringView view(reinterpret_cast<const uint16_t*>(utf16.getBuffer()), |
295 |
|
76354 |
utf16.length()); |
296 |
|
76354 |
return StringBuffer::create(view); |
297 |
|
|
} |
298 |
|
|
|
299 |
|
32 |
std::unique_ptr<InspectorSession> MainThreadHandle::Connect( |
300 |
|
|
std::unique_ptr<InspectorSessionDelegate> delegate, |
301 |
|
|
bool prevent_shutdown) { |
302 |
|
|
return std::unique_ptr<InspectorSession>( |
303 |
|
32 |
new CrossThreadInspectorSession(++next_session_id_, |
304 |
|
64 |
shared_from_this(), |
305 |
|
32 |
std::move(delegate), |
306 |
|
32 |
prevent_shutdown)); |
307 |
|
|
} |
308 |
|
|
|
309 |
|
16309 |
bool MainThreadHandle::Post(std::unique_ptr<Request> request) { |
310 |
|
32618 |
Mutex::ScopedLock scoped_lock(block_lock_); |
311 |
✓✓ |
16309 |
if (!main_thread_) |
312 |
|
5606 |
return false; |
313 |
|
10703 |
main_thread_->Post(std::move(request)); |
314 |
|
10703 |
return true; |
315 |
|
|
} |
316 |
|
|
|
317 |
|
5553 |
void MainThreadHandle::Reset() { |
318 |
|
5553 |
Mutex::ScopedLock scoped_lock(block_lock_); |
319 |
|
5553 |
main_thread_ = nullptr; |
320 |
|
5553 |
} |
321 |
|
|
|
322 |
|
|
std::unique_ptr<InspectorSessionDelegate> |
323 |
|
15 |
MainThreadHandle::MakeDelegateThreadSafe( |
324 |
|
|
std::unique_ptr<InspectorSessionDelegate> delegate) { |
325 |
|
15 |
int id = newObjectId(); |
326 |
|
15 |
main_thread_->AddObject(id, WrapInDeletable(std::move(delegate))); |
327 |
|
|
return std::unique_ptr<InspectorSessionDelegate>( |
328 |
|
15 |
new ThreadSafeDelegate(shared_from_this(), id)); |
329 |
|
|
} |
330 |
|
|
|
331 |
|
725 |
bool MainThreadHandle::Expired() { |
332 |
|
725 |
Mutex::ScopedLock scoped_lock(block_lock_); |
333 |
|
725 |
return main_thread_ == nullptr; |
334 |
|
|
} |
335 |
|
|
} // namespace inspector |
336 |
|
|
} // namespace node |