• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022-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 "inspector_server.h"
17 
18 #include "connection/server.h"
19 #include "types/location.h"
20 #include "types/numeric_id.h"
21 
22 #include "console_call_type.h"
23 #include "macros.h"
24 #include "tooling/pt_thread.h"
25 #include "utils/json_builder.h"
26 #include "utils/json_parser.h"
27 #include "utils/logger.h"
28 
29 #include <functional>
30 #include <regex>
31 #include <string>
32 #include <utility>
33 
34 namespace ark::tooling::inspector {
InspectorServer(Server & server)35 InspectorServer::InspectorServer(Server &server) : server_(server)
36 {
37     server_.OnCall("Debugger.enable", [](auto, auto &result, auto &) { result.AddProperty("debuggerId", "debugger"); });
38 }
39 
Kill()40 void InspectorServer::Kill()
41 {
42     server_.Kill();
43 }
44 
Run()45 void InspectorServer::Run()
46 {
47     server_.Run();
48 }
49 
OnValidate(std::function<void ()> && handler)50 void InspectorServer::OnValidate(std::function<void()> &&handler)
51 {
52     server_.OnValidate([handler = std::move(handler)]() {
53         // Pause debugger events processing
54         handler();
55 
56         // At this point, the whole messaging is stopped due to:
57         // - Debugger events are not processed by the inspector after the call above;
58         // - Client messages are not processed as we are executing on the server thread.
59     });
60 }
61 
OnOpen(std::function<void ()> && handler)62 void InspectorServer::OnOpen(std::function<void()> &&handler)
63 {
64     server_.OnOpen([this, handler = std::move(handler)]() {
65         // A new connection is open, reinitialize the state
66         sessionManager_.EnumerateSessions([this](auto &id, auto thread) {
67             sourceManager_.RemoveThread(thread);
68             if (!id.empty()) {
69                 SendTargetAttachedToTarget(id);
70             }
71         });
72 
73         // Resume debugger events processing
74         handler();
75     });
76 }
77 
OnFail(std::function<void ()> && handler)78 void InspectorServer::OnFail(std::function<void()> &&handler)
79 {
80     server_.OnFail([handler = std::move(handler)]() {
81         // Resume debugger events processing
82         handler();
83     });
84 }
85 
CallDebuggerPaused(PtThread thread,const std::vector<BreakpointId> & hitBreakpoints,const std::optional<RemoteObject> & exception,const std::function<void (const FrameInfoHandler &)> & enumerateFrames)86 void InspectorServer::CallDebuggerPaused(PtThread thread, const std::vector<BreakpointId> &hitBreakpoints,
87                                          const std::optional<RemoteObject> &exception,
88                                          const std::function<void(const FrameInfoHandler &)> &enumerateFrames)
89 {
90     auto sessionId = sessionManager_.GetSessionIdByThread(thread);
91 
92     server_.Call(sessionId, "Debugger.paused", [&](auto &params) {
93         params.AddProperty("callFrames", [this, thread, &enumerateFrames](JsonArrayBuilder &callFrames) {
94             EnumerateCallFrames(callFrames, thread, enumerateFrames);
95         });
96 
97         params.AddProperty("hitBreakpoints", [&hitBreakpoints](JsonArrayBuilder &hitBreakpointsBuilder) {
98             AddHitBreakpoints(hitBreakpointsBuilder, hitBreakpoints);
99         });
100 
101         if (exception) {
102             params.AddProperty("data", exception->ToJson());
103         }
104 
105         params.AddProperty("reason", exception.has_value() ? "exception" : "other");
106     });
107 }
108 
CallDebuggerResumed(PtThread thread)109 void InspectorServer::CallDebuggerResumed(PtThread thread)
110 {
111     server_.Call(sessionManager_.GetSessionIdByThread(thread), "Debugger.resumed");
112 }
113 
CallDebuggerScriptParsed(PtThread thread,ScriptId scriptId,std::string_view sourceFile)114 void InspectorServer::CallDebuggerScriptParsed(PtThread thread, ScriptId scriptId, std::string_view sourceFile)
115 {
116     auto sessionId = sessionManager_.GetSessionIdByThread(thread);
117     server_.Call(sessionId, "Debugger.scriptParsed", [&sourceFile, &thread, &scriptId](auto &params) {
118         params.AddProperty("executionContextId", thread.GetId());
119         params.AddProperty("scriptId", std::to_string(scriptId));
120         params.AddProperty("url", sourceFile.data());
121         params.AddProperty("startLine", 0);
122         params.AddProperty("startColumn", 0);
123         params.AddProperty("endLine", std::numeric_limits<int>::max());
124         params.AddProperty("endColumn", std::numeric_limits<int>::max());
125         params.AddProperty("hash", "");
126     });
127 }
128 
CallRuntimeConsoleApiCalled(PtThread thread,ConsoleCallType type,uint64_t timestamp,const std::vector<RemoteObject> & arguments)129 void InspectorServer::CallRuntimeConsoleApiCalled(PtThread thread, ConsoleCallType type, uint64_t timestamp,
130                                                   const std::vector<RemoteObject> &arguments)
131 {
132     auto sessionId = sessionManager_.GetSessionIdByThread(thread);
133 
134     server_.Call(sessionId, "Runtime.consoleAPICalled", [&](auto &params) {
135         params.AddProperty("executionContextId", thread.GetId());
136         params.AddProperty("timestamp", timestamp);
137 
138         switch (type) {
139             case ConsoleCallType::LOG:
140                 params.AddProperty("type", "log");
141                 break;
142             case ConsoleCallType::DEBUG:
143                 params.AddProperty("type", "debug");
144                 break;
145             case ConsoleCallType::INFO:
146                 params.AddProperty("type", "info");
147                 break;
148             case ConsoleCallType::ERROR:
149                 params.AddProperty("type", "error");
150                 break;
151             case ConsoleCallType::WARNING:
152                 params.AddProperty("type", "warning");
153                 break;
154             default:
155                 UNREACHABLE();
156         }
157 
158         params.AddProperty("args", [&](JsonArrayBuilder &argsBuilder) {
159             for (const auto &argument : arguments) {
160                 argsBuilder.Add(argument.ToJson());
161             }
162         });
163     });
164 }
165 
CallRuntimeExecutionContextCreated(PtThread thread)166 void InspectorServer::CallRuntimeExecutionContextCreated(PtThread thread)
167 {
168     auto sessionId = sessionManager_.GetSessionIdByThread(thread);
169 
170     std::string name;
171     if (thread != PtThread::NONE) {
172         name = "Thread #" + std::to_string(thread.GetId());
173     }
174 
175     server_.Call(sessionId, "Runtime.executionContextCreated", [&](auto &params) {
176         params.AddProperty("context", [&](JsonObjectBuilder &context) {
177             context.AddProperty("id", thread.GetId());
178             context.AddProperty("origin", "");
179             context.AddProperty("name", name);
180             context.AddProperty("uniqueId", GetExecutionContextUniqueId(thread));
181         });
182     });
183 }
184 
CallRuntimeExecutionContextsCleared()185 void InspectorServer::CallRuntimeExecutionContextsCleared()
186 {
187     server_.Call("Runtime.executionContextsCleared");
188 }
189 
CallTargetAttachedToTarget(PtThread thread)190 void InspectorServer::CallTargetAttachedToTarget(PtThread thread)
191 {
192     auto &sessionId = sessionManager_.AddSession(thread);
193     if (!sessionId.empty()) {
194         SendTargetAttachedToTarget(sessionId);
195     }
196 }
197 
CallTargetDetachedFromTarget(PtThread thread)198 void InspectorServer::CallTargetDetachedFromTarget(PtThread thread)
199 {
200     auto sessionId = sessionManager_.GetSessionIdByThread(thread);
201 
202     // Pause the server thread to ensure that there will be no dangling PtThreads
203     server_.Pause();
204 
205     sessionManager_.RemoveSession(sessionId);
206     sourceManager_.RemoveThread(thread);
207 
208     // Now no one will retrieve the detached thread from the sessions manager
209     server_.Continue();
210 
211     if (!sessionId.empty()) {
212         server_.Call("Target.detachedFromTarget",
213                      [&sessionId](auto &params) { params.AddProperty("session_id", sessionId); });
214     }
215 }
216 
OnCallDebuggerContinueToLocation(std::function<void (PtThread,std::string_view,size_t)> && handler)217 void InspectorServer::OnCallDebuggerContinueToLocation(
218     std::function<void(PtThread, std::string_view, size_t)> &&handler)
219 {
220     server_.OnCall(
221         "Debugger.continueToLocation", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &params) {
222             auto location = Location::FromJsonProperty(params, "location");
223             if (!location) {
224                 LOG(INFO, DEBUGGER) << location.Error();
225                 return;
226             }
227 
228             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
229 
230             handler(thread, sourceManager_.GetSourceFileName(location->GetScriptId()), location->GetLineNumber());
231         });
232 }
233 
OnCallDebuggerGetPossibleBreakpoints(std::function<std::set<size_t> (std::string_view,size_t,size_t,bool)> && handler)234 void InspectorServer::OnCallDebuggerGetPossibleBreakpoints(
235     std::function<std::set<size_t>(std::string_view, size_t, size_t, bool)> &&handler)
236 {
237     // clang-format off
238     server_.OnCall("Debugger.getPossibleBreakpoints",
239         [this, handler = std::move(handler)](auto &, auto &result, const JsonObject &params) {
240             auto start = Location::FromJsonProperty(params, "start");
241             if (!start) {
242                 LOG(INFO, DEBUGGER) << start.Error();
243                 return;
244             }
245 
246             auto scriptId = start->GetScriptId();
247 
248             size_t endLine = ~0U;
249             if (auto end = Location::FromJsonProperty(params, "end")) {
250                 if (end->GetScriptId() != scriptId) {
251                     LOG(INFO, DEBUGGER) << "Script ids don't match";
252                     return;
253                 }
254 
255                 endLine = end->GetLineNumber();
256             }
257 
258             bool restrictToFunction = false;
259             if (auto prop = params.GetValue<JsonObject::BoolT>("restrictToFunction")) {
260                 restrictToFunction = *prop;
261             }
262 
263             auto lineNumbers = handler(sourceManager_.GetSourceFileName(scriptId), start->GetLineNumber(),
264                                        endLine, restrictToFunction);
265 
266             result.AddProperty("locations", [scriptId, &lineNumbers](JsonArrayBuilder &array) {
267                 for (auto lineNumber : lineNumbers) {
268                     array.Add(Location(scriptId, lineNumber).ToJson());
269                 }
270             });
271         });
272     // clang-format on
273 }
274 
OnCallDebuggerGetScriptSource(std::function<std::string (std::string_view)> && handler)275 void InspectorServer::OnCallDebuggerGetScriptSource(std::function<std::string(std::string_view)> &&handler)
276 {
277     server_.OnCall("Debugger.getScriptSource",
278                    [this, handler = std::move(handler)](auto &, auto &result, auto &params) {
279                        if (auto scriptId = ParseNumericId<ScriptId>(params, "scriptId")) {
280                            auto sourceFile = sourceManager_.GetSourceFileName(*scriptId);
281                            result.AddProperty("scriptSource", handler(sourceFile));
282                        } else {
283                            LOG(INFO, DEBUGGER) << scriptId.Error();
284                        }
285                    });
286 }
287 
OnCallDebuggerPause(std::function<void (PtThread)> && handler)288 void InspectorServer::OnCallDebuggerPause(std::function<void(PtThread)> &&handler)
289 {
290     server_.OnCall("Debugger.pause", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
291         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
292         handler(thread);
293     });
294 }
295 
OnCallDebuggerRemoveBreakpoint(std::function<void (PtThread,BreakpointId)> && handler)296 void InspectorServer::OnCallDebuggerRemoveBreakpoint(std::function<void(PtThread, BreakpointId)> &&handler)
297 {
298     server_.OnCall("Debugger.removeBreakpoint",
299                    [this, handler = std::move(handler)](auto &sessionId, auto &, auto &params) {
300                        if (auto breakpointId = ParseNumericId<BreakpointId>(params, "breakpointId")) {
301                            handler(sessionManager_.GetThreadBySessionId(sessionId), *breakpointId);
302                        } else {
303                            LOG(INFO, DEBUGGER) << breakpointId.Error();
304                        }
305                    });
306 }
307 
OnCallDebuggerRestartFrame(std::function<void (PtThread,FrameId)> && handler)308 void InspectorServer::OnCallDebuggerRestartFrame(std::function<void(PtThread, FrameId)> &&handler)
309 {
310     // clang-format off
311     server_.OnCall("Debugger.restartFrame",
312         [this, handler = std::move(handler)](auto &sessionId, auto &result, auto &params) {
313             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
314 
315             auto frameId = ParseNumericId<FrameId>(params, "callFrameId");
316             if (!frameId) {
317                 LOG(INFO, DEBUGGER) << frameId.Error();
318                return;
319             }
320 
321             handler(thread, *frameId);
322 
323             result.AddProperty("callFrames", [](JsonArrayBuilder &) {});
324         });
325     // clang-format on
326 }
327 
OnCallDebuggerResume(std::function<void (PtThread)> && handler)328 void InspectorServer::OnCallDebuggerResume(std::function<void(PtThread)> &&handler)
329 {
330     server_.OnCall("Debugger.resume", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
331         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
332         handler(thread);
333     });
334 }
335 
OnCallDebuggerSetBreakpoint(std::function<std::optional<BreakpointId> (PtThread,const std::function<bool (std::string_view)> &,size_t,std::set<std::string_view> &)> && handler)336 void InspectorServer::OnCallDebuggerSetBreakpoint(
337     std::function<std::optional<BreakpointId>(PtThread, const std::function<bool(std::string_view)> &, size_t,
338                                               std::set<std::string_view> &)> &&handler)
339 {
340     // clang-format off
341     server_.OnCall("Debugger.setBreakpoint",
342         [this, handler = std::move(handler)](auto &sessionId, auto &result, auto &params) {
343             auto location = Location::FromJsonProperty(params, "location");
344             if (!location) {
345                 LOG(INFO, DEBUGGER) << location.Error();
346                 return;
347             }
348 
349             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
350 
351             auto sourceFile = sourceManager_.GetSourceFileName(location->GetScriptId());
352             std::set<std::string_view> sourceFiles;
353 
354             auto id = handler(
355                 thread, [sourceFile](auto fileName) { return fileName == sourceFile; },
356                 location->GetLineNumber(), sourceFiles);
357             if (!id) {
358                 LOG(INFO, DEBUGGER) << "Failed to set breakpoint";
359                 return;
360             }
361 
362             result.AddProperty("breakpointId", std::to_string(*id));
363             result.AddProperty("actualLocation", location->ToJson());
364         });
365     // clang-format on
366 }
367 
OnCallDebuggerSetBreakpointByUrl(std::function<std::optional<BreakpointId> (PtThread,const std::function<bool (std::string_view)> &,size_t,std::set<std::string_view> &)> && handler)368 void InspectorServer::OnCallDebuggerSetBreakpointByUrl(
369     std::function<std::optional<BreakpointId>(PtThread, const std::function<bool(std::string_view)> &, size_t,
370                                               std::set<std::string_view> &)> &&handler)
371 {
372     server_.OnCall("Debugger.setBreakpointByUrl", [this, handler = std::move(handler)](auto &sessionId, auto &result,
373                                                                                        const JsonObject &params) {
374         size_t lineNumber;
375         if (auto prop = params.GetValue<JsonObject::NumT>("lineNumber")) {
376             lineNumber = *prop + 1;
377         } else {
378             LOG(INFO, DEBUGGER) << "No 'lineNumber' property";
379             return;
380         }
381 
382         std::function<bool(std::string_view)> sourceFileFilter;
383         if (auto url = params.GetValue<JsonObject::StringT>("url")) {
384             sourceFileFilter = [sourceFile = url->find("file://") == 0 ? url->substr(std::strlen("file://")) : *url](
385                                    auto fileName) { return fileName == sourceFile; };
386         } else if (auto urlRegex = params.GetValue<JsonObject::StringT>("urlRegex")) {
387             sourceFileFilter = [regex = std::regex(*urlRegex)](auto fileName) {
388                 return std::regex_match(fileName.data(), regex);
389             };
390         } else {
391             LOG(INFO, DEBUGGER) << "No 'url' or 'urlRegex' properties";
392             return;
393         }
394 
395         std::set<std::string_view> sourceFiles;
396         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
397 
398         auto id = handler(thread, sourceFileFilter, lineNumber, sourceFiles);
399         if (!id) {
400             LOG(INFO, DEBUGGER) << "Failed to set breakpoint";
401             return;
402         }
403 
404         result.AddProperty("breakpointId", std::to_string(*id));
405         result.AddProperty("locations", [this, lineNumber, &sourceFiles, thread](JsonArrayBuilder &locations) {
406             AddBreakpointByUrlLocations(locations, sourceFiles, lineNumber, thread);
407         });
408     });
409 }
410 
OnCallDebuggerSetBreakpointsActive(std::function<void (PtThread,bool)> && handler)411 void InspectorServer::OnCallDebuggerSetBreakpointsActive(std::function<void(PtThread, bool)> &&handler)
412 {
413     // clang-format off
414     server_.OnCall("Debugger.setBreakpointsActive",
415         [this, handler = std::move(handler)](auto &sessionId, auto &, const JsonObject &params) {
416             bool active;
417             if (auto prop = params.GetValue<JsonObject::BoolT>("active")) {
418                 active = *prop;
419             } else {
420                 LOG(INFO, DEBUGGER) << "No 'active' property";
421                 return;
422             }
423 
424             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
425             handler(thread, active);
426         });
427     // clang-format on
428 }
429 
OnCallDebuggerSetPauseOnExceptions(std::function<void (PtThread,PauseOnExceptionsState)> && handler)430 void InspectorServer::OnCallDebuggerSetPauseOnExceptions(
431     std::function<void(PtThread, PauseOnExceptionsState)> &&handler)
432 {
433     // clang-format off
434     server_.OnCall("Debugger.setPauseOnExceptions",
435         [this, handler = std::move(handler)](auto &sessionId, auto &, const JsonObject &params) {
436             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
437 
438             PauseOnExceptionsState state;
439             auto stateStr = params.GetValue<JsonObject::StringT>("state");
440             if (stateStr == nullptr) {
441                 LOG(INFO, DEBUGGER) << "No 'state' property";
442                 return;
443             }
444 
445             if (*stateStr == "none") {
446                 state = PauseOnExceptionsState::NONE;
447             } else if (*stateStr == "caught") {
448                 state = PauseOnExceptionsState::CAUGHT;
449             } else if (*stateStr == "uncaught") {
450                 state = PauseOnExceptionsState::UNCAUGHT;
451             } else if (*stateStr == "all") {
452                 state = PauseOnExceptionsState::ALL;
453             } else {
454                 LOG(INFO, DEBUGGER) << "Invalid 'state' value: " << *stateStr;
455                 return;
456             }
457 
458             handler(thread, state);
459         });
460     // clang-format on
461 }
462 
OnCallDebuggerStepInto(std::function<void (PtThread)> && handler)463 void InspectorServer::OnCallDebuggerStepInto(std::function<void(PtThread)> &&handler)
464 {
465     server_.OnCall("Debugger.stepInto", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
466         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
467         handler(thread);
468     });
469 }
470 
OnCallDebuggerStepOut(std::function<void (PtThread)> && handler)471 void InspectorServer::OnCallDebuggerStepOut(std::function<void(PtThread)> &&handler)
472 {
473     server_.OnCall("Debugger.stepOut", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
474         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
475         handler(thread);
476     });
477 }
478 
OnCallDebuggerStepOver(std::function<void (PtThread)> && handler)479 void InspectorServer::OnCallDebuggerStepOver(std::function<void(PtThread)> &&handler)
480 {
481     server_.OnCall("Debugger.stepOver", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
482         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
483         handler(thread);
484     });
485 }
486 
OnCallRuntimeEnable(std::function<void (PtThread)> && handler)487 void InspectorServer::OnCallRuntimeEnable(std::function<void(PtThread)> &&handler)
488 {
489     server_.OnCall("Runtime.enable", [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
490         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
491         handler(thread);
492     });
493 }
494 
OnCallRuntimeGetProperties(std::function<std::vector<PropertyDescriptor> (PtThread,RemoteObjectId,bool)> && handler)495 void InspectorServer::OnCallRuntimeGetProperties(
496     std::function<std::vector<PropertyDescriptor>(PtThread, RemoteObjectId, bool)> &&handler)
497 {
498     // clang-format off
499     server_.OnCall("Runtime.getProperties",
500         [this, handler = std::move(handler)](auto &sessionId, auto &result, const JsonObject &params) {
501             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
502 
503             auto objectId = ParseNumericId<RemoteObjectId>(params, "objectId");
504             if (!objectId) {
505                 LOG(INFO, DEBUGGER) << objectId.Error();
506                 return;
507             }
508 
509             auto generatePreview = false;
510             if (auto prop = params.GetValue<JsonObject::BoolT>("generatePreview")) {
511                 generatePreview = *prop;
512             }
513 
514             result.AddProperty("result", [&](JsonArrayBuilder &array) {
515                 for (auto &descriptor : handler(thread, *objectId, generatePreview)) {
516                     array.Add(descriptor.ToJson());
517                 }
518             });
519         });
520     // clang-format on
521 }
522 
OnCallRuntimeRunIfWaitingForDebugger(std::function<void (PtThread)> && handler)523 void InspectorServer::OnCallRuntimeRunIfWaitingForDebugger(std::function<void(PtThread)> &&handler)
524 {
525     server_.OnCall("Runtime.runIfWaitingForDebugger",
526                    [this, handler = std::move(handler)](auto &sessionId, auto &, auto &) {
527                        auto thread = sessionManager_.GetThreadBySessionId(sessionId);
528                        handler(thread);
529                    });
530 }
531 
SendTargetAttachedToTarget(const std::string & sessionId)532 void InspectorServer::SendTargetAttachedToTarget(const std::string &sessionId)
533 {
534     server_.Call("Target.attachedToTarget", [&sessionId](auto &params) {
535         params.AddProperty("sessionId", sessionId);
536         params.AddProperty("targetInfo", [&sessionId](JsonObjectBuilder &targetInfo) {
537             targetInfo.AddProperty("targetId", sessionId);
538             targetInfo.AddProperty("type", "worker");
539             targetInfo.AddProperty("title", sessionId);
540             targetInfo.AddProperty("url", "");
541             targetInfo.AddProperty("attached", true);
542             targetInfo.AddProperty("canAccessOpener", false);
543         });
544         params.AddProperty("waitingForDebugger", true);
545     });
546 }
547 
EnumerateCallFrames(JsonArrayBuilder & callFrames,PtThread thread,const std::function<void (const FrameInfoHandler &)> & enumerateFrames)548 void InspectorServer::EnumerateCallFrames(JsonArrayBuilder &callFrames, PtThread thread,
549                                           const std::function<void(const FrameInfoHandler &)> &enumerateFrames)
550 {
551     enumerateFrames([this, thread, &callFrames](auto frameId, auto methodName, auto sourceFile, auto lineNumber,
552                                                 auto &scopeChain, auto &objThis) {
553         CallFrameInfo callFrameInfo {frameId, sourceFile, methodName, lineNumber};
554         AddCallFrameInfo(callFrames, callFrameInfo, scopeChain, thread, objThis);
555     });
556 }
557 
AddCallFrameInfo(JsonArrayBuilder & callFrames,const CallFrameInfo & callFrameInfo,const std::vector<Scope> & scopeChain,PtThread thread,const std::optional<RemoteObject> & objThis)558 void InspectorServer::AddCallFrameInfo(JsonArrayBuilder &callFrames, const CallFrameInfo &callFrameInfo,
559                                        const std::vector<Scope> &scopeChain, PtThread thread,
560                                        const std::optional<RemoteObject> &objThis)
561 {
562     callFrames.Add([&](JsonObjectBuilder &callFrame) {
563         auto [scriptId, isNew] = sourceManager_.GetScriptId(thread, callFrameInfo.sourceFile);
564 
565         if (isNew) {
566             CallDebuggerScriptParsed(thread, scriptId, callFrameInfo.sourceFile);
567         }
568 
569         callFrame.AddProperty("callFrameId", std::to_string(callFrameInfo.frameId));
570         callFrame.AddProperty("functionName", callFrameInfo.methodName.data());
571         callFrame.AddProperty("location", Location(scriptId, callFrameInfo.lineNumber).ToJson());
572         callFrame.AddProperty("url", callFrameInfo.sourceFile.data());
573 
574         callFrame.AddProperty("scopeChain", [&](JsonArrayBuilder &scopeChainBuilder) {
575             for (auto &scope : scopeChain) {
576                 scopeChainBuilder.Add(scope.ToJson());
577             }
578         });
579 
580         callFrame.AddProperty("this", objThis.value_or(RemoteObject::Undefined()).ToJson());
581         callFrame.AddProperty("canBeRestarted", true);
582     });
583 }
584 
AddBreakpointByUrlLocations(JsonArrayBuilder & locations,const std::set<std::string_view> & sourceFiles,size_t lineNumber,PtThread thread)585 void InspectorServer::AddBreakpointByUrlLocations(JsonArrayBuilder &locations,
586                                                   const std::set<std::string_view> &sourceFiles, size_t lineNumber,
587                                                   PtThread thread)
588 {
589     for (auto sourceFile : sourceFiles) {
590         locations.Add([this, lineNumber, thread, sourceFile](JsonObjectBuilder &location) {
591             auto [scriptId, isNew] = sourceManager_.GetScriptId(thread, sourceFile);
592 
593             if (isNew) {
594                 CallDebuggerScriptParsed(thread, scriptId, sourceFile);
595             }
596 
597             Location(scriptId, lineNumber).ToJson()(location);
598         });
599     }
600 }
601 
602 /* static */
AddHitBreakpoints(JsonArrayBuilder & hitBreakpointsBuilder,const std::vector<BreakpointId> & hitBreakpoints)603 void InspectorServer::AddHitBreakpoints(JsonArrayBuilder &hitBreakpointsBuilder,
604                                         const std::vector<BreakpointId> &hitBreakpoints)
605 {
606     for (auto id : hitBreakpoints) {
607         hitBreakpointsBuilder.Add(std::to_string(id));
608     }
609 }
610 
611 /* static */
GetExecutionContextUniqueId(const PtThread & thread)612 std::string InspectorServer::GetExecutionContextUniqueId(const PtThread &thread)
613 {
614     static int pid = os::thread::GetPid();
615     std::stringstream ss;
616     ss << pid << ':' << thread.GetId();
617     return ss.str();
618 }
619 }  // namespace ark::tooling::inspector
620