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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) { 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) -> 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 ¶ms) {
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