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