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