• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2022-2025 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 <functional>
19 #include <memory>
20 #include <regex>
21 #include <string>
22 #include <utility>
23 
24 #include "include/console_call_type.h"
25 #include "include/tooling/pt_thread.h"
26 #include "json_serialization/serializable.h"
27 #include "macros.h"
28 #include "utils/expected.h"
29 #include "utils/json_builder.h"
30 #include "utils/json_parser.h"
31 #include "utils/logger.h"
32 
33 #include "connection/server.h"
34 #include "json_serialization/jrpc_error.h"
35 #include "types/custom_url_breakpoint_response.h"
36 #include "types/debugger_evaluation_request.h"
37 #include "types/debugger_call_function_on_request.h"
38 #include "types/location.h"
39 #include "types/numeric_id.h"
40 #include "types/possible_breakpoints_response.h"
41 #include "types/script_source_response.h"
42 #include "types/url_breakpoint_request.h"
43 #include "types/url_breakpoint_response.h"
44 
45 namespace ark::tooling::inspector {
InspectorServer(Server & server)46 InspectorServer::InspectorServer(Server &server) : server_(server) {}
47 
Kill()48 void InspectorServer::Kill()
49 {
50     server_.Kill();
51 }
52 
Run(const std::string & msg)53 void InspectorServer::Run(const std::string& msg)
54 {
55     server_.Run(msg);
56 }
57 
OnValidate(std::function<void ()> && handler)58 void InspectorServer::OnValidate(std::function<void()> &&handler)
59 {
60     server_.OnValidate([handler = std::move(handler)]() {
61         // Pause debugger events processing
62         handler();
63 
64         // At this point, the whole messaging is stopped due to:
65         // - Debugger events are not processed by the inspector after the call above;
66         // - Client messages are not processed as we are executing on the server thread.
67     });
68 }
69 
OnOpen(std::function<void ()> && handler)70 void InspectorServer::OnOpen(std::function<void()> &&handler)
71 {
72     server_.OnOpen([this, handler = std::move(handler)]() {
73         // A new connection is open, reinitialize the state
74         sessionManager_.EnumerateSessions([this](auto &id, [[maybe_unused]] auto thread) {
75             if (!id.empty()) {
76                 SendTargetAttachedToTarget(id);
77             }
78         });
79 
80         // Resume debugger events processing
81         handler();
82     });
83 }
84 
OnFail(std::function<void ()> && handler)85 void InspectorServer::OnFail(std::function<void()> &&handler)
86 {
87     server_.OnFail([handler = std::move(handler)]() {
88         // Resume debugger events processing
89         handler();
90     });
91 }
92 
GetPauseReasonString(PauseReason reason)93 static std::string_view GetPauseReasonString(PauseReason reason)
94 {
95     switch (reason) {
96         case PauseReason::EXCEPTION:
97             return "exception";
98         case PauseReason::BREAK_ON_START:
99             return "Break on start";
100         default:
101             return "other";
102     }
103     UNREACHABLE();
104 }
105 
CallDebuggerPaused(PtThread thread,const std::vector<BreakpointId> & hitBreakpoints,const std::optional<RemoteObject> & exception,PauseReason pauseReason,const std::function<void (const FrameInfoHandler &)> & enumerateFrames)106 void InspectorServer::CallDebuggerPaused(PtThread thread, const std::vector<BreakpointId> &hitBreakpoints,
107                                          const std::optional<RemoteObject> &exception, PauseReason pauseReason,
108                                          const std::function<void(const FrameInfoHandler &)> &enumerateFrames)
109 {
110     auto sessionId = sessionManager_.GetSessionIdByThread(thread);
111 
112     server_.Call(sessionId, "Debugger.paused", [&](auto &params) {
113         params.AddProperty("callFrames", [this, thread, &enumerateFrames](JsonArrayBuilder &callFrames) {
114             EnumerateCallFrames(callFrames, thread, enumerateFrames);
115         });
116 
117         params.AddProperty("hitBreakpoints", [&hitBreakpoints](JsonArrayBuilder &hitBreakpointsBuilder) {
118             AddHitBreakpoints(hitBreakpointsBuilder, hitBreakpoints);
119         });
120 
121         if (exception) {
122             params.AddProperty("data", *exception);
123         }
124 
125         params.AddProperty("reason", GetPauseReasonString(pauseReason));
126     });
127 }
128 
CallDebuggerResumed(PtThread thread)129 void InspectorServer::CallDebuggerResumed(PtThread thread)
130 {
131     server_.Call(sessionManager_.GetSessionIdByThread(thread), "Debugger.resumed");
132 }
133 
CallDebuggerScriptParsed(ScriptId scriptId,std::string_view sourceFile)134 void InspectorServer::CallDebuggerScriptParsed(ScriptId scriptId, std::string_view sourceFile)
135 {
136     server_.Call("", "Debugger.scriptParsed", [&sourceFile, &scriptId](auto &params) {
137         params.AddProperty("executionContextId", 0);
138         params.AddProperty("scriptId", std::to_string(scriptId));
139         params.AddProperty("url", sourceFile.data());
140         params.AddProperty("startLine", 0);
141         params.AddProperty("startColumn", 0);
142         params.AddProperty("endLine", std::numeric_limits<int>::max());
143         params.AddProperty("endColumn", std::numeric_limits<int>::max());
144         params.AddProperty("hash", "");
145     });
146 }
147 
CallRuntimeConsoleApiCalled(PtThread thread,ConsoleCallType type,uint64_t timestamp,const std::vector<RemoteObject> & arguments)148 void InspectorServer::CallRuntimeConsoleApiCalled(PtThread thread, ConsoleCallType type, uint64_t timestamp,
149                                                   const std::vector<RemoteObject> &arguments)
150 {
151     auto sessionId = sessionManager_.GetSessionIdByThread(thread);
152 
153     server_.Call(sessionId, "Runtime.consoleAPICalled", [&](auto &params) {
154         params.AddProperty("executionContextId", 0);
155         params.AddProperty("timestamp", timestamp);
156 
157         switch (type) {
158             case ConsoleCallType::LOG:
159                 params.AddProperty("type", "log");
160                 break;
161             case ConsoleCallType::DEBUG:
162                 params.AddProperty("type", "debug");
163                 break;
164             case ConsoleCallType::INFO:
165                 params.AddProperty("type", "info");
166                 break;
167             case ConsoleCallType::ERROR:
168                 params.AddProperty("type", "error");
169                 break;
170             case ConsoleCallType::WARNING:
171                 params.AddProperty("type", "warning");
172                 break;
173             default:
174                 UNREACHABLE();
175         }
176 
177         params.AddProperty("args", [&](JsonArrayBuilder &argsBuilder) {
178             for (const auto &argument : arguments) {
179                 argsBuilder.Add(argument);
180             }
181         });
182     });
183 }
184 
CallRuntimeExecutionContextCreated(PtThread thread)185 void InspectorServer::CallRuntimeExecutionContextCreated(PtThread thread)
186 {
187     auto sessionId = sessionManager_.GetSessionIdByThread(thread);
188 
189     std::string name;
190     if (thread != PtThread::NONE) {
191         name = "Thread #" + std::to_string(thread.GetId());
192     }
193 
194     server_.Call(sessionId, "Runtime.executionContextCreated", [&](auto &params) {
195         params.AddProperty("context", [&](JsonObjectBuilder &context) {
196             context.AddProperty("id", thread.GetId());
197             context.AddProperty("origin", "");
198             context.AddProperty("name", name);
199             context.AddProperty("uniqueId", GetExecutionContextUniqueId(thread));
200         });
201     });
202 }
203 
CallRuntimeExecutionContextsCleared()204 void InspectorServer::CallRuntimeExecutionContextsCleared()
205 {
206     server_.Call("Runtime.executionContextsCleared");
207 }
208 
CallTargetAttachedToTarget(PtThread thread)209 void InspectorServer::CallTargetAttachedToTarget(PtThread thread)
210 {
211     auto &sessionId = sessionManager_.AddSession(thread);
212     if (!sessionId.empty()) {
213         SendTargetAttachedToTarget(sessionId);
214     }
215 }
216 
CallTargetDetachedFromTarget(PtThread thread)217 void InspectorServer::CallTargetDetachedFromTarget(PtThread thread)
218 {
219     auto sessionId = sessionManager_.GetSessionIdByThread(thread);
220 
221     // Pause the server thread to ensure that there will be no dangling PtThreads
222     server_.Pause();
223 
224     sessionManager_.RemoveSession(sessionId);
225 
226     // Now no one will retrieve the detached thread from the sessions manager
227     server_.Continue();
228 
229     if (!sessionId.empty()) {
230         server_.Call("Target.detachedFromTarget",
231                      [&sessionId](auto &params) { params.AddProperty("session_id", sessionId); });
232     }
233 }
234 
OnCallDebuggerContinueToLocation(std::function<void (PtThread,std::string_view,size_t)> && handler)235 void InspectorServer::OnCallDebuggerContinueToLocation(
236     std::function<void(PtThread, std::string_view, size_t)> &&handler)
237 {
238     // clang-format off
239     server_.OnCall("Debugger.continueToLocation",
240         [this, handler = std::move(handler)](auto &sessionId, const auto &params) -> Server::MethodResponse {
241             auto location = Location::FromJsonProperty(params, "location");
242             if (!location) {
243                 LOG(INFO, DEBUGGER) << location.Error();
244                 return Unexpected(JRPCError(location.Error()));
245             }
246 
247             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
248             handler(thread, sourceManager_.GetSourceFileName(location->GetScriptId()),
249                     location->GetLineNumber());
250             return std::unique_ptr<JsonSerializable>();
251         });
252     // clang-format on
253 }
254 
OnCallDebuggerEnable(std::function<void ()> && handler)255 void InspectorServer::OnCallDebuggerEnable(std::function<void()> &&handler)
256 {
257     struct Response : public JsonSerializable {
258         void Serialize(JsonObjectBuilder &builder) const override
259         {
260             builder.AddProperty("debuggerId", 0);
261             builder.AddProperty("protocols", [](JsonArrayBuilder &) {});
262         }
263     };
264 
265     server_.OnCall("Debugger.enable", [handler = std::move(handler)](auto, auto &) {
266         handler();
267         return std::unique_ptr<JsonSerializable>(std::make_unique<Response>());
268     });
269 }
270 
OnCallDebuggerGetPossibleBreakpoints(std::function<std::set<size_t> (std::string_view,size_t,size_t,bool)> && handler)271 void InspectorServer::OnCallDebuggerGetPossibleBreakpoints(
272     std::function<std::set<size_t>(std::string_view, size_t, size_t, bool)> &&handler)
273 {
274     // clang-format off
275     server_.OnCall("Debugger.getPossibleBreakpoints",
276         [this, handler = std::move(handler)](auto &, const JsonObject &params) -> Server::MethodResponse {
277             auto optStart = Location::FromJsonProperty(params, "start");
278             if (!optStart) {
279                 LOG(INFO, DEBUGGER) << optStart.Error();
280                 return Unexpected(JRPCError(optStart.Error(), ErrorCode::PARSE_ERROR));
281             }
282 
283             auto scriptId = optStart->GetScriptId();
284 
285             size_t endLine = ~0U;
286             if (auto end = Location::FromJsonProperty(params, "end")) {
287                 if (end->GetScriptId() != scriptId) {
288                     std::string_view msg = "Script ids don't match";
289                     LOG(INFO, DEBUGGER) << msg;
290                     return Unexpected(JRPCError(msg, ErrorCode::INVALID_PARAMS));
291                 }
292 
293                 endLine = end->GetLineNumber();
294             }
295 
296             bool restrictToFunction = false;
297             if (auto prop = params.GetValue<JsonObject::BoolT>("restrictToFunction")) {
298                 restrictToFunction = *prop;
299             }
300 
301             auto lineNumbers = handler(sourceManager_.GetSourceFileName(scriptId), optStart->GetLineNumber(),
302                                        endLine, restrictToFunction);
303             auto response = std::make_unique<PossibleBreakpointsResponse>();
304             for (const auto &line : lineNumbers) {
305                 response->Add(Location(scriptId, line));
306             }
307             return std::unique_ptr<JsonSerializable>(std::move(response));
308         });
309     // clang-format on
310 }
311 
OnCallDebuggerGetScriptSource(std::function<std::string (std::string_view)> && handler)312 void InspectorServer::OnCallDebuggerGetScriptSource(std::function<std::string(std::string_view)> &&handler)
313 {
314     // clang-format off
315     server_.OnCall("Debugger.getScriptSource",
316         [this, handler = std::move(handler)](auto &, auto &params) -> Server::MethodResponse {
317             auto scriptId = ParseNumericId<ScriptId>(params, "scriptId");
318             if (scriptId) {
319                 auto sourceFile = sourceManager_.GetSourceFileName(*scriptId);
320                 return std::unique_ptr<JsonSerializable>(
321                     std::make_unique<ScriptSourceResponse>(handler(sourceFile)));
322             }
323             LOG(INFO, DEBUGGER) << scriptId.Error();
324             return Unexpected(JRPCError(scriptId.Error(), ErrorCode::PARSE_ERROR));
325         });
326     // clang-format on
327 }
328 
OnCallDebuggerPause(std::function<void (PtThread)> && handler)329 void InspectorServer::OnCallDebuggerPause(std::function<void(PtThread)> &&handler)
330 {
331     server_.OnCall("Debugger.pause", [this, handler = std::move(handler)](auto &sessionId, auto &) {
332         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
333         handler(thread);
334         return std::unique_ptr<JsonSerializable>();
335     });
336 }
337 
OnCallDebuggerRemoveBreakpoint(std::function<void (PtThread,BreakpointId)> && handler)338 void InspectorServer::OnCallDebuggerRemoveBreakpoint(std::function<void(PtThread, BreakpointId)> &&handler)
339 {
340     // clang-format off
341     server_.OnCall("Debugger.removeBreakpoint",
342         [this, handler = std::move(handler)](auto &sessionId, auto &params) -> Server::MethodResponse {
343             auto breakpointId = ParseNumericId<BreakpointId>(params, "breakpointId");
344             if (breakpointId) {
345                 handler(sessionManager_.GetThreadBySessionId(sessionId), *breakpointId);
346                 return std::unique_ptr<JsonSerializable>();
347             }
348             LOG(INFO, DEBUGGER) << breakpointId.Error();
349             return Unexpected(JRPCError(breakpointId.Error()));
350         });
351     // clang-format on
352 }
353 
IsPathEqual(const std::string_view & src,const std::string_view & dst)354 static bool IsPathEqual(const std::string_view &src, const std::string_view &dst)
355 {
356     if (src.size() != dst.size()) {
357         return false;
358     }
359 
360     for (size_t i = 0; i < src.size(); ++i) {
361         if ((src[i] == '\\' || src[i] == '/') && (dst[i] == '\\' || dst[i] == '/')) {
362             continue;
363         }
364         if (src[i] != dst[i]) {
365             return false;
366         }
367     }
368 
369     return true;
370 }
371 
GetUrlFileFilter(const std::string & url)372 static auto GetUrlFileFilter(const std::string &url)
373 {
374     static constexpr std::string_view FILE_PREFIX = "file://";
375     return [sourceFile = url.find(FILE_PREFIX) == 0 ? url.substr(FILE_PREFIX.size()) : url](auto fileName) {
376         return IsPathEqual(sourceFile, fileName);
377     };
378 }
379 
OnCallDebuggerRemoveBreakpointsByUrl(std::function<void (PtThread,SourceFileFilter)> && handler)380 void InspectorServer::OnCallDebuggerRemoveBreakpointsByUrl(std::function<void(PtThread, SourceFileFilter)> &&handler)
381 {
382     // clang-format off
383     server_.OnCall("Debugger.removeBreakpointsByUrl",
384         [this, handler = std::move(handler)](auto &sessionId, auto &params) -> Server::MethodResponse {
385             const auto *url = params.template GetValue<JsonObject::StringT>("url");
386             if (url == nullptr) {
387                 std::string_view msg = "No 'url' parameter was provided for Debugger.removeBreakpointsByUrl";
388                 LOG(INFO, DEBUGGER) << msg;
389                 return Unexpected(JRPCError(msg, ErrorCode::PARSE_ERROR));
390             }
391             handler(sessionManager_.GetThreadBySessionId(sessionId), GetUrlFileFilter(*url));
392             return std::unique_ptr<JsonSerializable>();
393     });
394     // clang-format on
395 }
396 
OnCallDebuggerRestartFrame(std::function<void (PtThread,FrameId)> && handler)397 void InspectorServer::OnCallDebuggerRestartFrame(std::function<void(PtThread, FrameId)> &&handler)
398 {
399     struct Response : public JsonSerializable {
400         void Serialize(JsonObjectBuilder &builder) const override
401         {
402             builder.AddProperty("callFrames", [](JsonArrayBuilder &) {});
403         }
404     };
405     // clang-format off
406     server_.OnCall("Debugger.restartFrame",
407         [this, handler = std::move(handler)](auto &sessionId, auto &params) -> Server::MethodResponse {
408             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
409 
410             auto frameId = ParseNumericId<FrameId>(params, "callFrameId");
411             if (!frameId) {
412                 LOG(INFO, DEBUGGER) << frameId.Error();
413                 return Unexpected(JRPCError(frameId.Error(), ErrorCode::PARSE_ERROR));
414             }
415 
416             handler(thread, *frameId);
417             return std::unique_ptr<JsonSerializable>(std::make_unique<Response>());
418         });
419     // clang-format on
420 }
421 
OnCallDebuggerResume(std::function<void (PtThread)> && handler)422 void InspectorServer::OnCallDebuggerResume(std::function<void(PtThread)> &&handler)
423 {
424     server_.OnCall("Debugger.resume", [this, handler = std::move(handler)](auto &sessionId, auto &) {
425         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
426         handler(thread);
427         return std::unique_ptr<JsonSerializable>();
428     });
429 }
430 
OnCallDebuggerSetBreakpoint(std::function<SetBreakpointHandler> && handler)431 void InspectorServer::OnCallDebuggerSetBreakpoint(std::function<SetBreakpointHandler> &&handler)
432 {
433     class Response : public JsonSerializable {
434     public:
435         explicit Response(std::string &&bpId, Location &&loc)
436             : breakpointId_(std::move(bpId)), location_(std::move(loc))
437         {
438         }
439 
440         void Serialize(JsonObjectBuilder &builder) const override
441         {
442             builder.AddProperty("breakpointId", breakpointId_);
443             builder.AddProperty("actualLocation", location_);
444         }
445 
446     private:
447         std::string breakpointId_;
448         Location location_;
449     };
450     // clang-format off
451     server_.OnCall("Debugger.setBreakpoint",
452         [this, handler = std::move(handler)](auto &sessionId, auto &params) -> Server::MethodResponse {
453             auto location = Location::FromJsonProperty(params, "location");
454             if (!location) {
455                 LOG(INFO, DEBUGGER) << location.Error();
456                 return Unexpected(JRPCError(location.Error(), ErrorCode::PARSE_ERROR));
457             }
458             auto condition = params.template GetValue<JsonObject::StringT>("condition");
459 
460             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
461 
462             auto sourceFile = sourceManager_.GetSourceFileName(location->GetScriptId());
463             std::set<std::string_view> sourceFiles;
464 
465             auto id = handler(
466                 thread, [sourceFile](auto fileName) { return fileName == sourceFile; },
467                 location->GetLineNumber(), sourceFiles, condition);
468             if (!id) {
469                 std::string_view msg = "Failed to set breakpoint";
470                 LOG(INFO, DEBUGGER) << msg;
471                 return Unexpected(JRPCError(msg, ErrorCode::INTERNAL_ERROR));
472             }
473             auto response = std::make_unique<Response>(std::move(std::to_string(*id)), std::move(*location));
474             return std::unique_ptr<JsonSerializable>(std::move(response));
475         });
476     // clang-format on
477 }
478 
SetBreakpointByUrl(const std::string & sessionId,const UrlBreakpointRequest & breakpointRequest,const std::function<SetBreakpointHandler> & handler)479 Expected<std::unique_ptr<UrlBreakpointResponse>, std::string> InspectorServer::SetBreakpointByUrl(
480     const std::string &sessionId, const UrlBreakpointRequest &breakpointRequest,
481     const std::function<SetBreakpointHandler> &handler)
482 {
483     std::function<bool(std::string_view)> sourceFileFilter;
484     if (const auto &url = breakpointRequest.GetUrl()) {
485         sourceFileFilter = GetUrlFileFilter(*url);
486     } else if (const auto &urlRegex = breakpointRequest.GetUrlRegex()) {
487         sourceFileFilter = [regex = std::regex(*urlRegex)](auto fileName) {
488             return std::regex_match(fileName.data(), regex);
489         };
490     } else {
491         // Either 'url' or 'urlRegex' must be specified - checked in parser
492         UNREACHABLE();
493     }
494 
495     const auto *condition = breakpointRequest.GetCondition().has_value() ? &*breakpointRequest.GetCondition() : nullptr;
496 
497     std::set<std::string_view> sourceFiles;
498     auto thread = sessionManager_.GetThreadBySessionId(sessionId);
499 
500     auto id = handler(thread, std::move(sourceFileFilter), breakpointRequest.GetLineNumber(), sourceFiles, condition);
501     if (!id) {
502         std::string msg = "Failed to set breakpoint";
503         LOG(INFO, DEBUGGER) << msg;
504         return Unexpected(msg);
505     }
506 
507     auto breakpointInfo = std::make_unique<UrlBreakpointResponse>(*id);
508     AddLocations(*breakpointInfo, sourceFiles, breakpointRequest.GetLineNumber(), thread);
509     return Expected<std::unique_ptr<UrlBreakpointResponse>, std::string>(std::move(breakpointInfo));
510 }
511 
OnCallDebuggerSetBreakpointByUrl(std::function<SetBreakpointHandler> && handler)512 void InspectorServer::OnCallDebuggerSetBreakpointByUrl(std::function<SetBreakpointHandler> &&handler)
513 {
514     // clang-format off
515     server_.OnCall("Debugger.setBreakpointByUrl",
516         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
517             auto optRequest = UrlBreakpointRequest::FromJson(params);
518             if (!optRequest) {
519                 std::stringstream ss;
520                 ss << "Parse error: " << optRequest.Error();
521                 auto msg = ss.str();
522                 LOG(INFO, DEBUGGER) << msg;
523                 return Unexpected(JRPCError(std::move(msg), ErrorCode::PARSE_ERROR));
524             }
525             auto optResponse = SetBreakpointByUrl(sessionId, *optRequest, handler);
526             if (optResponse) {
527                 return std::unique_ptr<JsonSerializable>(std::move(optResponse.Value()));
528             }
529             return Unexpected(JRPCError(optResponse.Error()));
530         });
531     // clang-format on
532 }
533 
ParseUrlBreakpointRequests(const std::vector<JsonObject::Value> & rawRequests)534 static Expected<std::vector<UrlBreakpointRequest>, std::string> ParseUrlBreakpointRequests(
535     const std::vector<JsonObject::Value> &rawRequests)
536 {
537     std::vector<UrlBreakpointRequest> requestedBreakpoints;
538     for (const auto &rawRequest : rawRequests) {
539         auto *jsonObject = rawRequest.Get<JsonObject::JsonObjPointer>();
540         if (jsonObject == nullptr) {
541             std::string msg = "Invalid 'locations' array in getPossibleAndSetBreakpointByUrl";
542             LOG(INFO, DEBUGGER) << msg;
543             return Unexpected(std::move(msg));
544         }
545         auto optBreakpointRequest = UrlBreakpointRequest::FromJson(**jsonObject);
546         if (!optBreakpointRequest) {
547             std::stringstream ss;
548             ss << "Invalid breakpoint request: " << optBreakpointRequest.Error();
549             auto msg = ss.str();
550             LOG(INFO, DEBUGGER) << msg;
551             return Unexpected(std::move(msg));
552         }
553         requestedBreakpoints.push_back(std::move(*optBreakpointRequest));
554     }
555     return requestedBreakpoints;
556 }
557 
OnCallDebuggerGetPossibleAndSetBreakpointByUrl(std::function<SetBreakpointHandler> && handler)558 void InspectorServer::OnCallDebuggerGetPossibleAndSetBreakpointByUrl(std::function<SetBreakpointHandler> &&handler)
559 {
560     // clang-format off
561     server_.OnCall("Debugger.getPossibleAndSetBreakpointByUrl",
562         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
563             auto rawRequests = params.GetValue<JsonObject::ArrayT>("locations");
564             if (rawRequests == nullptr) {
565                 std::string_view msg = "No 'locations' array in getPossibleAndSetBreakpointByUrl";
566                 LOG(INFO, DEBUGGER) << msg;
567                 return Unexpected(JRPCError(msg, ErrorCode::PARSE_ERROR));
568             }
569             auto optRequests = ParseUrlBreakpointRequests(*rawRequests);
570             if (!optRequests) {
571                 return Unexpected(JRPCError(std::move(optRequests.Error()), ErrorCode::PARSE_ERROR));
572             }
573 
574             auto response = std::make_unique<CustomUrlBreakpointLocations>();
575             for (const auto &req : *optRequests) {
576                 auto optResponse = SetBreakpointByUrl(sessionId, req, handler);
577                 if (!optResponse.HasValue()) {
578                     response->Add(CustomUrlBreakpointResponse(req.GetLineNumber()));
579                     continue;
580                 }
581 
582                 if (optResponse.Value()->GetLocations().size() != 0) {
583                     response->Add(optResponse.Value()->ToCustomUrlBreakpointResponse());
584                 } else {
585                     auto resp = CustomUrlBreakpointResponse(req.GetLineNumber());
586                     resp.SetBreakpointId(optResponse.Value()->GetBreakpointId());
587                     response->Add(std::move(resp));
588                 }
589             }
590             return std::unique_ptr<JsonSerializable>(std::move(response));
591         });
592     // clang-format on
593 }
594 
OnCallDebuggerSetBreakpointsActive(std::function<void (PtThread,bool)> && handler)595 void InspectorServer::OnCallDebuggerSetBreakpointsActive(std::function<void(PtThread, bool)> &&handler)
596 {
597     // clang-format off
598     server_.OnCall("Debugger.setBreakpointsActive",
599         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
600             bool active;
601             if (auto prop = params.GetValue<JsonObject::BoolT>("active")) {
602                 active = *prop;
603             } else {
604                 std::string_view msg = "No 'active' property";
605                 LOG(INFO, DEBUGGER) << msg;
606                 return Unexpected(JRPCError(msg));
607             }
608 
609             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
610             handler(thread, active);
611             return std::unique_ptr<JsonSerializable>();
612         });
613     // clang-format on
614 }
615 
OnCallDebuggerSetSkipAllPauses(std::function<void (PtThread,bool)> && handler)616 void InspectorServer::OnCallDebuggerSetSkipAllPauses(std::function<void(PtThread, bool)> &&handler)
617 {
618     // clang-format off
619     server_.OnCall("Debugger.setSkipAllPauses",
620         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
621             bool skip;
622             if (auto prop = params.GetValue<JsonObject::BoolT>("skip")) {
623                 skip = *prop;
624             } else {
625                 std::string_view msg = "No 'active' property";
626                 LOG(INFO, DEBUGGER) << msg;
627                 return Unexpected(JRPCError(msg, ErrorCode::INVALID_PARAMS));
628             }
629 
630             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
631             handler(thread, skip);
632             return std::unique_ptr<JsonSerializable>();
633         });
634     // clang-format on
635 }
636 
OnCallDebuggerSetPauseOnExceptions(std::function<void (PtThread,PauseOnExceptionsState)> && handler)637 void InspectorServer::OnCallDebuggerSetPauseOnExceptions(
638     std::function<void(PtThread, PauseOnExceptionsState)> &&handler)
639 {
640     // clang-format off
641     server_.OnCall("Debugger.setPauseOnExceptions",
642         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
643             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
644 
645             PauseOnExceptionsState state;
646             auto stateStr = params.GetValue<JsonObject::StringT>("state");
647             if (stateStr == nullptr) {
648                 std::string_view msg = "No 'state' property";
649                 LOG(INFO, DEBUGGER) << msg;
650                 return Unexpected(JRPCError(msg, ErrorCode::INVALID_PARAMS));
651             }
652 
653             if (*stateStr == "none") {
654                 state = PauseOnExceptionsState::NONE;
655             } else if (*stateStr == "caught") {
656                 state = PauseOnExceptionsState::CAUGHT;
657             } else if (*stateStr == "uncaught") {
658                 state = PauseOnExceptionsState::UNCAUGHT;
659             } else if (*stateStr == "all") {
660                 state = PauseOnExceptionsState::ALL;
661             } else {
662                 std::stringstream ss;
663                 ss << "Invalid 'state' value: " << *stateStr;
664                 auto msg = ss.str();
665                 LOG(INFO, DEBUGGER) << msg;
666                 return Unexpected(JRPCError(std::move(msg), ErrorCode::INVALID_PARAMS));
667             }
668 
669             handler(thread, state);
670             return std::unique_ptr<JsonSerializable>();
671         });
672     // clang-format on
673 }
674 
OnCallDebuggerStepInto(std::function<void (PtThread)> && handler)675 void InspectorServer::OnCallDebuggerStepInto(std::function<void(PtThread)> &&handler)
676 {
677     server_.OnCall("Debugger.stepInto", [this, handler = std::move(handler)](auto &sessionId, auto &) {
678         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
679         handler(thread);
680         return std::unique_ptr<JsonSerializable>();
681     });
682 }
683 
OnCallDebuggerStepOut(std::function<void (PtThread)> && handler)684 void InspectorServer::OnCallDebuggerStepOut(std::function<void(PtThread)> &&handler)
685 {
686     server_.OnCall("Debugger.stepOut", [this, handler = std::move(handler)](auto &sessionId, auto &) {
687         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
688         handler(thread);
689         return std::unique_ptr<JsonSerializable>();
690     });
691 }
692 
OnCallDebuggerStepOver(std::function<void (PtThread)> && handler)693 void InspectorServer::OnCallDebuggerStepOver(std::function<void(PtThread)> &&handler)
694 {
695     server_.OnCall("Debugger.stepOver", [this, handler = std::move(handler)](auto &sessionId, auto &) {
696         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
697         handler(thread);
698         return std::unique_ptr<JsonSerializable>();
699     });
700 }
701 
OnCallDebuggerEvaluateOnCallFrame(std::function<Expected<EvaluationResult,std::string> (PtThread,const std::string &,size_t)> && handler)702 void InspectorServer::OnCallDebuggerEvaluateOnCallFrame(
703     std::function<Expected<EvaluationResult, std::string>(PtThread, const std::string &, size_t)> &&handler)
704 {
705     // clang-format off
706     server_.OnCall("Debugger.evaluateOnCallFrame",
707         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
708             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
709 
710             auto optRequest = DebuggerEvaluationRequest::FromJson(params);
711             if (!optRequest) {
712                 LOG(INFO, DEBUGGER) << optRequest.Error();
713                 return Unexpected(JRPCError(std::move(optRequest.Error()), ErrorCode::PARSE_ERROR));
714             }
715 
716             auto optResult = handler(thread, optRequest->GetExpression(), optRequest->GetCallFrameId());
717             if (!optResult) {
718                 std::stringstream ss;
719                 ss << "Evaluation failed: " << optResult.Error();
720                 auto msg = ss.str();
721                 LOG(DEBUG, DEBUGGER) << msg;
722                 return Unexpected(JRPCError(std::move(msg), ErrorCode::INTERNAL_ERROR));
723             }
724 
725             return std::unique_ptr<JsonSerializable>(std::make_unique<EvaluationResult>(std::move(*optResult)));
726         });
727     // clang-format on
728 }
729 
OnCallDebuggerCallFunctionOn(std::function<Expected<EvaluationResult,std::string> (PtThread,const std::string &,size_t)> && handler)730 void InspectorServer::OnCallDebuggerCallFunctionOn(
731     std::function<Expected<EvaluationResult, std::string>(PtThread, const std::string &, size_t)> &&handler)
732 {
733     // clang-format off
734     server_.OnCall("Debugger.callFunctionOn",
735         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
736             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
737 
738             auto optRequest = DebuggerCallFunctionOnRequest::FromJson(params);
739             if (!optRequest) {
740                 LOG(INFO, DEBUGGER) << optRequest.Error();
741                 return Unexpected(JRPCError(std::move(optRequest.Error()), ErrorCode::PARSE_ERROR));
742             }
743 
744             auto optResult = handler(thread, optRequest->GetFunctionDeclaration(), optRequest->GetCallFrameId());
745             if (!optResult) {
746                 std::stringstream ss;
747                 ss << "Evaluation failed: " << optResult.Error();
748                 auto msg = ss.str();
749                 LOG(DEBUG, DEBUGGER) << msg;
750                 return Unexpected(JRPCError(std::move(msg), ErrorCode::INTERNAL_ERROR));
751             }
752 
753             return std::unique_ptr<JsonSerializable>(std::make_unique<EvaluationResult>(std::move(*optResult)));
754         });
755     // clang-format on
756 }
757 
OnCallDebuggerSetMixedDebugEnabled(std::function<void (PtThread,bool)> && handler)758 void InspectorServer::OnCallDebuggerSetMixedDebugEnabled(std::function<void(PtThread, bool)> &&handler)
759 {
760     // clang-format off
761     server_.OnCall("Debugger.setMixedDebugEnabled",
762         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
763             bool mixedDebugEnabled;
764             if (auto prop = params.GetValue<JsonObject::BoolT>("mixedDebugEnabled")) {
765                 mixedDebugEnabled = *prop;
766             } else {
767                 std::string_view msg = "No 'active' property";
768                 LOG(INFO, DEBUGGER) << msg;
769                 return Unexpected(JRPCError(msg, ErrorCode::INVALID_PARAMS));
770             }
771 
772             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
773             handler(thread, mixedDebugEnabled);
774             return std::unique_ptr<JsonSerializable>();
775         });
776     // clang-format on
777 }
778 
OnCallDebuggerDisable(std::function<void (PtThread)> && handler)779 void InspectorServer::OnCallDebuggerDisable(std::function<void(PtThread)> &&handler)
780 {
781     server_.OnCall("Debugger.disable", [this, handler = std::move(handler)](auto &sessionId, auto &) {
782         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
783         handler(thread);
784         return std::unique_ptr<JsonSerializable>();
785     });
786 }
787 
OnCallDebuggerClientDisconnect(std::function<void (PtThread)> && handler)788 void InspectorServer::OnCallDebuggerClientDisconnect(std::function<void(PtThread)> &&handler)
789 {
790     server_.OnCall("Debugger.clientDisconnect", [this, handler = std::move(handler)](auto &sessionId, auto &) {
791         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
792         handler(thread);
793         return std::unique_ptr<JsonSerializable>();
794     });
795 }
796 
OnCallDebuggerSetAsyncCallStackDepth(std::function<void (PtThread)> && handler)797 void InspectorServer::OnCallDebuggerSetAsyncCallStackDepth(std::function<void(PtThread)> &&handler)
798 {
799     server_.OnCall("Debugger.setAsyncCallStackDepth", [this, handler = std::move(handler)](auto &sessionId, auto &) {
800         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
801         handler(thread);
802         return std::unique_ptr<JsonSerializable>();
803     });
804 }
805 
OnCallDebuggerSetBlackboxPatterns(std::function<void (PtThread)> && handler)806 void InspectorServer::OnCallDebuggerSetBlackboxPatterns(std::function<void(PtThread)> &&handler)
807 {
808     server_.OnCall("Debugger.setBlackboxPatterns", [this, handler = std::move(handler)](auto &sessionId, auto &) {
809         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
810         handler(thread);
811         return std::unique_ptr<JsonSerializable>();
812     });
813 }
814 
OnCallDebuggerSmartStepInto(std::function<void (PtThread)> && handler)815 void InspectorServer::OnCallDebuggerSmartStepInto(std::function<void(PtThread)> &&handler)
816 {
817     server_.OnCall("Debugger.smartStepInto", [this, handler = std::move(handler)](auto &sessionId, auto &) {
818         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
819         handler(thread);
820         return std::unique_ptr<JsonSerializable>();
821     });
822 }
823 
OnCallDebuggerDropFrame(std::function<void (PtThread)> && handler)824 void InspectorServer::OnCallDebuggerDropFrame(std::function<void(PtThread)> &&handler)
825 {
826     server_.OnCall("Debugger.dropFrame", [this, handler = std::move(handler)](auto &sessionId, auto &) {
827         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
828         handler(thread);
829         return std::unique_ptr<JsonSerializable>();
830     });
831 }
832 
OnCallDebuggerSetNativeRange(std::function<void (PtThread)> && handler)833 void InspectorServer::OnCallDebuggerSetNativeRange(std::function<void(PtThread)> &&handler)
834 {
835     server_.OnCall("Debugger.setNativeRange", [this, handler = std::move(handler)](auto &sessionId, auto &) {
836         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
837         handler(thread);
838         return std::unique_ptr<JsonSerializable>();
839     });
840 }
841 
OnCallDebuggerReplyNativeCalling(std::function<void (PtThread)> && handler)842 void InspectorServer::OnCallDebuggerReplyNativeCalling(std::function<void(PtThread)> &&handler)
843 {
844     server_.OnCall("Debugger.replyNativeCalling", [this, handler = std::move(handler)](auto &sessionId, auto &) {
845         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
846         handler(thread);
847         return std::unique_ptr<JsonSerializable>();
848     });
849 }
850 
OnCallRuntimeEnable(std::function<void (PtThread)> && handler)851 void InspectorServer::OnCallRuntimeEnable(std::function<void(PtThread)> &&handler)
852 {
853     server_.OnCall("Runtime.enable", [this, handler = std::move(handler)](auto &sessionId, auto &) {
854         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
855         handler(thread);
856         return std::unique_ptr<JsonSerializable>();
857     });
858 }
859 
OnCallRuntimeGetProperties(std::function<std::vector<PropertyDescriptor> (PtThread,RemoteObjectId,bool)> && handler)860 void InspectorServer::OnCallRuntimeGetProperties(
861     std::function<std::vector<PropertyDescriptor>(PtThread, RemoteObjectId, bool)> &&handler)
862 {
863     class Response : public JsonSerializable {
864     public:
865         explicit Response(std::vector<PropertyDescriptor> props) : properties_(std::move(props)) {}
866         void Serialize(JsonObjectBuilder &builder) const override
867         {
868             builder.AddProperty("result", [&](JsonArrayBuilder &array) {
869                 for (auto &descriptor : properties_) {
870                     array.Add(descriptor);
871                 }
872             });
873         }
874 
875     private:
876         std::vector<PropertyDescriptor> properties_;
877     };
878     // clang-format off
879     server_.OnCall("Runtime.getProperties",
880         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
881             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
882 
883             auto objectId = ParseNumericId<RemoteObjectId>(params, "objectId");
884             if (!objectId) {
885                 LOG(INFO, DEBUGGER) << objectId.Error();
886                 return Unexpected(JRPCError(objectId.Error(), ErrorCode::PARSE_ERROR));
887             }
888 
889             auto generatePreview = false;
890             if (auto prop = params.GetValue<JsonObject::BoolT>("generatePreview")) {
891                 generatePreview = *prop;
892             }
893 
894             auto properties = handler(thread, *objectId, generatePreview);
895             return std::unique_ptr<JsonSerializable>(std::make_unique<Response>(std::move(properties)));
896         });
897     // clang-format on
898 }
899 
OnCallRuntimeRunIfWaitingForDebugger(std::function<void (PtThread)> && handler)900 void InspectorServer::OnCallRuntimeRunIfWaitingForDebugger(std::function<void(PtThread)> &&handler)
901 {
902     server_.OnCall("Runtime.runIfWaitingForDebugger", [this, handler = std::move(handler)](auto &sessionId, auto &) {
903         auto thread = sessionManager_.GetThreadBySessionId(sessionId);
904         handler(thread);
905         return std::unique_ptr<JsonSerializable>();
906     });
907 }
908 
OnCallRuntimeEvaluate(std::function<Expected<EvaluationResult,std::string> (PtThread,const std::string &)> && handler)909 void InspectorServer::OnCallRuntimeEvaluate(
910     std::function<Expected<EvaluationResult, std::string>(PtThread, const std::string &)> &&handler)
911 {
912     // clang-format off
913     server_.OnCall("Runtime.evaluate",
914         [this, handler = std::move(handler)](auto &sessionId, const JsonObject &params) -> Server::MethodResponse {
915             auto thread = sessionManager_.GetThreadBySessionId(sessionId);
916 
917             auto *expressionStr = params.GetValue<JsonObject::StringT>("expression");
918             if (expressionStr == nullptr || expressionStr->empty()) {
919                 std::string_view msg = "'expression' property is absent or empty";
920                 LOG(INFO, DEBUGGER) << msg;
921                 return Unexpected(JRPCError(msg, ErrorCode::PARSE_ERROR));
922             }
923 
924             auto optResult = handler(thread, *expressionStr);
925             if (!optResult) {
926                 std::stringstream ss;
927                 ss << "Evaluation failed: " << optResult.Error();
928                 auto msg = ss.str();
929                 LOG(DEBUG, DEBUGGER) << msg;
930                 return Unexpected(JRPCError(std::move(msg), ErrorCode::INTERNAL_ERROR));
931             }
932 
933             return std::unique_ptr<JsonSerializable>(std::make_unique<EvaluationResult>(std::move(*optResult)));
934         });
935     // clang-format on
936 }
937 
OnCallProfilerEnable()938 void InspectorServer::OnCallProfilerEnable()
939 {
940     server_.OnCall("Profiler.enable", [](auto &, auto &) { return std::unique_ptr<JsonSerializable>(); });
941 }
942 
OnCallProfilerDisable()943 void InspectorServer::OnCallProfilerDisable()
944 {
945     server_.OnCall("Profiler.disable", [](auto &, auto &) { return std::unique_ptr<JsonSerializable>(); });
946 }
947 
OnCallProfilerSetSamplingInterval(std::function<void (int32_t)> && handler)948 void InspectorServer::OnCallProfilerSetSamplingInterval(std::function<void(int32_t)> &&handler)
949 {
950     // clang-format off
951     server_.OnCall("Profiler.setSamplingInterval",
952         [handler = std::move(handler)](auto &, const JsonObject &params) -> Server::MethodResponse {
953             int32_t interval = 0;
954             if (auto prop = params.GetValue<JsonObject::NumT>("interval")) {
955                 interval = *prop;
956                 handler(interval);
957             } else {
958                 std::string_view msg = "No 'interval' property";
959                 LOG(INFO, DEBUGGER) << msg;
960                 return Unexpected(JRPCError(msg, ErrorCode::INTERNAL_ERROR));
961             }
962             return std::unique_ptr<JsonSerializable>();
963         });
964     // clang-format on
965 }
966 
OnCallProfilerStart(std::function<Expected<bool,std::string> ()> && handler)967 void InspectorServer::OnCallProfilerStart(std::function<Expected<bool, std::string>()> &&handler)
968 {
969     server_.OnCall("Profiler.start", [handler = std::move(handler)](auto &, auto &) -> Server::MethodResponse {
970         auto optResult = handler();
971         if (!optResult) {
972             std::stringstream ss;
973             ss << "Profiler failed: " << optResult.Error();
974             auto msg = ss.str();
975             LOG(DEBUG, DEBUGGER) << msg;
976             return Unexpected(JRPCError(std::move(msg), ErrorCode::INTERNAL_ERROR));
977         }
978         return std::unique_ptr<JsonSerializable>();
979     });
980 }
981 
OnCallProfilerStop(std::function<Expected<Profile,std::string> ()> && handler)982 void InspectorServer::OnCallProfilerStop(std::function<Expected<Profile, std::string>()> &&handler)
983 {
984     server_.OnCall("Profiler.stop", [handler = std::move(handler)](auto &, auto &) -> Server::MethodResponse {
985         auto optResult = handler();
986         if (!optResult) {
987             std::stringstream ss;
988             ss << "Profiler failed: " << optResult.Error();
989             auto msg = ss.str();
990             LOG(DEBUG, DEBUGGER) << msg;
991             return Unexpected(JRPCError(std::move(msg), ErrorCode::INTERNAL_ERROR));
992         }
993         return std::unique_ptr<JsonSerializable>(std::make_unique<Profile>(std::move(*optResult)));
994     });
995 }
996 
SendTargetAttachedToTarget(const std::string & sessionId)997 void InspectorServer::SendTargetAttachedToTarget(const std::string &sessionId)
998 {
999     server_.Call("Target.attachedToTarget", [&sessionId](auto &params) {
1000         params.AddProperty("sessionId", sessionId);
1001         params.AddProperty("targetInfo", [&sessionId](JsonObjectBuilder &targetInfo) {
1002             targetInfo.AddProperty("targetId", sessionId);
1003             targetInfo.AddProperty("type", "worker");
1004             targetInfo.AddProperty("title", sessionId);
1005             targetInfo.AddProperty("url", "");
1006             targetInfo.AddProperty("attached", true);
1007             targetInfo.AddProperty("canAccessOpener", false);
1008         });
1009         params.AddProperty("waitingForDebugger", true);
1010     });
1011 }
1012 
EnumerateCallFrames(JsonArrayBuilder & callFrames,PtThread thread,const std::function<void (const FrameInfoHandler &)> & enumerateFrames)1013 void InspectorServer::EnumerateCallFrames(JsonArrayBuilder &callFrames, PtThread thread,
1014                                           const std::function<void(const FrameInfoHandler &)> &enumerateFrames)
1015 {
1016     enumerateFrames([this, thread, &callFrames](auto frameId, auto methodName, auto sourceFile, auto lineNumber,
1017                                                 auto &scopeChain, auto &objThis) {
1018         CallFrameInfo callFrameInfo {frameId, sourceFile, methodName, lineNumber};
1019         AddCallFrameInfo(callFrames, callFrameInfo, scopeChain, thread, objThis);
1020     });
1021 }
1022 
AddCallFrameInfo(JsonArrayBuilder & callFrames,const CallFrameInfo & callFrameInfo,const std::vector<Scope> & scopeChain,PtThread thread,const std::optional<RemoteObject> & objThis)1023 void InspectorServer::AddCallFrameInfo(JsonArrayBuilder &callFrames, const CallFrameInfo &callFrameInfo,
1024                                        const std::vector<Scope> &scopeChain, [[maybe_unused]] PtThread thread,
1025                                        const std::optional<RemoteObject> &objThis)
1026 {
1027     callFrames.Add([&](JsonObjectBuilder &callFrame) {
1028         auto [scriptId, isNew] = sourceManager_.GetScriptId(callFrameInfo.sourceFile);
1029 
1030         if (isNew) {
1031             CallDebuggerScriptParsed(scriptId, callFrameInfo.sourceFile);
1032         }
1033 
1034         callFrame.AddProperty("callFrameId", std::to_string(callFrameInfo.frameId));
1035         callFrame.AddProperty("functionName", callFrameInfo.methodName.data());
1036         callFrame.AddProperty("location", Location(scriptId, callFrameInfo.lineNumber));
1037         callFrame.AddProperty("url", callFrameInfo.sourceFile.data());
1038 
1039         callFrame.AddProperty("scopeChain", [&](JsonArrayBuilder &scopeChainBuilder) {
1040             for (auto &scope : scopeChain) {
1041                 scopeChainBuilder.Add(scope);
1042             }
1043         });
1044 
1045         callFrame.AddProperty("this", objThis.value_or(RemoteObject::Undefined()));
1046         callFrame.AddProperty("canBeRestarted", true);
1047     });
1048 }
1049 
AddLocations(UrlBreakpointResponse & response,const std::set<std::string_view> & sourceFiles,size_t lineNumber,PtThread thread)1050 void InspectorServer::AddLocations(UrlBreakpointResponse &response, const std::set<std::string_view> &sourceFiles,
1051                                    size_t lineNumber, [[maybe_unused]] PtThread thread)
1052 {
1053     for (auto sourceFile : sourceFiles) {
1054         auto [scriptId, isNew] = sourceManager_.GetScriptId(sourceFile);
1055 
1056         if (isNew) {
1057             CallDebuggerScriptParsed(scriptId, sourceFile);
1058         }
1059         response.AddLocation(Location {scriptId, lineNumber});
1060     }
1061 }
1062 
1063 /* static */
AddHitBreakpoints(JsonArrayBuilder & hitBreakpointsBuilder,const std::vector<BreakpointId> & hitBreakpoints)1064 void InspectorServer::AddHitBreakpoints(JsonArrayBuilder &hitBreakpointsBuilder,
1065                                         const std::vector<BreakpointId> &hitBreakpoints)
1066 {
1067     for (auto id : hitBreakpoints) {
1068         hitBreakpointsBuilder.Add(std::to_string(id));
1069     }
1070 }
1071 
1072 /* static */
GetExecutionContextUniqueId(const PtThread & thread)1073 std::string InspectorServer::GetExecutionContextUniqueId(const PtThread &thread)
1074 {
1075     static int pid = os::thread::GetPid();
1076     std::stringstream ss;
1077     ss << pid << ':' << thread.GetId();
1078     return ss.str();
1079 }
1080 }  // namespace ark::tooling::inspector
1081