1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#ifndef DEBUG_H
6#define DEBUG_H
7
8#include "dap/protocol.h"
9#include "debuggerglobals.h"
10#include "interface/variable.h"
11
12#include <QString>
13#include <QUrl>
14#include <QUuid>
15
16#include <map>
17
18namespace DEBUG_NAMESPACE {
19
20#define undefined \
21 { \
22 }
23#define dapNumber dap::integer
24#define ReadonlyArray dap::array
25
26struct IDebugSession;
27struct IStackFrame;
28struct IExpression;
29struct Thread;
30struct Source;
31
32struct IRawStoppedDetails
33{
34 dap::optional<dap::string> reason;
35 dap::optional<dap::string> description;
36 dap::optional<dapNumber> threadId;
37 dap::optional<dap::string> text;
38 dap::optional<dapNumber> totalFrames;
39 dap::optional<bool> allThreadsStopped;
40 dap::optional<dap::string> framesErrorMessage;
41 dap::optional<dap::array<dapNumber>> hitBreakpointIds;
42};
43
44struct IRawModelUpdate
45{
46 dap::string sessionId;
47 dap::array<dap::Thread> threads;
48 dap::optional<IRawStoppedDetails> stoppedDetails;
49};
50
51struct IBreakpointData
52{
53 dap::optional<dap::string> id;
54 dap::optional<dapNumber> lineNumber;
55 dap::optional<dapNumber> column;
56 bool enabled = true;
57 dap::optional<dap::string> condition;
58 dap::optional<dap::string> logMessage;
59 dap::optional<dap::string> hitCondition;
60};
61
62struct IBreakpointUpdateData
63{
64 dap::optional<dap::string> condition;
65 dap::optional<dap::string> hitCondition;
66 dap::optional<dap::string> logMessage;
67 dap::optional<dapNumber> lineNumber;
68 dap::optional<dapNumber> column;
69};
70
71struct ITreeElement
72{
73 virtual dap::string getId() { return ""; }
74};
75
76struct IEnablement : public ITreeElement
77{
78 bool enabled = true;
79};
80
81struct IBaseBreakpoint : public IEnablement
82{
83 dap::optional<dap::string> condition;
84 dap::optional<dap::string> hitCondition;
85 dap::optional<dap::string> logMessage;
86 bool verified = false;
87 bool support = false; // suported.
88 dap::optional<dap::string> message;
89 dap::array<dap::string> sessionsThatVerified;
90 dap::optional<dapNumber> getIdFromAdapter(dap::string sessionId);
91};
92
93struct IInnerBreakpoint
94{
95 QUrl uri;
96 dapNumber lineNumber = 0;
97 dap::optional<dapNumber> endLineNumber;
98 dap::optional<dapNumber> column;
99 dap::optional<dapNumber> endColumn;
100 dap::any adapterData;
101};
102
103struct IBreakpoint : public IBaseBreakpoint, public IInnerBreakpoint
104{
105};
106
107struct IFunctionBreakpoint : public IBaseBreakpoint
108{
109 dap::string name;
110};
111
112struct IExceptionBreakpoint : public IBaseBreakpoint
113{
114 dap::string filter;
115 dap::string label;
116 dap::string description;
117};
118
119struct IDataBreakpoint : IBaseBreakpoint
120{
121 dap::string description;
122 dap::string dataId;
123 bool canPersist = false;
124 dap::DataBreakpointAccessType accessType;
125};
126
127struct IInstructionBreakpoint : public IBaseBreakpoint
128{
129 // instructionReference is the instruction 'address' from the debugger.
130 dap::string instructionReference;
131 dapNumber offset = 0;
132};
133
134struct IExceptionInfo
135{
136 dap::optional<dap::string> id;
137 dap::optional<dap::string> description;
138 dap::string breakMode;
139 dap::optional<dap::ExceptionDetails> details;
140};
141
142/**
143 * Base structs.
144 */
145
146struct Enablement : public IEnablement
147{
148 Enablement(bool _enabled, dap::string &_id)
149 : id(_id)
150 {
151 enabled = _enabled;
152 }
153
154 dap::string getId() override
155 {
156 return id;
157 }
158
159private:
160 dap::string id;
161};
162
163struct IBreakpointSessionData : public dap::Breakpoint
164{
165 bool supportsConditionalBreakpoints;
166 bool supportsHitConditionalBreakpoints;
167 bool supportsLogPoints;
168 bool supportsFunctionBreakpoints;
169 bool supportsDataBreakpoints;
170 bool supportsInstructionBreakpoints;
171 dap::string sessionId;
172};
173
174struct BaseBreakpoint : public IBaseBreakpoint
175{
176 BaseBreakpoint(
177 bool _enabled,
178 dap::optional<dap::string> _hitCondition,
179 dap::optional<dap::string> _condition,
180 dap::optional<dap::string> _logMessage,
181 const dap::string &_id)
182 : id(_id)
183 {
184 enabled = _enabled;
185 hitCondition = _hitCondition;
186 condition = _condition;
187 logMessage = _logMessage;
188 }
189
190 virtual ~BaseBreakpoint() {}
191
192 void setSessionData(dap::string &sessionId, dap::optional<IBreakpointSessionData> data)
193 {
194 if (!data) {
195 auto it = sessionData.begin();
196 for (; it != sessionData.end(); ++it) {
197 if (sessionId == it->first) {
198 it = sessionData.erase(it);
199 }
200 }
201 } else {
202 data.value().sessionId = sessionId;
203 sessionData.insert(std::pair<dap::string, IBreakpointSessionData>(sessionId, data.value()));
204 }
205
206 // TODO(mozart)
207 }
208
209 dap::optional<dap::string> message()
210 {
211 if (!data) {
212 return undefined;
213 }
214 return data.value().message;
215 }
216
217 bool verified()
218 {
219 if (data) {
220 return data.value().verified;
221 }
222 return true;
223 }
224
225 dap::array<dap::string> sessionsThatVerified()
226 {
227 dap::array<dap::string> sessionIds;
228
229 auto it = sessionData.begin();
230 for (; it != sessionData.end(); ++it) {
231 if (it->second.verified) {
232 sessionIds.push_back(it->first);
233 }
234 }
235 return sessionIds;
236 }
237
238 dap::optional<dapNumber> getIdFromAdapter(dap::string &sessionId)
239 {
240 dap::optional<IBreakpointSessionData> data = getData(sessionId);
241 if (data) {
242 return data.value().id;
243 }
244 return undefined;
245 }
246
247 dap::optional<IBreakpointSessionData> getData(dap::string &sessionId)
248 {
249 auto it = sessionData.begin();
250 bool bFound = false;
251 for (; it != sessionData.end(); ++it) {
252 if (it->first == sessionId) {
253 data = it->second;
254 bFound = true;
255 break;
256 }
257 }
258 if (bFound) {
259 return data;
260 }
261 return undefined;
262 }
263
264 dap::optional<dap::Breakpoint> getDebugProtocolBreakpoint(dap::string &sessionId)
265 {
266 dap::optional<dap::Breakpoint> bp;
267 auto data = getData(sessionId);
268 if (data) {
269 bp.value().id = data->id;
270 bp.value().verified = data->verified;
271 bp.value().message = data->message;
272 bp.value().source = data->source;
273 bp.value().line = data->line;
274 bp.value().column = data->column;
275 bp.value().endLine = data->endLine;
276 bp.value().endColumn = data->endColumn;
277 bp.value().instructionReference = data->instructionReference;
278 bp.value().offset = data->offset;
279 }
280 return bp;
281 }
282
283protected:
284 dap::optional<IBreakpointSessionData> data;
285
286 std::map<dap::string, IBreakpointSessionData> sessionData;
287 dap::string id;
288};
289
290struct Breakpoint : public BaseBreakpoint, public IInnerBreakpoint
291{
292 Breakpoint(
293 QUrl &uri,
294 dapNumber lineNumber,
295 dap::optional<dapNumber> column,
296 bool enabled,
297 dap::optional<dap::string> condition,
298 dap::optional<dap::string> hitCondition,
299 dap::optional<dap::string> logMessage,
300 dap::any adapterData,
301 const std::string &id = QUuid::createUuid().toString().toStdString())
302 : BaseBreakpoint(enabled, hitCondition, condition, logMessage, id), _uri(uri), _lineNumber(lineNumber), _column(column), _adapterData(adapterData)
303 {
304 }
305
306 bool isDirty(QUrl uri)
307 {
308 Q_UNUSED(uri)
309 // Not support dirty check now.
310 return false;
311 }
312
313 dapNumber lineNumber()
314 {
315 if (verified() && data && data.value().line) {
316 return data.value().line.value();
317 }
318 return _lineNumber;
319 }
320
321 bool verified()
322 {
323 if (data) {
324 return data.value().verified && !isDirty(_uri);
325 }
326 return true;
327 }
328
329 QUrl getUriFromSource(dap::Source source, dap::optional<dap::string> path, dap::string sessionId)
330 {
331 Q_UNUSED(path)
332 Q_UNUSED(sessionId)
333 return QUrl(source.path->c_str());
334 }
335
336 QUrl uri()
337 {
338 if (verified() && data && data.value().source) {
339 return getUriFromSource(data.value().source.value(), data.value().source.value().path, data->sessionId);
340 }
341 return _uri;
342 }
343
344 dap::optional<dap::integer> column()
345 {
346 if (verified() && data && data.value().column) {
347 return data.value().column;
348 }
349 return _column;
350 }
351
352 dap::optional<dap::string> message()
353 {
354 if (isDirty(uri())) {
355 return "Unverified breakpoint. File is modified, please restart debug session.";
356 }
357 return BaseBreakpoint::message();
358 }
359
360 dap::any adapterData()
361 {
362 if (data && data.value().source && data.value().source.value().adapterData) {
363 return data.value().source.value().adapterData.value();
364 }
365 return _adapterData;
366 }
367
368 dap::optional<dapNumber> endLineNumber()
369 {
370 if (verified() && data) {
371 return data.value().endLine;
372 }
373 return undefined;
374 }
375
376 dap::optional<dapNumber> endColumn()
377 {
378 if (verified() && data) {
379 return data.value().endColumn;
380 }
381 return undefined;
382 }
383
384 bool supported()
385 {
386 if (!data) {
387 return true;
388 }
389 if (logMessage && !data.value().supportsLogPoints) {
390 return false;
391 }
392 if (condition && !data.value().supportsConditionalBreakpoints) {
393 return false;
394 }
395 if (hitCondition && !data.value().supportsHitConditionalBreakpoints) {
396 return false;
397 }
398
399 return true;
400 }
401
402 void setSessionData(dap::string &sessionId, dap::optional<IBreakpointSessionData> data)
403 {
404 BaseBreakpoint::setSessionData(sessionId, data);
405 if (_adapterData.is<std::nullptr_t>()) {
406 _adapterData = adapterData();
407 }
408 }
409
410 dap::string toString()
411 {
412 return undefined;
413 }
414
415 void update(IBreakpointUpdateData &data)
416 {
417 if (!data.lineNumber) {
418 _lineNumber = data.lineNumber.value();
419 }
420 if (!data.column) {
421 _column = data.column;
422 }
423 if (!data.condition) {
424 condition = data.condition;
425 }
426 if (!data.hitCondition) {
427 hitCondition = data.hitCondition;
428 }
429 if (!data.logMessage) {
430 logMessage = data.logMessage;
431 }
432 }
433
434private:
435 QUrl _uri;
436 dapNumber _lineNumber = 0;
437 dap::optional<dapNumber> _column;
438 dap::any _adapterData;
439};
440
441struct IThread : public ITreeElement
442{
443public:
444 /**
445 * Process the thread belongs to
446 */
447 IDebugSession *session = nullptr;
448
449 /**
450 * Id of the thread generated by the debug adapter backend.
451 */
452 dapNumber threadId = 0;
453
454 /**
455 * Name of the thread.
456 */
457 dap::string name;
458
459 /**
460 * Information about the current thread stop event. Undefined if thread is not stopped.
461 */
462 dap::optional<IRawStoppedDetails> stoppedDetails;
463
464 /**
465 * Information about the exception if an 'exception' stopped event raised and DA supports the 'exceptionInfo' request, otherwise undefined.
466 */
467 dap::optional<IExceptionInfo> exceptionInfo;
468
469 dap::string stateLabel;
470
471 /**
472 * Gets the callstack if it has already been received from the debug
473 * adapter.
474 */
475 ReadonlyArray<IStackFrame *> getCallStack();
476
477 /**
478 * Gets the top stack frame that is not hidden if the callstack has already been received from the debug adapter
479 */
480 dap::optional<IStackFrame *> getTopStackFrame();
481
482 /**
483 * Invalidates the callstack cache
484 */
485 void clearCallStack();
486
487 /**
488 * Indicates whether this thread is stopped. The callstack for stopped
489 * threads can be retrieved from the debug adapter.
490 */
491 bool stopped = false;
492
493 dap::any next(dap::SteppingGranularity granularity);
494 dap::any stepIn(dap::optional<dap::SteppingGranularity> granularity);
495 dap::any stepOut(dap::SteppingGranularity granularity);
496 dap::any stepBack(dap::SteppingGranularity granularity);
497 dap::any continue_();
498 dap::any pause();
499 dap::any terminate();
500 dap::any reverseContinue();
501};
502
503struct IExpressionContainer : public ITreeElement
504{
505 bool hasChildren = false;
506 dap::array<IExpression *> getChildren();
507 dap::optional<dapNumber> reference;
508 dap::string value;
509 dap::string type;
510 dap::optional<bool> valueChanged;
511};
512
513struct IExpression : public IExpressionContainer
514{
515 dap::string name;
516};
517
518struct IRange
519{
520 /**
521 * Line number on which the range starts (starts at 1).
522 */
523 dapNumber startLineNumber = 0;
524 /**
525 * Column on which the range starts in line `startLineNumber` (starts at 1).
526 */
527 dapNumber startColumn = 0;
528 /**
529 * Line number on which the range ends.
530 */
531 dapNumber endLineNumber = 0;
532 /**
533 * Column on which the range ends in line `endLineNumber`.
534 */
535 dapNumber endColumn = 0;
536};
537
538struct IScope : public IExpressionContainer
539{
540 dap::string name;
541 bool expensive = false;
542 dap::optional<IRange> range;
543};
544
545struct IStackFrame : public ITreeElement
546{
547 virtual ~IStackFrame() {}
548 IThread *thread = nullptr;
549 dap::string name;
550 dap::optional<dap::string> presentationHint;
551 dapNumber frameId = 0;
552 IRange range;
553 Source *source = nullptr;
554 bool canRestart;
555 dap::optional<dap::string> instructionPointerReference;
556 dap::array<IScope> getScopes();
557 ReadonlyArray<IScope> getMostSpecificScopes(IRange range);
558 void forgetScopes();
559 dap::any restart();
560 dap::string toString();
561 // openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<IEditorPane | undefined>;
562 bool equals(IStackFrame other);
563};
564
565enum State {
566 kInactive,
567 kInitializing,
568 kStopped,
569 kRunning
570};
571
572struct IEnvConfig
573{
574 // 'neverOpen' | 'openOnSessionStart' | 'openOnFirstSessionStart'
575 dap::optional<dap::string> internalConsoleOptions;
576 dap::optional<dap::string> preRestartTask;
577 dap::optional<dap::string> postRestartTask;
578 dap::optional<dap::string> preLaunchTaskr;
579 dap::optional<dap::string> postDebugTask;
580 dap::optional<dapNumber> debugServer;
581 dap::optional<bool> noDebug;
582};
583
584struct IConfigPresentation
585{
586 dap::optional<bool> hidden;
587 dap::optional<dap::string> group;
588 dap::optional<dapNumber> order;
589};
590
591struct IConfig : public IEnvConfig
592{
593 // fundamental attributes
594 dap::string type;
595 dap::string request;
596 dap::string name;
597 dap::optional<IConfigPresentation *> presentation;
598
599 // internals
600 dap::optional<dap::string> __sessionId;
601 dap::optional<dap::any> __restart;
602 dap::optional<bool> __autoAttach;
603 dap::optional<dapNumber> port;
604};
605
606struct IDebugSession : public ITreeElement
607{
608 virtual ~IDebugSession() {}
609 virtual const dap::Capabilities &capabilities() const = 0;
610
611 virtual bool initialize(const char *ip, int port, dap::InitializeRequest &iniRequest) = 0;
612
613 virtual bool launch(dap::LaunchRequest &config) = 0;
614 virtual bool attach(dap::AttachRequest &config) = 0;
615
616 virtual void restart() = 0;
617 virtual void terminate(bool restart = false) = 0;
618 virtual void disconnect(bool terminateDebuggee = true, bool restart = false) = 0;
619
620 virtual void sendBreakpoints(const QString &sourcePath, dap::array<IBreakpoint> &breakpointsToSend) = 0;
621 virtual void sendFunctionBreakpoints(dap::array<IFunctionBreakpoint> &fbpts) = 0;
622 virtual void sendExceptionBreakpoints(dap::array<IExceptionBreakpoint> &exbpts) = 0;
623 virtual dap::optional<dap::DataBreakpointInfoResponse> dataBreakpointInfo(
624 dap::string &name, dap::optional<dapNumber> variablesReference) = 0;
625 virtual void sendDataBreakpoints(dap::array<IDataBreakpoint> dataBreakpoints) = 0;
626 virtual void sendInstructionBreakpoints(dap::array<IInstructionBreakpoint> instructionBreakpoints) = 0;
627 // dap::array<IPosition> breakpointsLocations(URI uri, number lineNumber);
628 virtual dap::optional<dap::Breakpoint> getDebugProtocolBreakpoint(dap::string &breakpointId) = 0;
629 // dap::optional<dap::Response> customRequest(dap::string &request, dap::any args);
630 virtual dap::optional<dap::StackTraceResponse> stackTrace(dapNumber threadId, dapNumber startFrame, dapNumber levels) = 0;
631 virtual dap::optional<IExceptionInfo> exceptionInfo(dapNumber threadId) = 0;
632 virtual dap::optional<dap::ScopesResponse> scopes(dapNumber frameId, dapNumber threadId) = 0;
633 virtual dap::optional<dap::VariablesResponse> variables(dapNumber variablesReference,
634 dap::optional<dapNumber> threadId,
635 dap::optional<dap::string> filter,
636 dap::optional<dapNumber> start,
637 dap::optional<dapNumber> count) = 0;
638 virtual dap::optional<dap::EvaluateResponse> evaluate(
639 dap::string &expression, dapNumber frameId, dap::optional<dap::string> context) = 0;
640 virtual void restartFrame(dapNumber frameId, dapNumber threadId) = 0;
641 virtual void setLastSteppingGranularity(dapNumber threadId, dap::optional<dap::SteppingGranularity> granularity) = 0;
642
643 virtual void next(dap::integer threadId, dap::optional<dap::SteppingGranularity> granularity) = 0;
644 virtual void stepIn(dap::integer threadId, dap::optional<dap::integer> targetId,
645 dap::optional<dap::SteppingGranularity> granularity) = 0;
646 virtual void stepOut(dap::integer threadId, dap::optional<dap::SteppingGranularity> granularity) = 0;
647 virtual void stepBack(dapNumber threadId, dap::optional<dap::SteppingGranularity> granularity) = 0;
648 virtual void continueDbg(dap::integer threadId) = 0;
649 virtual void reverseContinue(dapNumber threadId) = 0;
650 virtual void pause(dap::integer threadId) = 0;
651 virtual void terminateThreads(dap::array<dapNumber> &threadIds) = 0;
652 virtual dap::optional<dap::SetVariableResponse> setVariable(
653 dapNumber variablesReference, dap::string &name, dap::string &value) = 0;
654 virtual dap::optional<dap::SetExpressionResponse> setExpression(
655 dapNumber frameId, dap::string &expression, dap::string &value) = 0;
656 virtual dap::optional<dap::GotoTargetsResponse> gotoTargets(dap::Source &source, dapNumber line, dapNumber column) = 0;
657 virtual dap::optional<dap::GotoResponse> goto_(dapNumber threadId, dapNumber targetId) = 0;
658 // dap::optional<dap::SourceResponse> loadSource(QUrl &resource);
659 // dap::array<dap::Source> getLoadedSources();
660 // dap::optional<dap::CompletionsResponse> completions(
661 // dap::optional<number> frameId,
662 // dap::optional<number> threadId,
663 // dap::string &text,
664 // dap::Position &position,
665 // number overwriteBefore);
666 virtual dap::optional<dap::StepInTargetsResponse> stepInTargets(dapNumber frameId) = 0;
667 virtual dap::optional<dap::CancelResponse> cancel(dap::string &progressId) = 0;
668 // dap::optional<dap::array<dap::DisassembledInstruction>> disassemble(dap::string &memoryReference, number offset, number instructionOffset, number instructionCount);
669 // dap::optional<dap::ReadMemoryResponse> readMemory(dap::string &memoryReference, number offset, number count);
670 // dap::optional<dap::WriteMemoryResponse> writeMemory(dap::string &memoryReference, number offset, dap::string &data, dap::optional<bool> allowPartial);
671 // threads.
672 virtual dap::optional<Thread *> getThread(dapNumber threadId) = 0;
673 virtual dap::optional<dap::array<IThread *>> getAllThreads() const = 0;
674 virtual void rawUpdate(IRawModelUpdate *data) = 0;
675 virtual void clearThreads(bool removeThreads, dap::optional<dapNumber> reference) = 0;
676 virtual dap::array<IRawStoppedDetails *> &getStoppedDetails() = 0;
677 virtual void fetchThreads(dap::optional<IRawStoppedDetails> stoppedDetails) = 0;
678 virtual dap::optional<dap::Source> getSourceForUri(QUrl &uri) = 0;
679 virtual Source *getSource(dap::optional<dap::Source> raw) = 0;
680 virtual dap::string getLabel() const = 0;
681
682 virtual dap::integer getThreadId() = 0;
683 virtual void setName(dap::string &name) = 0;
684
685 virtual bool getLocals(dap::integer frameId, IVariables *out) = 0;
686
687 State state = kInactive;
688 IConfig *configuration = nullptr;
689};
690
691struct ExpressionContainer : public IExpressionContainer
692{
693};
694
695struct Range : public IRange
696{
697 Range(dapNumber _startLineNumber, dapNumber _startColumn, dapNumber _endLineNumber, dapNumber _endColumn)
698 {
699 startLineNumber = _startLineNumber;
700 startColumn = _startColumn;
701 endLineNumber = _endLineNumber;
702 endColumn = _endColumn;
703 }
704};
705
706struct Source
707{
708 Source(dap::optional<dap::Source> raw_, dap::string &sessionId)
709 {
710 dap::string path;
711 if (raw_) {
712 raw = raw_.value();
713 if (raw.path)
714 path = raw.path.value();
715 else if (raw.name) {
716 path = raw.name.value();
717 } else {
718 path = "";
719 }
720 available = true;
721 } else {
722 raw.name = "Unknown Source";
723 available = false;
724 path = "debug:Unknown Source";
725 }
726 uri = getUriFromSource(raw, path, sessionId);
727 }
728
729 dap::optional<dap::string> name() const
730 {
731 return raw.name;
732 }
733
734 dap::optional<dap::string> origin() const
735 {
736 return raw.origin;
737 }
738
739 dap::optional<dap::string> path() const
740 {
741 return raw.path;
742 }
743
744 dap::optional<dap::string> presentationHint() const
745 {
746 return raw.presentationHint;
747 }
748
749 dap::optional<dap::integer> reference() const
750 {
751 return raw.sourceReference;
752 }
753
754 bool inMemory() const
755 {
756 return uri.scheme() == "debug";
757 }
758
759 QUrl getUriFromSource(dap::Source &raw, dap::optional<dap::string> path, dap::string &sessionId)
760 {
761 if (raw.sourceReference && raw.sourceReference.value() > 0) {
762 QUrl url;
763 url.setPath(path->c_str());
764 url.setScheme("debug");
765 QString query = QString("session=%s&ref=%d").arg(sessionId.c_str()).arg(raw.sourceReference.value());
766 url.setQuery(query);
767 return url;
768 }
769 return undefined;
770 }
771
772 QUrl uri;
773 bool available = false;
774 dap::Source raw;
775};
776
777// Thread
778struct Thread : public IThread
779{
780 dap::array<dap::StackFrame> callStack;
781 dap::array<dap::StackFrame> staleCallStack;
782 // dap::optional<IRawStoppedDetails> stoppedDetails;
783 // bool stopped = false;
784 bool reachedEndOfCallStack = false;
785 dap::optional<dap::SteppingGranularity> lastSteppingGranularity;
786 // dap::string name;
787
788 Thread(IDebugSession *_session, dap::string _name, dapNumber _threadId)
789 {
790 name = _name;
791 threadId = _threadId;
792 session = _session;
793 stopped = false;
794 }
795
796 virtual ~Thread()
797 {
798 }
799
800 dap::string getId() override
801 {
802 return session->getId();
803 }
804
805 void clearCallStack()
806 {
807 if (callStack.size()) {
808 staleCallStack = callStack;
809 }
810 callStack.clear();
811 }
812
813 dap::array<dap::StackFrame> getCallStack()
814 {
815 return callStack;
816 }
817
818 ReadonlyArray<dap::StackFrame> getStaleCallStack()
819 {
820 return staleCallStack;
821 }
822
823 dap::optional<IStackFrame> getTopStackFrame()
824 {
825 // TODO(mozart)
826 return undefined;
827 }
828
829 dap::string stateLabel()
830 {
831 if (stoppedDetails) {
832 return stoppedDetails.value().description.value();
833 }
834
835 return undefined;
836 }
837
838 /**
839 * Queries the debug adapter for the callstack and returns a promise
840 * which completes once the call stack has been retrieved.
841 * If the thread is not stopped, it returns a promise to an empty array.
842 * Only fetches the first stack frame for performance reasons. Calling this method consecutive times
843 * gets the remainder of the call stack.
844 */
845 void fetchCallStack(dapNumber levels = 20)
846 {
847// if (stopped) {
848 callStack.clear();
849 auto start = callStack.size();
850 auto callStack = getCallStackImpl(static_cast<int>(start), levels);
851 reachedEndOfCallStack = static_cast<int>(callStack.size()) < levels;
852 if (start < this->callStack.size()) {
853 size_t endIndex = callStack.size() - start;
854 this->callStack.erase(callStack.begin() + static_cast<int>(start), callStack.begin() + static_cast<int>(endIndex));
855 }
856 this->callStack.insert(this->callStack.end(), callStack.begin(), callStack.end());
857// if (stoppedDetails.value().totalFrames && stoppedDetails.value().totalFrames.value() == static_cast<int>(callStack.size())) {
858// reachedEndOfCallStack = true;
859// }
860// }
861 }
862
863private:
864 dap::array<dap::StackFrame> getCallStackImpl(dapNumber startFrame, dapNumber levels)
865 {
866 auto response = session->stackTrace(threadId, startFrame, levels);
867 if (!response) {
868 return undefined;
869 }
870
871 if (stoppedDetails && response && response->totalFrames) {
872 stoppedDetails.value().totalFrames = response.value().totalFrames.value();
873 }
874
875 auto stackFrames = response.value().stackFrames;
876 return stackFrames;
877 }
878
879 /**
880 * Returns exception info promise if the exception was thrown, otherwise undefined
881 */
882#if 0 // TODO(mozart):not used.
883 dap::optional<IExceptionInfo> exceptionInfo()
884 {
885 // TODO(mozart)
886 return undefined;
887 }
888#endif
889
890 void next(dap::SteppingGranularity granularity)
891 {
892 session->next(threadId, granularity);
893 }
894
895 void stepIn(dap::SteppingGranularity granularity)
896 {
897 session->stepIn(threadId, undefined, granularity);
898 }
899
900 void stepOut(dap::SteppingGranularity granularity)
901 {
902 session->stepOut(threadId, granularity);
903 }
904
905 void stepBack(dap::SteppingGranularity granularity)
906 {
907 session->stepBack(threadId, granularity);
908 }
909#if 0 // TODO(mozart):not used.
910 void continue_()
911 {
912 session->continueDbg(threadId);
913 }
914#endif
915
916 void pause()
917 {
918 session->pause(threadId);
919 }
920#if 0 // TODO(mozart):not used.
921 void terminate()
922 {
923 dap::array<number> threadIds;
924 threadIds.push_back(threadId);
925 session->terminateThreads(threadIds);
926 }
927#endif
928
929 void reverseContinue()
930 {
931 session->reverseContinue(threadId);
932 }
933};
934
935} // end namespace.
936
937#endif // DEBUG_H
938