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