• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 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 "js_native_api_v8_inspector.h"
17 
18 #include <unistd.h>
19 
20 #include "inspector_socket_server.h"
21 #include "inspector_utils.h"
22 #include "js_native_api_v8.h"
23 #include "jsvm_mutex.h"
24 #include "libplatform/libplatform.h"
25 #include "v8-inspector.h"
26 #include "v8-platform.h"
27 
28 #ifdef __POSIX__
29 #include <climits> // PTHREAD_STACK_MIN
30 #include <pthread.h>
31 #endif // __POSIX__
32 
33 #include <algorithm>
34 #include <cstring>
35 #include <securec.h>
36 #include <sstream>
37 #include <unordered_map>
38 #include <vector>
39 
40 namespace v8impl {
41 
42 namespace {
43 using jsvm::ConditionVariable;
44 using jsvm::Mutex;
45 using jsvm::inspector::StringViewToUtf8;
46 using jsvm::inspector::Utf8ToStringView;
47 using v8_inspector::StringBuffer;
48 using v8_inspector::StringView;
49 
50 class MainThreadInterface;
51 
52 class Request {
53 public:
54     virtual void Call(MainThreadInterface*) = 0;
55     virtual ~Request() = default;
56 };
57 
58 class Deletable {
59 public:
60     virtual ~Deletable() = default;
61 };
62 
63 using MessageQueue = std::deque<std::unique_ptr<Request>>;
64 
65 class MainThreadHandle : public std::enable_shared_from_this<MainThreadHandle> {
66 public:
MainThreadHandle(MainThreadInterface * mainThread)67     explicit MainThreadHandle(MainThreadInterface* mainThread) : mainThread(mainThread) {}
~MainThreadHandle()68     ~MainThreadHandle()
69     {
70         Mutex::ScopedLock scopedLock(blockLock);
71         CHECK_NULL(mainThread); // mainThread should have called Reset
72     }
73     std::unique_ptr<InspectorSession> Connect(std::unique_ptr<InspectorSessionDelegate> delegate, bool preventShutdown);
NewObjectId()74     int NewObjectId()
75     {
76         return ++nextObjectId;
77     }
78     bool Post(std::unique_ptr<Request> request);
79 
80 private:
81     void Reset();
82 
83     MainThreadInterface* mainThread;
84     Mutex blockLock;
85     int nextSessionId = 0;
86     std::atomic_int nextObjectId = { 1 };
87 
88     friend class MainThreadInterface;
89 };
90 
91 class MainThreadInterface : public std::enable_shared_from_this<MainThreadInterface> {
92 public:
93     explicit MainThreadInterface(Agent* agent);
94     ~MainThreadInterface();
95 
96     void DispatchMessages();
97     void Post(std::unique_ptr<Request> request);
98     bool WaitForFrontendEvent();
99     std::shared_ptr<MainThreadHandle> GetHandle();
InspectorAgent()100     Agent* InspectorAgent()
101     {
102         return agent;
103     }
104     void AddObject(int handle, std::unique_ptr<Deletable> object);
105     Deletable* GetObject(int id);
106     Deletable* GetObjectIfExists(int id);
107     void RemoveObject(int handle);
108 
109 private:
110     MessageQueue requests;
111     Mutex requestsLock; // requests live across threads
112     // This queue is to maintain the order of the messages for the cases
113     // when we reenter the DispatchMessages function.
114     MessageQueue dispatchingMessageQueue;
115     bool dispatchingMessages = false;
116     ConditionVariable incomingMessageCond;
117     // Used from any thread
118     Agent* const agent;
119     std::shared_ptr<MainThreadHandle> handle;
120     std::unordered_map<int, std::unique_ptr<Deletable>> managedObjects;
121 };
122 
123 template<typename T>
124 class DeletableWrapper : public Deletable {
125 public:
DeletableWrapper(std::unique_ptr<T> object)126     explicit DeletableWrapper(std::unique_ptr<T> object) : object(std::move(object)) {}
127     ~DeletableWrapper() override = default;
128 
Get(MainThreadInterface * thread,int id)129     static T* Get(MainThreadInterface* thread, int id)
130     {
131         return static_cast<DeletableWrapper<T>*>(thread->GetObject(id))->object.get();
132     }
133 
134 private:
135     std::unique_ptr<T> object;
136 };
137 
138 template<typename T>
WrapInDeletable(std::unique_ptr<T> object)139 std::unique_ptr<Deletable> WrapInDeletable(std::unique_ptr<T> object)
140 {
141     return std::make_unique<DeletableWrapper<T>>(std::move(object));
142 }
143 
144 template<typename Factory>
145 class CreateObjectRequest : public Request {
146 public:
CreateObjectRequest(int objectId,Factory factory)147     CreateObjectRequest(int objectId, Factory factory) : objectId(objectId), factory(std::move(factory)) {}
148 
Call(MainThreadInterface * thread)149     void Call(MainThreadInterface* thread) override
150     {
151         thread->AddObject(objectId, WrapInDeletable(factory(thread)));
152     }
153 
154 private:
155     int objectId;
156     Factory factory;
157 };
158 
159 template<typename Factory>
NewCreateRequest(int objectId,Factory factory)160 std::unique_ptr<Request> NewCreateRequest(int objectId, Factory factory)
161 {
162     return std::make_unique<CreateObjectRequest<Factory>>(objectId, std::move(factory));
163 }
164 
165 class DeleteRequest : public Request {
166 public:
DeleteRequest(int objectId)167     explicit DeleteRequest(int objectId) : objectId(objectId) {}
168 
Call(MainThreadInterface * thread)169     void Call(MainThreadInterface* thread) override
170     {
171         thread->RemoveObject(objectId);
172     }
173 
174 private:
175     int objectId;
176 };
177 
178 template<typename Target, typename Fn>
179 class CallRequest : public Request {
180 public:
CallRequest(int id,Fn fn)181     CallRequest(int id, Fn fn) : id(id), fn(std::move(fn)) {}
182 
Call(MainThreadInterface * thread)183     void Call(MainThreadInterface* thread) override
184     {
185         fn(DeletableWrapper<Target>::Get(thread, id));
186     }
187 
188 private:
189     int id;
190     Fn fn;
191 };
192 
193 template<typename T>
194 class AnotherThreadObjectReference {
195 public:
AnotherThreadObjectReference(std::shared_ptr<MainThreadHandle> thread,int objectId)196     AnotherThreadObjectReference(std::shared_ptr<MainThreadHandle> thread, int objectId)
197         : thread(thread), objectId(objectId)
198     {}
199 
200     template<typename Factory>
AnotherThreadObjectReference(std::shared_ptr<MainThreadHandle> thread,Factory factory)201     AnotherThreadObjectReference(std::shared_ptr<MainThreadHandle> thread, Factory factory)
202         : AnotherThreadObjectReference(thread, thread->NewObjectId())
203     {
204         thread->Post(NewCreateRequest(objectId, std::move(factory)));
205     }
206 
207     AnotherThreadObjectReference(AnotherThreadObjectReference&) = delete;
208 
209     AnotherThreadObjectReference& operator=(const AnotherThreadObjectReference&) = delete;
210 
~AnotherThreadObjectReference()211     ~AnotherThreadObjectReference()
212     {
213         // Disappearing thread may cause a memory leak
214         thread->Post(std::make_unique<DeleteRequest>(objectId));
215     }
216 
217     template<typename Fn>
Call(Fn fn) const218     void Call(Fn fn) const
219     {
220         using Request = CallRequest<T, Fn>;
221         thread->Post(std::make_unique<Request>(objectId, std::move(fn)));
222     }
223 
224     template<typename Arg>
Call(void (T::* fn)(Arg),Arg argument) const225     void Call(void (T::*fn)(Arg), Arg argument) const
226     {
227         Call(std::bind(Apply<Arg>, std::placeholders::_1, fn, std::move(argument)));
228     }
229 
230 private:
231     // This has to use non-const reference to support std::bind with non-copyable
232     // types
233     template<typename Argument>
Apply(T * target,void (T::* fn)(Argument),Argument & argument)234     static void Apply(T* target, void (T::*fn)(Argument), Argument& argument) /* NOLINT (runtime/references) */
235     {
236         (target->*fn)(std::move(argument));
237     }
238 
239     std::shared_ptr<MainThreadHandle> thread;
240     const int objectId;
241 };
242 
243 class MainThreadSessionState {
244 public:
MainThreadSessionState(MainThreadInterface * thread,bool preventShutdown)245     MainThreadSessionState(MainThreadInterface* thread, bool preventShutdown)
246         : thread(thread), preventShutdown(preventShutdown)
247     {}
248 
Create(MainThreadInterface * thread,bool preventShutdown)249     static std::unique_ptr<MainThreadSessionState> Create(MainThreadInterface* thread, bool preventShutdown)
250     {
251         return std::make_unique<MainThreadSessionState>(thread, preventShutdown);
252     }
253 
Connect(std::unique_ptr<InspectorSessionDelegate> delegate)254     void Connect(std::unique_ptr<InspectorSessionDelegate> delegate)
255     {
256         Agent* agent = thread->InspectorAgent();
257         if (agent != nullptr) {
258             session = agent->Connect(std::move(delegate), preventShutdown);
259         }
260     }
261 
Dispatch(std::unique_ptr<StringBuffer> message)262     void Dispatch(std::unique_ptr<StringBuffer> message)
263     {
264         session->Dispatch(message->string());
265     }
266 
267 private:
268     MainThreadInterface* thread;
269     bool preventShutdown;
270     std::unique_ptr<InspectorSession> session;
271 };
272 
273 class CrossThreadInspectorSession : public InspectorSession {
274 public:
CrossThreadInspectorSession(int id,std::shared_ptr<MainThreadHandle> thread,std::unique_ptr<InspectorSessionDelegate> delegate,bool preventShutdown)275     CrossThreadInspectorSession(int id,
276                                 std::shared_ptr<MainThreadHandle> thread,
277                                 std::unique_ptr<InspectorSessionDelegate> delegate,
278                                 bool preventShutdown)
279         : state(thread, std::bind(MainThreadSessionState::Create, std::placeholders::_1, preventShutdown))
280     {
281         state.Call(&MainThreadSessionState::Connect, std::move(delegate));
282     }
283 
Dispatch(const StringView & message)284     void Dispatch(const StringView& message) override
285     {
286         state.Call(&MainThreadSessionState::Dispatch, StringBuffer::create(message));
287     }
288 
289 private:
290     AnotherThreadObjectReference<MainThreadSessionState> state;
291 };
292 
293 class ThreadSafeDelegate : public InspectorSessionDelegate {
294 public:
ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread,int objectId)295     ThreadSafeDelegate(std::shared_ptr<MainThreadHandle> thread, int objectId)
296         : thread(thread), delegate(thread, objectId)
297     {}
298 
SendMessageToFrontend(const v8_inspector::StringView & message)299     void SendMessageToFrontend(const v8_inspector::StringView& message) override
300     {
301         delegate.Call([m = StringBuffer::create(message)](InspectorSessionDelegate* delegate) {
302             delegate->SendMessageToFrontend(m->string());
303         });
304     }
305 
306 private:
307     std::shared_ptr<MainThreadHandle> thread;
308     AnotherThreadObjectReference<InspectorSessionDelegate> delegate;
309 };
310 
MainThreadInterface(Agent * agent)311 MainThreadInterface::MainThreadInterface(Agent* agent) : agent(agent) {}
312 
~MainThreadInterface()313 MainThreadInterface::~MainThreadInterface()
314 {
315     if (handle) {
316         handle->Reset();
317     }
318 }
319 
Post(std::unique_ptr<Request> request)320 void MainThreadInterface::Post(std::unique_ptr<Request> request)
321 {
322     CHECK_NOT_NULL(agent);
323     Mutex::ScopedLock scopedLock(requestsLock);
324     bool needsNotify = requests.empty();
325     requests.push_back(std::move(request));
326     if (needsNotify) {
327         std::weak_ptr<MainThreadInterface> weakSelf { shared_from_this() };
328         agent->env()->RequestInterrupt([weakSelf](Environment*) {
329             if (auto iface = weakSelf.lock()) {
330                 iface->DispatchMessages();
331             }
332         });
333     }
334     incomingMessageCond.Broadcast(scopedLock);
335 }
336 
WaitForFrontendEvent()337 bool MainThreadInterface::WaitForFrontendEvent()
338 {
339     // We allow DispatchMessages reentry as we enter the pause. This is important
340     // to support debugging the code invoked by an inspector call, such
341     // as Runtime.evaluate
342     dispatchingMessages = false;
343     if (dispatchingMessageQueue.empty()) {
344         Mutex::ScopedLock scopedLock(requestsLock);
345         while (requests.empty())
346             incomingMessageCond.Wait(scopedLock);
347     }
348     return true;
349 }
350 
DispatchMessages()351 void MainThreadInterface::DispatchMessages()
352 {
353     if (dispatchingMessages) {
354         return;
355     }
356     dispatchingMessages = true;
357     bool hadMessages = false;
358     do {
359         if (dispatchingMessageQueue.empty()) {
360             Mutex::ScopedLock scopedLock(requestsLock);
361             requests.swap(dispatchingMessageQueue);
362         }
363         hadMessages = !dispatchingMessageQueue.empty();
364         while (!dispatchingMessageQueue.empty()) {
365             MessageQueue::value_type task;
366             std::swap(dispatchingMessageQueue.front(), task);
367             dispatchingMessageQueue.pop_front();
368 
369             v8::SealHandleScope sealHandleScope(agent->env()->isolate);
370             task->Call(this);
371         }
372     } while (hadMessages);
373     dispatchingMessages = false;
374 }
375 
GetHandle()376 std::shared_ptr<MainThreadHandle> MainThreadInterface::GetHandle()
377 {
378     if (handle == nullptr) {
379         handle = std::make_shared<MainThreadHandle>(this);
380     }
381     return handle;
382 }
383 
AddObject(int id,std::unique_ptr<Deletable> object)384 void MainThreadInterface::AddObject(int id, std::unique_ptr<Deletable> object)
385 {
386     CHECK_NOT_NULL(object);
387     managedObjects[id] = std::move(object);
388 }
389 
RemoveObject(int handle)390 void MainThreadInterface::RemoveObject(int handle)
391 {
392     CHECK_EQ(1, managedObjects.erase(handle));
393 }
394 
GetObject(int id)395 Deletable* MainThreadInterface::GetObject(int id)
396 {
397     Deletable* pointer = GetObjectIfExists(id);
398     // This would mean the object is requested after it was disposed, which is
399     // a coding error.
400     CHECK_NOT_NULL(pointer);
401     return pointer;
402 }
403 
GetObjectIfExists(int id)404 Deletable* MainThreadInterface::GetObjectIfExists(int id)
405 {
406     auto iterator = managedObjects.find(id);
407     if (iterator == managedObjects.end()) {
408         return nullptr;
409     }
410     return iterator->second.get();
411 }
412 
Connect(std::unique_ptr<InspectorSessionDelegate> delegate,bool preventShutdown)413 std::unique_ptr<InspectorSession> MainThreadHandle::Connect(std::unique_ptr<InspectorSessionDelegate> delegate,
414                                                             bool preventShutdown)
415 {
416     return std::unique_ptr<InspectorSession>(
417         new CrossThreadInspectorSession(++nextSessionId, shared_from_this(), std::move(delegate), preventShutdown));
418 }
419 
Post(std::unique_ptr<Request> request)420 bool MainThreadHandle::Post(std::unique_ptr<Request> request)
421 {
422     Mutex::ScopedLock scopedLock(blockLock);
423     if (!mainThread) {
424         return false;
425     }
426     mainThread->Post(std::move(request));
427     return true;
428 }
429 
Reset()430 void MainThreadHandle::Reset()
431 {
432     Mutex::ScopedLock scopedLock(blockLock);
433     mainThread = nullptr;
434 }
435 } // namespace
436 
437 namespace {
438 using jsvm::InspectPublishUid;
439 using jsvm::inspector::CheckedUvLoopClose;
440 using jsvm::inspector::CSPRNG;
441 using jsvm::inspector::FormatWsAddress;
442 using jsvm::inspector::GetHumanReadableProcessName;
443 using jsvm::inspector::InspectorSocketServer;
444 
445 // K_KILL closes connections and stops the server, K_STOP only stops the server
446 enum class TransportAction { K_KILL, K_SEND_MESSAGE, K_STOP };
447 
ScriptPath(uv_loop_t * loop,const std::string & scriptName)448 std::string ScriptPath(uv_loop_t* loop, const std::string& scriptName)
449 {
450     std::string scriptPath;
451 
452     if (!scriptName.empty()) {
453         uv_fs_t req;
454         req.ptr = nullptr;
455         if (0 == uv_fs_realpath(loop, &req, scriptName.c_str(), nullptr)) {
456             CHECK_NOT_NULL(req.ptr);
457             scriptPath = std::string(static_cast<char*>(req.ptr));
458         }
459         uv_fs_req_cleanup(&req);
460     }
461 
462     return scriptPath;
463 }
464 
465 // UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
466 // Used ver 4 - with numbers
GenerateID()467 std::string GenerateID()
468 {
469     uint16_t buffer[8];
470     CHECK(CSPRNG(buffer, sizeof(buffer)));
471 
472     char uuid[256];
473     int ret = snprintf_s(uuid, sizeof(uuid), sizeof(uuid) - 1, "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
474                          buffer[0],                     // time_low
475                          buffer[1],                     // time_mid
476                          buffer[2],                     // time_low
477                          (buffer[3] & 0x0fff) | 0x4000, // time_hi_and_version
478                          (buffer[4] & 0x3fff) | 0x8000, // clk_seq_hi clk_seq_low
479                          buffer[5],                     // node
480                          buffer[6], buffer[7]);
481     CHECK(ret >= 0);
482     return uuid;
483 }
484 
485 class RequestToServer {
486 public:
RequestToServer(TransportAction action,int sessionId,std::unique_ptr<v8_inspector::StringBuffer> message)487     RequestToServer(TransportAction action, int sessionId, std::unique_ptr<v8_inspector::StringBuffer> message)
488         : action(action), sessionId(sessionId), message(std::move(message))
489     {}
490 
Dispatch(InspectorSocketServer * server) const491     void Dispatch(InspectorSocketServer* server) const
492     {
493         switch (action) {
494             case TransportAction::K_KILL:
495                 server->TerminateConnections();
496                 [[fallthrough]];
497             case TransportAction::K_STOP:
498                 server->Stop();
499                 break;
500             case TransportAction::K_SEND_MESSAGE:
501                 server->Send(sessionId, StringViewToUtf8(message->string()));
502                 break;
503         }
504     }
505 
506 private:
507     TransportAction action;
508     int sessionId;
509     std::unique_ptr<v8_inspector::StringBuffer> message;
510 };
511 
512 class RequestQueue;
513 
514 class RequestQueueData {
515 public:
516     using MessageQueue = std::deque<RequestToServer>;
517 
RequestQueueData(uv_loop_t * loop)518     explicit RequestQueueData(uv_loop_t* loop) : handle(std::make_shared<RequestQueue>(this))
519     {
520         int err = uv_async_init(loop, &async, [](uv_async_t* async) {
521             RequestQueueData* wrapper = jsvm::inspector::ContainerOf(&RequestQueueData::async, async);
522             wrapper->DoDispatch();
523         });
524         CHECK_EQ(0, err);
525     }
526 
527     static void CloseAndFree(RequestQueueData* queue);
528 
Post(int sessionId,TransportAction action,std::unique_ptr<StringBuffer> message)529     void Post(int sessionId, TransportAction action, std::unique_ptr<StringBuffer> message)
530     {
531         Mutex::ScopedLock scopedLock(stateLock);
532         bool notify = messages.empty();
533         messages.emplace_back(action, sessionId, std::move(message));
534         if (notify) {
535             CHECK_EQ(0, uv_async_send(&async));
536             incomingMessageCond.Broadcast(scopedLock);
537         }
538     }
539 
Wait()540     void Wait()
541     {
542         Mutex::ScopedLock scopedLock(stateLock);
543         if (messages.empty()) {
544             incomingMessageCond.Wait(scopedLock);
545         }
546     }
547 
SetServer(InspectorSocketServer * serverParam)548     void SetServer(InspectorSocketServer* serverParam)
549     {
550         server = serverParam;
551     }
552 
GetHandle()553     std::shared_ptr<RequestQueue> GetHandle()
554     {
555         return handle;
556     }
557 
558 private:
559     ~RequestQueueData() = default;
560 
GetMessages()561     MessageQueue GetMessages()
562     {
563         Mutex::ScopedLock scopedLock(stateLock);
564         MessageQueue messagesQ;
565         messages.swap(messagesQ);
566         return messagesQ;
567     }
568 
DoDispatch()569     void DoDispatch()
570     {
571         if (server == nullptr) {
572             return;
573         }
574         for (const auto& request : GetMessages()) {
575             request.Dispatch(server);
576         }
577     }
578 
579     std::shared_ptr<RequestQueue> handle;
580     uv_async_t async;
581     InspectorSocketServer* server = nullptr;
582     MessageQueue messages;
583     Mutex stateLock; // Locked before mutating the queue.
584     ConditionVariable incomingMessageCond;
585 };
586 
587 class RequestQueue {
588 public:
RequestQueue(RequestQueueData * data)589     explicit RequestQueue(RequestQueueData* data) : data(data) {}
590 
Reset()591     void Reset()
592     {
593         Mutex::ScopedLock scopedLock(lock);
594         data = nullptr;
595     }
596 
Post(int sessionId,TransportAction action,std::unique_ptr<StringBuffer> message)597     void Post(int sessionId, TransportAction action, std::unique_ptr<StringBuffer> message)
598     {
599         Mutex::ScopedLock scopedLock(lock);
600         if (data != nullptr) {
601             data->Post(sessionId, action, std::move(message));
602         }
603     }
604 
Expired()605     bool Expired()
606     {
607         Mutex::ScopedLock scopedLock(lock);
608         return data == nullptr;
609     }
610 
611 private:
612     RequestQueueData* data;
613     Mutex lock;
614 };
615 
616 class IoSessionDelegate : public InspectorSessionDelegate {
617 public:
IoSessionDelegate(std::shared_ptr<RequestQueue> queue,int id)618     explicit IoSessionDelegate(std::shared_ptr<RequestQueue> queue, int id) : requestQueue(queue), id(id) {}
SendMessageToFrontend(const v8_inspector::StringView & message)619     void SendMessageToFrontend(const v8_inspector::StringView& message) override
620     {
621         requestQueue->Post(id, TransportAction::K_SEND_MESSAGE, StringBuffer::create(message));
622     }
623 
624 private:
625     std::shared_ptr<RequestQueue> requestQueue;
626     int id;
627 };
628 
629 // Passed to InspectorSocketServer to handle WS inspector protocol events,
630 // mostly session start, message received, and session end.
631 class InspectorIoDelegate : public jsvm::inspector::SocketServerDelegate {
632 public:
633     InspectorIoDelegate(std::shared_ptr<RequestQueueData> queue,
634                         std::shared_ptr<MainThreadHandle> mainThread,
635                         const std::string& targetId,
636                         const std::string& scriptPath,
637                         const std::string& scriptName);
638     ~InspectorIoDelegate() override = default;
639 
640     void StartSession(int sessionId, const std::string& inTargetId) override;
641     void MessageReceived(int sessionId, const std::string& message) override;
642     void EndSession(int sessionId) override;
643 
644     std::vector<std::string> GetTargetIds() override;
645     std::string GetTargetTitle(const std::string& id) override;
646     std::string GetTargetUrl(const std::string& id) override;
AssignServer(InspectorSocketServer * server)647     void AssignServer(InspectorSocketServer* server) override
648     {
649         requestQueue->SetServer(server);
650     }
651 
652 private:
653     std::shared_ptr<RequestQueueData> requestQueue;
654     std::shared_ptr<MainThreadHandle> mainThread;
655     std::unordered_map<int, std::unique_ptr<InspectorSession>> sessions;
656     const std::string scriptName;
657     const std::string scriptPath;
658     const std::string targetId;
659 };
660 
InspectorIoDelegate(std::shared_ptr<RequestQueueData> queue,std::shared_ptr<MainThreadHandle> mainThread,const std::string & targetId,const std::string & scriptPath,const std::string & scriptName)661 InspectorIoDelegate::InspectorIoDelegate(std::shared_ptr<RequestQueueData> queue,
662                                          std::shared_ptr<MainThreadHandle> mainThread,
663                                          const std::string& targetId,
664                                          const std::string& scriptPath,
665                                          const std::string& scriptName)
666     : requestQueue(queue), mainThread(mainThread), scriptName(scriptName), scriptPath(scriptPath), targetId(targetId)
667 {}
668 
StartSession(int sessionId,const std::string & inTargetId)669 void InspectorIoDelegate::StartSession(int sessionId, const std::string& inTargetId)
670 {
671     auto session = mainThread->Connect(std::make_unique<IoSessionDelegate>(requestQueue->GetHandle(), sessionId), true);
672     if (session) {
673         sessions[sessionId] = std::move(session);
674         if (fprintf(stderr, "Debugger attached.\n") < 0) {
675             return;
676         }
677     }
678 }
679 
MessageReceived(int sessionId,const std::string & message)680 void InspectorIoDelegate::MessageReceived(int sessionId, const std::string& message)
681 {
682     auto session = sessions.find(sessionId);
683     if (session != sessions.end()) {
684         session->second->Dispatch(Utf8ToStringView(message)->string());
685     }
686 }
687 
EndSession(int sessionId)688 void InspectorIoDelegate::EndSession(int sessionId)
689 {
690     sessions.erase(sessionId);
691 }
692 
GetTargetIds()693 std::vector<std::string> InspectorIoDelegate::GetTargetIds()
694 {
695     return { targetId };
696 }
697 
GetTargetTitle(const std::string & id)698 std::string InspectorIoDelegate::GetTargetTitle(const std::string& id)
699 {
700     return scriptName.empty() ? GetHumanReadableProcessName() : scriptName;
701 }
702 
GetTargetUrl(const std::string & id)703 std::string InspectorIoDelegate::GetTargetUrl(const std::string& id)
704 {
705     return "file://" + scriptPath;
706 }
707 
708 // static
CloseAndFree(RequestQueueData * queue)709 void RequestQueueData::CloseAndFree(RequestQueueData* queue)
710 {
711     queue->handle->Reset();
712     queue->handle.reset();
713     uv_close(reinterpret_cast<uv_handle_t*>(&queue->async), [](uv_handle_t* handle) {
714         uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
715         RequestQueueData* wrapper = jsvm::inspector::ContainerOf(&RequestQueueData::async, async);
716         delete wrapper;
717     });
718 }
719 } // namespace
720 
721 class InspectorIo {
722 public:
723     // Start the inspector agent thread, waiting for it to initialize.
724     // Returns empty pointer if thread was not started.
725     static std::unique_ptr<InspectorIo> Start(std::shared_ptr<MainThreadHandle> mainThread,
726                                               const std::string& path,
727                                               std::shared_ptr<ExclusiveAccess<HostPort>> hostPortParam,
728                                               const jsvm::InspectPublishUid& inspectPublishUid);
729 
730     // Will block till the transport thread shuts down
731     ~InspectorIo();
732 
733     void StopAcceptingNewConnections();
734     std::string GetWsUrl() const;
735 
736 private:
737     InspectorIo(std::shared_ptr<MainThreadHandle> handle,
738                 const std::string& path,
739                 std::shared_ptr<ExclusiveAccess<HostPort>> hostPortParam,
740                 const jsvm::InspectPublishUid& inspectPublishUid);
741 
742     // Wrapper for agent->ThreadMain()
743     static void ThreadMain(void* io);
744 
745     // Runs a uv_loop_t
746     void ThreadMain();
747 
748     // This is a thread-safe object that will post async tasks. It lives as long
749     // as an Inspector object lives (almost as long as an Isolate).
750     std::shared_ptr<MainThreadHandle> mainThread;
751     // Used to post on a frontend interface thread, lives while the server is
752     // running
753     std::shared_ptr<RequestQueue> requestQueue;
754     std::shared_ptr<ExclusiveAccess<HostPort>> hostPort;
755     jsvm::InspectPublishUid inspectPublishUid;
756 
757     // The IO thread runs its own uv_loop to implement the TCP server off
758     // the main thread.
759     uv_thread_t thread;
760 
761     // For setting up interthread communications
762     Mutex threadStartLock;
763     jsvm::ConditionVariable threadStartCondition;
764     std::string scriptName;
765     // May be accessed from any thread
766     const std::string id;
767 };
768 
769 // static
Start(std::shared_ptr<MainThreadHandle> mainThread,const std::string & path,std::shared_ptr<ExclusiveAccess<HostPort>> hostPortParam,const InspectPublishUid & inspectPublishUid)770 std::unique_ptr<InspectorIo> InspectorIo::Start(std::shared_ptr<MainThreadHandle> mainThread,
771                                                 const std::string& path,
772                                                 std::shared_ptr<ExclusiveAccess<HostPort>> hostPortParam,
773                                                 const InspectPublishUid& inspectPublishUid)
774 {
775     auto io = std::unique_ptr<InspectorIo>(new InspectorIo(mainThread, path, hostPortParam, inspectPublishUid));
776     if (io->requestQueue->Expired()) { // Thread is not running
777         return nullptr;
778     }
779     return io;
780 }
781 
InspectorIo(std::shared_ptr<MainThreadHandle> mainThread,const std::string & path,std::shared_ptr<ExclusiveAccess<HostPort>> hostPortParam,const InspectPublishUid & inspectPublishUid)782 InspectorIo::InspectorIo(std::shared_ptr<MainThreadHandle> mainThread,
783                          const std::string& path,
784                          std::shared_ptr<ExclusiveAccess<HostPort>> hostPortParam,
785                          const InspectPublishUid& inspectPublishUid)
786     : mainThread(mainThread), hostPort(hostPortParam), inspectPublishUid(inspectPublishUid), thread(), scriptName(path),
787       id(GenerateID())
788 {
789     Mutex::ScopedLock scopedLock(threadStartLock);
790     CHECK_EQ(uv_thread_create(&thread, InspectorIo::ThreadMain, this), 0);
791     threadStartCondition.Wait(scopedLock);
792 }
793 
~InspectorIo()794 InspectorIo::~InspectorIo()
795 {
796     requestQueue->Post(0, TransportAction::K_KILL, nullptr);
797     int err = uv_thread_join(&thread);
798     CHECK_EQ(err, 0);
799 }
800 
StopAcceptingNewConnections()801 void InspectorIo::StopAcceptingNewConnections()
802 {
803     requestQueue->Post(0, TransportAction::K_STOP, nullptr);
804 }
805 
806 // static
ThreadMain(void * io)807 void InspectorIo::ThreadMain(void* io)
808 {
809     static_cast<InspectorIo*>(io)->ThreadMain();
810 }
811 
ThreadMain()812 void InspectorIo::ThreadMain()
813 {
814     uv_loop_t loop;
815     loop.data = nullptr;
816     int err = uv_loop_init(&loop);
817     CHECK_EQ(err, 0);
818     std::shared_ptr<RequestQueueData> queue(new RequestQueueData(&loop), RequestQueueData::CloseAndFree);
819     std::string scriptPath = ScriptPath(&loop, scriptName);
820     std::unique_ptr<InspectorIoDelegate> delegate(
821         new InspectorIoDelegate(queue, mainThread, id, scriptPath, scriptName));
822     std::string host;
823     int port;
824     int pid;
825     {
826         ExclusiveAccess<HostPort>::Scoped scopedHostPort(hostPort);
827         host = scopedHostPort->GetHost();
828         port = scopedHostPort->GetPort();
829         pid = scopedHostPort->GetPid();
830     }
831     InspectorSocketServer server(std::move(delegate), &loop, std::move(host), port, inspectPublishUid, stderr, pid);
832     requestQueue = queue->GetHandle();
833     // Its lifetime is now that of the server delegate
834     queue.reset();
835     {
836         Mutex::ScopedLock scopedLock(threadStartLock);
837         if (server.Start()) {
838             ExclusiveAccess<HostPort>::Scoped scopedHostPort(hostPort);
839             scopedHostPort->SetPort(server.GetPort());
840         }
841         threadStartCondition.Broadcast(scopedLock);
842     }
843     uv_run(&loop, UV_RUN_DEFAULT);
844     CheckedUvLoopClose(&loop);
845 }
846 
GetWsUrl() const847 std::string InspectorIo::GetWsUrl() const
848 {
849     ExclusiveAccess<HostPort>::Scoped scopedHostPort(hostPort);
850     return FormatWsAddress(scopedHostPort->GetHost(), scopedHostPort->GetPort(), id, true);
851 }
852 
853 namespace {
854 
855 using jsvm::inspector::TwoByteValue;
856 
857 using v8::Local;
858 using v8::Context;
859 using v8::Function;
860 using v8::HandleScope;
861 using v8::Isolate;
862 using v8::Message;
863 using v8::Object;
864 using v8::Value;
865 
866 using v8_inspector::StringBuffer;
867 using v8_inspector::StringView;
868 using v8_inspector::V8InspectorClient;
869 using v8_inspector::V8Inspector;
870 
ToProtocolString(Isolate * isolate,Local<Value> value)871 std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate, Local<Value> value)
872 {
873     TwoByteValue buffer(isolate, value);
874     return StringBuffer::create(StringView(*buffer, buffer.GetLength()));
875 }
876 
877 const int CONTEXT_GROUP_ID = 1;
878 
GetWorkerLabel(Environment * env)879 std::string GetWorkerLabel(Environment* env)
880 {
881     std::ostringstream result;
882     result << "Worker["
883            << "env->thread_id()"
884            << "]";
885     return result.str();
886 }
887 
888 class ChannelImpl final : public v8_inspector::V8Inspector::Channel {
889 public:
ChannelImpl(const std::unique_ptr<V8Inspector> & inspector,std::unique_ptr<InspectorSessionDelegate> delegate,std::shared_ptr<MainThreadHandle> mainThread,bool preventShutdown)890     explicit ChannelImpl(const std::unique_ptr<V8Inspector>& inspector,
891                          std::unique_ptr<InspectorSessionDelegate> delegate,
892                          std::shared_ptr<MainThreadHandle> mainThread,
893                          bool preventShutdown)
894         : delegate(std::move(delegate)), preventShutdown(preventShutdown)
895     {
896         session =
897             inspector->connect(CONTEXT_GROUP_ID, this, StringView(), V8Inspector::ClientTrustLevel::kFullyTrusted);
898     }
899 
900     ~ChannelImpl() = default;
901 
DispatchProtocolMessage(const StringView & message)902     void DispatchProtocolMessage(const StringView& message)
903     {
904         session->dispatchProtocolMessage(message);
905     }
906 
SchedulePauseOnNextStatement(const std::string & reason)907     void SchedulePauseOnNextStatement(const std::string& reason)
908     {
909         std::unique_ptr<StringBuffer> buffer = Utf8ToStringView(reason);
910         session->schedulePauseOnNextStatement(buffer->string(), buffer->string());
911     }
912 
PreventShutdown()913     bool PreventShutdown()
914     {
915         return preventShutdown;
916     }
917 
918 private:
sendResponse(int callId,std::unique_ptr<v8_inspector::StringBuffer> message)919     void sendResponse(int callId, std::unique_ptr<v8_inspector::StringBuffer> message) override
920     {
921         SendMessageToFrontend(message->string());
922     }
923 
sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message)924     void sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message) override
925     {
926         SendMessageToFrontend(message->string());
927     }
928 
flushProtocolNotifications()929     void flushProtocolNotifications() override {}
930 
SendMessageToFrontend(const StringView & message)931     void SendMessageToFrontend(const StringView& message)
932     {
933         delegate->SendMessageToFrontend(message);
934     }
935 
sendMessageToFrontend(const std::string & message)936     void sendMessageToFrontend(const std::string& message)
937     {
938         SendMessageToFrontend(Utf8ToStringView(message)->string());
939     }
940 
941     std::unique_ptr<InspectorSessionDelegate> delegate;
942     std::unique_ptr<v8_inspector::V8InspectorSession> session;
943     bool preventShutdown;
944 };
945 
946 class SameThreadInspectorSession : public InspectorSession {
947 public:
SameThreadInspectorSession(int sessionId,std::shared_ptr<InspectorClient> client)948     SameThreadInspectorSession(int sessionId, std::shared_ptr<InspectorClient> client)
949         : sessionId(sessionId), client(client)
950     {}
951     ~SameThreadInspectorSession() override;
952     void Dispatch(const v8_inspector::StringView& message) override;
953 
954 private:
955     int sessionId;
956     std::weak_ptr<InspectorClient> client;
957 };
958 
959 } // namespace
960 
961 class InspectorClient : public V8InspectorClient {
962 public:
InspectorClient(Environment * env,bool isMain)963     explicit InspectorClient(Environment* env, bool isMain) : env(env), isMain(isMain)
964     {
965         client = V8Inspector::create(env->isolate, this);
966         std::string name = isMain ? GetHumanReadableProcessName() : GetWorkerLabel(env);
967         ContextInfo info(name);
968         info.isDefault = true;
969         ContextCreated(env->context(), info);
970     }
971 
runMessageLoopOnPause(int contextGroupId)972     void runMessageLoopOnPause(int contextGroupId) override
973     {
974         waitingForResume = true;
975         RunMessageLoop();
976     }
977 
WaitForSessionsDisconnect()978     void WaitForSessionsDisconnect()
979     {
980         waitingForSessionsDisconnect = true;
981         RunMessageLoop();
982     }
983 
WaitForFrontend()984     void WaitForFrontend()
985     {
986         waitingForFrontend = true;
987         RunMessageLoop();
988     }
989 
maxAsyncCallStackDepthChanged(int depth)990     void maxAsyncCallStackDepthChanged(int depth) override
991     {
992         if (waitingForSessionsDisconnect) {
993             // V8 isolate is mostly done and is only letting Inspector protocol
994             // clients gather data.
995             return;
996         }
997     }
998 
ContextCreated(Local<Context> context,const ContextInfo & info)999     void ContextCreated(Local<Context> context, const ContextInfo& info)
1000     {
1001         auto nameBuffer = Utf8ToStringView(info.name);
1002         auto originBuffer = Utf8ToStringView(info.origin);
1003         std::unique_ptr<StringBuffer> auxDataBuffer;
1004 
1005         v8_inspector::V8ContextInfo v8info(context, CONTEXT_GROUP_ID, nameBuffer->string());
1006         v8info.origin = originBuffer->string();
1007 
1008         if (info.isDefault) {
1009             auxDataBuffer = Utf8ToStringView("{\"isDefault\":true}");
1010         } else {
1011             auxDataBuffer = Utf8ToStringView("{\"isDefault\":false}");
1012         }
1013         v8info.auxData = auxDataBuffer->string();
1014 
1015         client->contextCreated(v8info);
1016     }
1017 
ContextDestroyed(Local<Context> context)1018     void ContextDestroyed(Local<Context> context)
1019     {
1020         client->contextDestroyed(context);
1021     }
1022 
quitMessageLoopOnPause()1023     void quitMessageLoopOnPause() override
1024     {
1025         waitingForResume = false;
1026     }
1027 
runIfWaitingForDebugger(int contextGroupId)1028     void runIfWaitingForDebugger(int contextGroupId) override
1029     {
1030         waitingForFrontend = false;
1031     }
1032 
ConnectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate,bool preventShutdown)1033     int ConnectFrontend(std::unique_ptr<InspectorSessionDelegate> delegate, bool preventShutdown)
1034     {
1035         int sessionId = nextSessionId++;
1036         channels[sessionId] =
1037             std::make_unique<ChannelImpl>(client, std::move(delegate), GetThreadHandle(), preventShutdown);
1038         return sessionId;
1039     }
1040 
DisconnectFrontend(int sessionId)1041     void DisconnectFrontend(int sessionId)
1042     {
1043         auto it = channels.find(sessionId);
1044         if (it == channels.end()) {
1045             return;
1046         }
1047         channels.erase(it);
1048         if (waitingForSessionsDisconnect && !isMain) {
1049             waitingForSessionsDisconnect = false;
1050         }
1051     }
1052 
DispatchMessageFromFrontend(int sessionId,const StringView & message)1053     void DispatchMessageFromFrontend(int sessionId, const StringView& message)
1054     {
1055         channels[sessionId]->DispatchProtocolMessage(message);
1056     }
1057 
ensureDefaultContextInGroup(int contextGroupId)1058     Local<Context> ensureDefaultContextInGroup(int contextGroupId) override
1059     {
1060         return env->context();
1061     }
1062 
ReportUncaughtException(Local<Value> error,Local<Message> message)1063     void ReportUncaughtException(Local<Value> error, Local<Message> message)
1064     {
1065         Isolate* isolate = env->isolate;
1066         Local<Context> context = env->context();
1067 
1068         int scriptId = message->GetScriptOrigin().ScriptId();
1069 
1070         Local<v8::StackTrace> stackTrace = message->GetStackTrace();
1071 
1072         if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0 &&
1073             scriptId == stackTrace->GetFrame(isolate, 0)->GetScriptId()) {
1074             scriptId = 0;
1075         }
1076 
1077         const uint8_t details[] = "Uncaught";
1078 
1079         client->exceptionThrown(context, StringView(details, sizeof(details) - 1), error,
1080                                 ToProtocolString(isolate, message->Get())->string(),
1081                                 ToProtocolString(isolate, message->GetScriptResourceName())->string(),
1082                                 message->GetLineNumber(context).FromMaybe(0),
1083                                 message->GetStartColumn(context).FromMaybe(0), client->createStackTrace(stackTrace),
1084                                 scriptId);
1085     }
1086 
startRepeatingTimer(double interval,TimerCallback callback,void * data)1087     void startRepeatingTimer(double interval, TimerCallback callback, void* data) override {}
1088 
cancelTimer(void * data)1089     void cancelTimer(void* data) override {}
1090 
SchedulePauseOnNextStatement(const std::string & reason)1091     void SchedulePauseOnNextStatement(const std::string& reason)
1092     {
1093         for (const auto& idChannel : channels) {
1094             idChannel.second->SchedulePauseOnNextStatement(reason);
1095         }
1096     }
1097 
HasConnectedSessions()1098     bool HasConnectedSessions()
1099     {
1100         for (const auto& idChannel : channels) {
1101             // Other sessions are "invisible" more most purposes
1102             if (idChannel.second->PreventShutdown()) {
1103                 return true;
1104             }
1105         }
1106         return false;
1107     }
1108 
GetThreadHandle()1109     std::shared_ptr<MainThreadHandle> GetThreadHandle()
1110     {
1111         if (!interface) {
1112             interface = std::make_shared<MainThreadInterface>(static_cast<Agent*>(env->GetInspectorAgent()));
1113         }
1114         return interface->GetHandle();
1115     }
1116 
IsActive()1117     bool IsActive()
1118     {
1119         return !channels.empty();
1120     }
1121 
1122 private:
ShouldRunMessageLoop()1123     bool ShouldRunMessageLoop()
1124     {
1125         if (waitingForFrontend) {
1126             return true;
1127         }
1128         if (waitingForSessionsDisconnect || waitingForResume) {
1129             return HasConnectedSessions();
1130         }
1131         return false;
1132     }
1133 
RunMessageLoop()1134     void RunMessageLoop()
1135     {
1136         if (runningNestedLoop) {
1137             return;
1138         }
1139 
1140         runningNestedLoop = true;
1141 
1142         while (ShouldRunMessageLoop()) {
1143             if (interface) {
1144                 interface->WaitForFrontendEvent();
1145             }
1146             env->RunAndClearInterrupts();
1147         }
1148         runningNestedLoop = false;
1149     }
1150 
currentTimeMS()1151     double currentTimeMS() override
1152     {
1153         return env->platform()->CurrentClockTimeMillis();
1154     }
1155 
1156     Environment* env;
1157     bool isMain;
1158     bool runningNestedLoop = false;
1159     std::unique_ptr<V8Inspector> client;
1160     std::unordered_map<int, std::unique_ptr<ChannelImpl>> channels;
1161     int nextSessionId = 1;
1162     bool waitingForResume = false;
1163     bool waitingForFrontend = false;
1164     bool waitingForSessionsDisconnect = false;
1165     // Allows accessing Inspector from non-main threads
1166     std::shared_ptr<MainThreadInterface> interface;
1167 };
1168 
Agent(Environment * env)1169 Agent::Agent(Environment* env) : parentEnv(env) {}
1170 
1171 Agent::~Agent() = default;
1172 
Start(const std::string & pathParam,std::shared_ptr<ExclusiveAccess<HostPort>> hostPortParam,bool isMain,bool waitForConnect)1173 bool Agent::Start(const std::string& pathParam,
1174                   std::shared_ptr<ExclusiveAccess<HostPort>> hostPortParam,
1175                   bool isMain,
1176                   bool waitForConnect)
1177 {
1178     path = pathParam;
1179     CHECK_NOT_NULL(hostPortParam);
1180     hostPort = hostPortParam;
1181 
1182     client = std::make_shared<InspectorClient>(parentEnv, isMain);
1183 
1184     if (!StartIoThread()) {
1185         return false;
1186     }
1187 
1188     if (waitForConnect) {
1189         client->WaitForFrontend();
1190     }
1191     return true;
1192 }
1193 
FindAvailablePort()1194 int FindAvailablePort()
1195 {
1196     constexpr int startPort = 9229;
1197     constexpr int endPort = 9999;
1198     constexpr int invalidPort = -1;
1199     int sockfd = -1;
1200 
1201     for (auto port = startPort; port <= endPort; ++port) {
1202         sockfd = socket(AF_INET, SOCK_STREAM, 0);
1203         if (sockfd < 0) {
1204             continue;
1205         }
1206         struct sockaddr_in addr;
1207         addr.sin_family = AF_INET;
1208         addr.sin_addr.s_addr = htonl(INADDR_ANY);
1209         addr.sin_port = htons(port);
1210 
1211         if (bind(sockfd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
1212             close(sockfd);
1213             if (errno == EADDRINUSE) {
1214                 continue;
1215             } else {
1216                 break;
1217             }
1218         }
1219         close(sockfd);
1220         return port;
1221     }
1222     return invalidPort;
1223 }
1224 
Start(const std::string & pathParam,int pid)1225 bool Agent::Start(const std::string& pathParam, int pid)
1226 {
1227     int port = FindAvailablePort();
1228     if (port < 0) {
1229         return false;
1230     }
1231     auto hostPort = std::make_shared<jsvm::ExclusiveAccess<jsvm::HostPort>>("localhost", port, pid);
1232     return Start(pathParam, hostPort, true, false);
1233 }
1234 
StartIoThread()1235 bool Agent::StartIoThread()
1236 {
1237     if (io != nullptr) {
1238         return true;
1239     }
1240 
1241     if (!client) {
1242         return false;
1243     }
1244 
1245     CHECK_NOT_NULL(client);
1246 
1247     io = InspectorIo::Start(client->GetThreadHandle(), path, hostPort, { false, true });
1248     if (io == nullptr) {
1249         return false;
1250     }
1251     return true;
1252 }
1253 
Stop()1254 void Agent::Stop()
1255 {
1256     io.reset();
1257 }
1258 
Connect(std::unique_ptr<InspectorSessionDelegate> delegate,bool preventShutdown)1259 std::unique_ptr<InspectorSession> Agent::Connect(std::unique_ptr<InspectorSessionDelegate> delegate,
1260                                                  bool preventShutdown)
1261 {
1262     if (!client) {
1263         return std::unique_ptr<InspectorSession> {};
1264     }
1265 
1266     CHECK_NOT_NULL(client);
1267 
1268     int sessionId = client->ConnectFrontend(std::move(delegate), preventShutdown);
1269     return std::make_unique<SameThreadInspectorSession>(sessionId, client);
1270 }
1271 
WaitForDisconnect()1272 void Agent::WaitForDisconnect()
1273 {
1274     CHECK_NOT_NULL(client);
1275     if (client->HasConnectedSessions()) {
1276         if (fprintf(stderr, "Waiting for the debugger to disconnect...\n") < 0) {
1277             return;
1278         }
1279         if (fflush(stderr) != 0) {
1280             return;
1281         }
1282     }
1283 
1284     client->ContextDestroyed(parentEnv->context());
1285 
1286     if (io != nullptr) {
1287         io->StopAcceptingNewConnections();
1288         client->WaitForSessionsDisconnect();
1289     }
1290 }
1291 
PauseOnNextJavascriptStatement(const std::string & reason)1292 void Agent::PauseOnNextJavascriptStatement(const std::string& reason)
1293 {
1294     client->SchedulePauseOnNextStatement(reason);
1295 }
1296 
IsActive()1297 bool Agent::IsActive()
1298 {
1299     if (client == nullptr) {
1300         return false;
1301     }
1302     return io != nullptr || client->IsActive();
1303 }
1304 
WaitForConnect()1305 void Agent::WaitForConnect()
1306 {
1307     CHECK_NOT_NULL(client);
1308     client->WaitForFrontend();
1309 }
1310 
~SameThreadInspectorSession()1311 SameThreadInspectorSession::~SameThreadInspectorSession()
1312 {
1313     auto clientLock = client.lock();
1314     if (clientLock) {
1315         clientLock->DisconnectFrontend(sessionId);
1316     }
1317 }
1318 
Dispatch(const v8_inspector::StringView & message)1319 void SameThreadInspectorSession::Dispatch(const v8_inspector::StringView& message)
1320 {
1321     auto clientLock = client.lock();
1322     if (clientLock) {
1323         clientLock->DispatchMessageFromFrontend(sessionId, message);
1324     }
1325 }
1326 
1327 } // namespace v8impl
1328 
New(JSVM_Env env)1329 jsvm::InspectorAgent* jsvm::InspectorAgent::New(JSVM_Env env)
1330 {
1331     return new v8impl::Agent(env);
1332 }
1333