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