1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "jsdebugger.h"
6
7#include "services/window/windowservice.h"
8#include "services/project/projectservice.h"
9#include "base/abstractcentral.h"
10#include "base/abstractwidget.h"
11
12#include <QFile>
13#include <QTextStream>
14#include <QDebug>
15#include <QAction>
16
17#define SAFE_DELETE(p) \
18if (p) { \
19 delete p; \
20 p = nullptr; \
21}
22
23using namespace dpfservice;
24JSDebugger::JSDebugger(QObject *parent)
25 : AbstractDebugger(parent)
26{
27}
28
29JSDebugger::~JSDebugger()
30{
31
32}
33
34
35QWidget *JSDebugger::getOutputPane() const
36{
37 return debuggerWidget(QScriptEngineDebugger::DebugOutputWidget);
38}
39
40QWidget *JSDebugger::getStackPane() const
41{
42 return debuggerWidget(QScriptEngineDebugger::StackWidget);
43}
44
45QWidget *JSDebugger::getLocalsPane() const
46{
47 return debuggerWidget(QScriptEngineDebugger::LocalsWidget);
48}
49
50QWidget *JSDebugger::getBreakpointPane() const
51{
52 return debuggerWidget(QScriptEngineDebugger::BreakpointsWidget);
53}
54
55void JSDebugger::startDebug()
56{
57 // initialize debug interface.
58 QMetaObject::invokeMethod(this, "setupDebugEnv");
59}
60
61void JSDebugger::detachDebug()
62{
63}
64
65void JSDebugger::interruptDebug()
66{
67 runCommand(QScriptEngineDebugger::InterruptAction);
68 runState = kStopped;
69}
70
71void JSDebugger::continueDebug()
72{
73 runCommand(QScriptEngineDebugger::ContinueAction);
74 runState = kRunning;
75 emit runStateChanged(kRunning);
76}
77
78void JSDebugger::abortDebug()
79{
80}
81
82void JSDebugger::restartDebug()
83{
84 abortDebug();
85 startDebug();
86}
87
88void JSDebugger::stepOver()
89{
90 runCommand(QScriptEngineDebugger::StepOverAction);
91}
92
93void JSDebugger::stepIn()
94{
95 runCommand(QScriptEngineDebugger::StepIntoAction);
96}
97
98void JSDebugger::stepOut()
99{
100 runCommand(QScriptEngineDebugger::StepOverAction);
101}
102
103AbstractDebugger::RunState JSDebugger::getRunState() const
104{
105 return runState;
106}
107
108bool JSDebugger::runCoredump(const QString &target, const QString &core, const QString &kit)
109{
110 Q_UNUSED(target)
111 Q_UNUSED(core)
112 Q_UNUSED(kit)
113
114 // no need this function.
115 return false;
116}
117
118void JSDebugger::slotEvaluationSuspended()
119{
120 runState = kStopped;
121 emit runStateChanged(kStopped);
122}
123
124void JSDebugger::slotEvaluationResumed()
125{
126}
127
128void JSDebugger::runCommand(QScriptEngineDebugger::DebuggerAction command)
129{
130 emit execCommand(command);
131}
132
133QWidget *JSDebugger::debuggerWidget(QScriptEngineDebugger::DebuggerWidget widget) const
134{
135// return debugger.widget(widget);
136 return {};
137}
138
139QScriptValue JSDebugger::evaluateFile(QScriptEngine &engine, const QString &filePath)
140{
141 QFile file(filePath);
142 file.open(QIODevice::ReadOnly);
143 QString byteArray = file.readAll();
144 file.close();
145
146 // check syntax.
147 QScriptSyntaxCheckResult syntaxCheck = engine.checkSyntax(byteArray);
148 if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {
149 qInfo() << syntaxCheck.errorMessage() + " at line " + QString::number(syntaxCheck.errorLineNumber());
150 return {};
151 }
152 return engine.evaluate(byteArray, filePath);
153}
154
155void JSDebugger::addPagesToContext(const QScriptEngineDebugger &debugger)
156{
157 codeEditor = new AbstractCentral(debugger.widget(QScriptEngineDebugger::CodeWidget));
158 stackPane = new AbstractWidget(debugger.widget(QScriptEngineDebugger::StackWidget));
159 breakpointsPane = new AbstractWidget(debugger.widget(QScriptEngineDebugger::BreakpointsWidget));
160 scriptPane = new AbstractWidget(debugger.widget(QScriptEngineDebugger::ScriptsWidget));
161 errorPane = new AbstractWidget(debugger.widget(QScriptEngineDebugger::ErrorLogWidget));
162 localsPane = new AbstractWidget(debugger.widget(QScriptEngineDebugger::LocalsWidget));
163 auto windowService = dpfGetService(WindowService);
164 oldWidgetEdit = windowService->setWidgetEdit(codeEditor);
165 // instert output pane to window.
166 windowService->addContextWidget(tr("Stac&kFrame"), stackPane, "Application");
167 oldWidgetWatch = windowService->setWidgetWatch(localsPane);
168 debugger.widget(QScriptEngineDebugger::LocalsWidget)->show();
169 windowService->addContextWidget(tr("Break&points"), breakpointsPane, "Application");
170 windowService->addContextWidget(tr("ScriptWidget"), scriptPane, "Application");
171 windowService->addContextWidget(tr("ErrorLogWidget"), errorPane, "Application");
172}
173
174void JSDebugger::removePagesFromContext()
175{
176 auto windowService = dpfGetService(WindowService);
177 auto removePage = [windowService](AbstractWidget *page){
178 Q_ASSERT(page != nullptr);
179 windowService->removeContextWidget(page);
180 };
181
182 removePage(stackPane);
183 removePage(breakpointsPane);
184 removePage(scriptPane);
185 removePage(errorPane);
186
187 windowService->setWidgetEdit(new AbstractCentral(oldWidgetEdit));
188 windowService->setWidgetWatch(new AbstractWidget(oldWidgetWatch));
189}
190
191void JSDebugger::setupDebugEnv()
192{
193 // intialize debugger.
194 QScriptEngineDebugger debugger;
195 connect(this, &JSDebugger::execCommand, [&](QScriptEngineDebugger::DebuggerAction debuggerAction){
196 debugger.action(debuggerAction)->trigger();
197 });
198 debugger.setAutoShowStandardWindow(false);
199 connect(&debugger, &QScriptEngineDebugger::evaluationResumed, this, &JSDebugger::slotEvaluationResumed);
200 connect(&debugger, &QScriptEngineDebugger::evaluationSuspended, this, &JSDebugger::slotEvaluationSuspended);
201
202 QScriptEngine engine;
203 debugger.attachTo(&engine);
204
205 addPagesToContext(debugger);
206
207 // evaluate js files.
208 auto prjService = dpfGetService(ProjectService);
209 auto sourceFiles = prjService->getActiveProjectInfo().sourceFiles();
210
211 interruptDebug();
212 for (auto sourceFile : sourceFiles) {
213 auto value = evaluateFile(engine, sourceFile);
214 qInfo() << value.toString();
215 }
216 removePagesFromContext();
217
218 disconnect(this, &JSDebugger::execCommand, nullptr, nullptr);
219
220 runState = kNoRun;
221 emit runStateChanged(kNoRun);
222}
223