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