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) \ |
18 | if (p) { \ |
19 | delete p; \ |
20 | p = nullptr; \ |
21 | } |
22 | |
23 | using namespace dpfservice; |
24 | JSDebugger::JSDebugger(QObject *parent) |
25 | : AbstractDebugger(parent) |
26 | { |
27 | } |
28 | |
29 | JSDebugger::~JSDebugger() |
30 | { |
31 | |
32 | } |
33 | |
34 | |
35 | QWidget *JSDebugger::getOutputPane() const |
36 | { |
37 | return debuggerWidget(QScriptEngineDebugger::DebugOutputWidget); |
38 | } |
39 | |
40 | QWidget *JSDebugger::getStackPane() const |
41 | { |
42 | return debuggerWidget(QScriptEngineDebugger::StackWidget); |
43 | } |
44 | |
45 | QWidget *JSDebugger::getLocalsPane() const |
46 | { |
47 | return debuggerWidget(QScriptEngineDebugger::LocalsWidget); |
48 | } |
49 | |
50 | QWidget *JSDebugger::getBreakpointPane() const |
51 | { |
52 | return debuggerWidget(QScriptEngineDebugger::BreakpointsWidget); |
53 | } |
54 | |
55 | void JSDebugger::startDebug() |
56 | { |
57 | // initialize debug interface. |
58 | QMetaObject::invokeMethod(this, "setupDebugEnv" ); |
59 | } |
60 | |
61 | void JSDebugger::detachDebug() |
62 | { |
63 | } |
64 | |
65 | void JSDebugger::interruptDebug() |
66 | { |
67 | runCommand(QScriptEngineDebugger::InterruptAction); |
68 | runState = kStopped; |
69 | } |
70 | |
71 | void JSDebugger::continueDebug() |
72 | { |
73 | runCommand(QScriptEngineDebugger::ContinueAction); |
74 | runState = kRunning; |
75 | emit runStateChanged(kRunning); |
76 | } |
77 | |
78 | void JSDebugger::abortDebug() |
79 | { |
80 | } |
81 | |
82 | void JSDebugger::restartDebug() |
83 | { |
84 | abortDebug(); |
85 | startDebug(); |
86 | } |
87 | |
88 | void JSDebugger::stepOver() |
89 | { |
90 | runCommand(QScriptEngineDebugger::StepOverAction); |
91 | } |
92 | |
93 | void JSDebugger::stepIn() |
94 | { |
95 | runCommand(QScriptEngineDebugger::StepIntoAction); |
96 | } |
97 | |
98 | void JSDebugger::stepOut() |
99 | { |
100 | runCommand(QScriptEngineDebugger::StepOverAction); |
101 | } |
102 | |
103 | AbstractDebugger::RunState JSDebugger::getRunState() const |
104 | { |
105 | return runState; |
106 | } |
107 | |
108 | bool 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 | |
118 | void JSDebugger::slotEvaluationSuspended() |
119 | { |
120 | runState = kStopped; |
121 | emit runStateChanged(kStopped); |
122 | } |
123 | |
124 | void JSDebugger::slotEvaluationResumed() |
125 | { |
126 | } |
127 | |
128 | void JSDebugger::runCommand(QScriptEngineDebugger::DebuggerAction command) |
129 | { |
130 | emit execCommand(command); |
131 | } |
132 | |
133 | QWidget *JSDebugger::debuggerWidget(QScriptEngineDebugger::DebuggerWidget widget) const |
134 | { |
135 | // return debugger.widget(widget); |
136 | return {}; |
137 | } |
138 | |
139 | QScriptValue 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 | |
155 | void 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 | |
174 | void 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 | |
191 | void 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 | |