1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2018 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtSCriptTools module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qscriptdebuggerscriptedconsolecommand_p.h" |
41 | #include "qscriptdebuggerconsolecommand_p_p.h" |
42 | #include "qscriptdebuggerconsolecommandjob_p.h" |
43 | #include "qscriptdebuggerconsolecommandjob_p_p.h" |
44 | #include "qscriptmessagehandlerinterface_p.h" |
45 | #include "qscriptdebuggerconsoleglobalobject_p.h" |
46 | #include "qscriptdebuggerresponse_p.h" |
47 | #include "qscriptdebuggercommandschedulerinterface_p.h" |
48 | |
49 | #include <QtCore/qstring.h> |
50 | #include <QtCore/qstringlist.h> |
51 | #include <QtScript/qscriptengine.h> |
52 | #include <QtScript/qscriptvalue.h> |
53 | #include <QtScript/qscriptvalueiterator.h> |
54 | #include <QtScript/qscriptcontextinfo.h> |
55 | #include <QtCore/qdebug.h> |
56 | |
57 | Q_DECLARE_METATYPE(QScriptDebuggerResponse) |
58 | |
59 | QT_BEGIN_NAMESPACE |
60 | |
61 | /*! |
62 | \since 4.5 |
63 | \class QScriptDebuggerScriptedConsoleCommand |
64 | \internal |
65 | |
66 | \brief The QScriptDebuggerScriptedConsoleCommand class encapsulates a command defined in a script. |
67 | */ |
68 | |
69 | class QScriptDebuggerScriptedConsoleCommandPrivate |
70 | : public QScriptDebuggerConsoleCommandPrivate |
71 | { |
72 | Q_DECLARE_PUBLIC(QScriptDebuggerScriptedConsoleCommand) |
73 | public: |
74 | QScriptDebuggerScriptedConsoleCommandPrivate(); |
75 | ~QScriptDebuggerScriptedConsoleCommandPrivate(); |
76 | |
77 | QString name; |
78 | QString group; |
79 | QString shortDescription; |
80 | QString longDescription; |
81 | QStringList aliases; |
82 | QStringList seeAlso; |
83 | QStringList argumentTypes; |
84 | QStringList subCommands; |
85 | QScriptValue globalObject; |
86 | QScriptValue execFunction; |
87 | QScriptValue responseFunction; |
88 | }; |
89 | |
90 | QScriptDebuggerScriptedConsoleCommandPrivate::QScriptDebuggerScriptedConsoleCommandPrivate() |
91 | { |
92 | } |
93 | |
94 | QScriptDebuggerScriptedConsoleCommandPrivate::~QScriptDebuggerScriptedConsoleCommandPrivate() |
95 | { |
96 | } |
97 | |
98 | QScriptDebuggerScriptedConsoleCommand::QScriptDebuggerScriptedConsoleCommand( |
99 | const QString &name, const QString &group, |
100 | const QString &shortDescription, const QString &longDescription, |
101 | const QStringList &aliases, const QStringList &seeAlso, |
102 | const QStringList &argumentTypes, const QStringList &subCommands, |
103 | const QScriptValue &globalObject, |
104 | const QScriptValue &execFunction, const QScriptValue &responseFunction) |
105 | : QScriptDebuggerConsoleCommand(*new QScriptDebuggerScriptedConsoleCommandPrivate) |
106 | { |
107 | Q_D(QScriptDebuggerScriptedConsoleCommand); |
108 | d->name = name; |
109 | d->group = group; |
110 | d->shortDescription = shortDescription; |
111 | d->longDescription = longDescription; |
112 | d->aliases = aliases; |
113 | d->seeAlso = seeAlso; |
114 | d->argumentTypes = argumentTypes; |
115 | d->subCommands = subCommands; |
116 | d->globalObject = globalObject; |
117 | d->execFunction = execFunction; |
118 | d->responseFunction = responseFunction; |
119 | } |
120 | |
121 | QScriptDebuggerScriptedConsoleCommand::~QScriptDebuggerScriptedConsoleCommand() |
122 | { |
123 | } |
124 | |
125 | class QScriptDebuggerScriptedConsoleCommandJobPrivate; |
126 | class QScriptDebuggerScriptedConsoleCommandJob |
127 | : public QScriptDebuggerConsoleCommandJob, |
128 | public QScriptDebuggerCommandSchedulerInterface |
129 | { |
130 | public: |
131 | QScriptDebuggerScriptedConsoleCommandJob( |
132 | QScriptDebuggerScriptedConsoleCommandPrivate *command, |
133 | const QStringList &arguments, |
134 | QScriptDebuggerConsole *console, |
135 | QScriptMessageHandlerInterface *messageHandler, |
136 | QScriptDebuggerCommandSchedulerInterface *commandScheduler); |
137 | ~QScriptDebuggerScriptedConsoleCommandJob(); |
138 | |
139 | int scheduleCommand( |
140 | const QScriptDebuggerCommand &command, |
141 | QScriptDebuggerResponseHandlerInterface *responseHandler); |
142 | |
143 | void start(); |
144 | void handleResponse(const QScriptDebuggerResponse &response, |
145 | int commandId); |
146 | |
147 | private: |
148 | Q_DECLARE_PRIVATE(QScriptDebuggerScriptedConsoleCommandJob) |
149 | Q_DISABLE_COPY(QScriptDebuggerScriptedConsoleCommandJob) |
150 | }; |
151 | |
152 | class QScriptDebuggerScriptedConsoleCommandJobPrivate |
153 | : public QScriptDebuggerConsoleCommandJobPrivate |
154 | { |
155 | public: |
156 | QScriptDebuggerScriptedConsoleCommandJobPrivate() : command(0), commandCount(0) {} |
157 | ~QScriptDebuggerScriptedConsoleCommandJobPrivate() {} |
158 | |
159 | QScriptDebuggerScriptedConsoleCommandPrivate *command; |
160 | QStringList arguments; |
161 | int commandCount; |
162 | }; |
163 | |
164 | QScriptDebuggerScriptedConsoleCommandJob::QScriptDebuggerScriptedConsoleCommandJob( |
165 | QScriptDebuggerScriptedConsoleCommandPrivate *command, |
166 | const QStringList &arguments, |
167 | QScriptDebuggerConsole *console, |
168 | QScriptMessageHandlerInterface *messageHandler, |
169 | QScriptDebuggerCommandSchedulerInterface *commandScheduler) |
170 | : QScriptDebuggerConsoleCommandJob(*new QScriptDebuggerScriptedConsoleCommandJobPrivate, |
171 | console, messageHandler, commandScheduler) |
172 | { |
173 | Q_D(QScriptDebuggerScriptedConsoleCommandJob); |
174 | d->command = command; |
175 | d->arguments = arguments; |
176 | } |
177 | |
178 | QScriptDebuggerScriptedConsoleCommandJob::~QScriptDebuggerScriptedConsoleCommandJob() |
179 | { |
180 | } |
181 | |
182 | int QScriptDebuggerScriptedConsoleCommandJob::scheduleCommand( |
183 | const QScriptDebuggerCommand &command, |
184 | QScriptDebuggerResponseHandlerInterface *responseHandler) |
185 | { |
186 | Q_D(QScriptDebuggerScriptedConsoleCommandJob); |
187 | ++d->commandCount; |
188 | return commandScheduler()->scheduleCommand(command, responseHandler); |
189 | } |
190 | |
191 | void QScriptDebuggerScriptedConsoleCommandJob::start() |
192 | { |
193 | Q_D(QScriptDebuggerScriptedConsoleCommandJob); |
194 | QScriptEngine *engine = d->command->globalObject.engine(); |
195 | engine->setGlobalObject(d->command->globalObject); |
196 | QScriptValueList args; |
197 | for (int i = 0; i < d->arguments.size(); ++i) |
198 | args.append(t: QScriptValue(engine, d->arguments.at(i))); |
199 | QScriptDebuggerConsoleGlobalObject *global; |
200 | global = qobject_cast<QScriptDebuggerConsoleGlobalObject*>(object: engine->globalObject().toQObject()); |
201 | Q_ASSERT(global != 0); |
202 | global->setScheduler(this); |
203 | global->setResponseHandler(this); |
204 | global->setMessageHandler(d->messageHandler); |
205 | global->setConsole(d->console); |
206 | d->commandCount = 0; |
207 | QScriptValue ret = d->command->execFunction.call(thisObject: QScriptValue(), args); |
208 | global->setScheduler(0); |
209 | global->setResponseHandler(0); |
210 | global->setMessageHandler(0); |
211 | global->setConsole(0); |
212 | if (ret.isError()) { |
213 | qWarning(msg: "*** internal error: %s" , qPrintable(ret.toString())); |
214 | } |
215 | if (d->commandCount == 0) |
216 | finish(); |
217 | } |
218 | |
219 | void QScriptDebuggerScriptedConsoleCommandJob::handleResponse( |
220 | const QScriptDebuggerResponse &response, |
221 | int commandId) |
222 | { |
223 | Q_D(QScriptDebuggerScriptedConsoleCommandJob); |
224 | // ### generalize |
225 | QScriptEngine *engine = d->command->globalObject.engine(); |
226 | engine->setGlobalObject(d->command->globalObject); |
227 | QScriptValueList args; |
228 | args.append(t: engine->toScriptValue(value: response)); |
229 | args.append(t: QScriptValue(engine, commandId)); |
230 | QScriptDebuggerConsoleGlobalObject *global; |
231 | global = qobject_cast<QScriptDebuggerConsoleGlobalObject*>(object: d->command->globalObject.toQObject()); |
232 | Q_ASSERT(global != 0); |
233 | global->setScheduler(this); |
234 | global->setResponseHandler(this); |
235 | global->setMessageHandler(d->messageHandler); |
236 | global->setConsole(d->console); |
237 | d->commandCount = 0; |
238 | QScriptValue ret = d->command->responseFunction.call(thisObject: QScriptValue(), args); |
239 | global->setScheduler(0); |
240 | global->setResponseHandler(0); |
241 | global->setMessageHandler(0); |
242 | global->setConsole(0); |
243 | if (ret.isError()) { |
244 | qWarning(msg: "*** internal error: %s" , qPrintable(ret.toString())); |
245 | } |
246 | if (d->commandCount == 0) |
247 | finish(); |
248 | } |
249 | |
250 | /*! |
251 | \internal |
252 | */ |
253 | QString QScriptDebuggerScriptedConsoleCommand::name() const |
254 | { |
255 | Q_D(const QScriptDebuggerScriptedConsoleCommand); |
256 | return d->name; |
257 | } |
258 | |
259 | /*! |
260 | \internal |
261 | */ |
262 | QString QScriptDebuggerScriptedConsoleCommand::group() const |
263 | { |
264 | Q_D(const QScriptDebuggerScriptedConsoleCommand); |
265 | return d->group; |
266 | } |
267 | |
268 | /*! |
269 | \internal |
270 | */ |
271 | QString QScriptDebuggerScriptedConsoleCommand::shortDescription() const |
272 | { |
273 | Q_D(const QScriptDebuggerScriptedConsoleCommand); |
274 | return d->shortDescription; |
275 | } |
276 | |
277 | /*! |
278 | \internal |
279 | */ |
280 | QString QScriptDebuggerScriptedConsoleCommand::longDescription() const |
281 | { |
282 | Q_D(const QScriptDebuggerScriptedConsoleCommand); |
283 | return d->longDescription; |
284 | } |
285 | |
286 | /*! |
287 | \internal |
288 | */ |
289 | QStringList QScriptDebuggerScriptedConsoleCommand::aliases() const |
290 | { |
291 | Q_D(const QScriptDebuggerScriptedConsoleCommand); |
292 | return d->aliases; |
293 | } |
294 | |
295 | /*! |
296 | \internal |
297 | */ |
298 | QStringList QScriptDebuggerScriptedConsoleCommand::seeAlso() const |
299 | { |
300 | Q_D(const QScriptDebuggerScriptedConsoleCommand); |
301 | return d->seeAlso; |
302 | } |
303 | |
304 | /*! |
305 | \internal |
306 | */ |
307 | QStringList QScriptDebuggerScriptedConsoleCommand::argumentTypes() const |
308 | { |
309 | Q_D(const QScriptDebuggerScriptedConsoleCommand); |
310 | return d->argumentTypes; |
311 | } |
312 | |
313 | /*! |
314 | \internal |
315 | */ |
316 | QStringList QScriptDebuggerScriptedConsoleCommand::subCommands() const |
317 | { |
318 | Q_D(const QScriptDebuggerScriptedConsoleCommand); |
319 | return d->subCommands; |
320 | } |
321 | |
322 | /*! |
323 | \internal |
324 | */ |
325 | QScriptDebuggerConsoleCommandJob *QScriptDebuggerScriptedConsoleCommand::createJob( |
326 | const QStringList &arguments, |
327 | QScriptDebuggerConsole *console, |
328 | QScriptMessageHandlerInterface *messageHandler, |
329 | QScriptDebuggerCommandSchedulerInterface *commandScheduler) |
330 | { |
331 | Q_D(QScriptDebuggerScriptedConsoleCommand); |
332 | return new QScriptDebuggerScriptedConsoleCommandJob( |
333 | d, arguments, console, messageHandler, commandScheduler); |
334 | } |
335 | |
336 | /*! |
337 | Parses a command defined by the given \a program. |
338 | Returns an object that encapsulates the command, or 0 if parsing failed. |
339 | */ |
340 | QScriptDebuggerScriptedConsoleCommand *QScriptDebuggerScriptedConsoleCommand::parse( |
341 | const QString &program, const QString &fileName, |
342 | QScriptEngine *engine, QScriptMessageHandlerInterface *messageHandler) |
343 | { |
344 | // create a custom global object |
345 | QScriptDebuggerConsoleGlobalObject *cppGlobal = new QScriptDebuggerConsoleGlobalObject(); |
346 | QScriptValue global = engine->newQObject(object: cppGlobal, |
347 | ownership: QScriptEngine::ScriptOwnership, |
348 | options: QScriptEngine::ExcludeSuperClassContents); |
349 | { |
350 | QScriptValueIterator it(engine->globalObject()); |
351 | while (it.hasNext()) { |
352 | it.next(); |
353 | global.setProperty(name: it.scriptName(), value: it.value(), flags: it.flags()); |
354 | } |
355 | } |
356 | engine->setGlobalObject(global); |
357 | |
358 | cppGlobal->setMessageHandler(messageHandler); |
359 | QScriptValue ret = engine->evaluate(program, fileName); |
360 | cppGlobal->setMessageHandler(0); |
361 | if (engine->hasUncaughtException()) { |
362 | messageHandler->message(type: QtCriticalMsg, text: ret.toString(), fileName, |
363 | lineNumber: engine->uncaughtExceptionLineNumber()); |
364 | return 0; |
365 | } |
366 | |
367 | QScriptValue name = global.property(name: QLatin1String("name" )); |
368 | if (!name.isString()) { |
369 | messageHandler->message(type: QtCriticalMsg, text: QLatin1String("command definition lacks a name" ), fileName); |
370 | return 0; |
371 | } |
372 | QString nameStr = name.toString(); |
373 | |
374 | QScriptValue group = global.property(name: QLatin1String("group" )); |
375 | if (!group.isString()) { |
376 | messageHandler->message(type: QtCriticalMsg, text: QString::fromLatin1(str: "definition of command \"%0\" lacks a group name" ) |
377 | .arg(a: nameStr), fileName); |
378 | return 0; |
379 | } |
380 | QString groupStr = group.toString(); |
381 | |
382 | QScriptValue shortDesc = global.property(name: QLatin1String("shortDescription" )); |
383 | if (!shortDesc.isString()) { |
384 | messageHandler->message(type: QtCriticalMsg, text: QString::fromLatin1(str: "definition of command \"%0\" lacks shortDescription" ) |
385 | .arg(a: nameStr), fileName); |
386 | return 0; |
387 | } |
388 | QString shortDescStr = shortDesc.toString(); |
389 | |
390 | QScriptValue longDesc = global.property(name: QLatin1String("longDescription" )); |
391 | if (!longDesc.isString()) { |
392 | messageHandler->message(type: QtCriticalMsg, text: QString::fromLatin1(str: "definition of command \"%0\" lacks longDescription" ) |
393 | .arg(a: nameStr), fileName); |
394 | return 0; |
395 | } |
396 | QString longDescStr = longDesc.toString(); |
397 | |
398 | QStringList aliases; |
399 | qScriptValueToSequence(value: global.property(name: QLatin1String("aliases" )), cont&: aliases); |
400 | |
401 | QStringList seeAlso; |
402 | qScriptValueToSequence(value: global.property(name: QLatin1String("seeAlso" )), cont&: seeAlso); |
403 | |
404 | QStringList argTypes; |
405 | qScriptValueToSequence(value: global.property(name: QLatin1String("argumentTypes" )), cont&: argTypes); |
406 | |
407 | QStringList subCommands; |
408 | qScriptValueToSequence(value: global.property(name: QLatin1String("subCommands" )), cont&: subCommands); |
409 | |
410 | QScriptValue execFunction = global.property(name: QLatin1String("execute" )); |
411 | if (!execFunction.isFunction()) { |
412 | messageHandler->message(type: QtCriticalMsg, text: QString::fromLatin1(str: "definition of command \"%0\" lacks execute() function" ) |
413 | .arg(a: nameStr), fileName); |
414 | return 0; |
415 | } |
416 | |
417 | QScriptValue responseFunction = global.property(name: QLatin1String("handleResponse" )); |
418 | |
419 | QScriptDebuggerScriptedConsoleCommand *result = new QScriptDebuggerScriptedConsoleCommand( |
420 | nameStr, groupStr, |
421 | shortDescStr, longDescStr, |
422 | aliases, seeAlso, |
423 | argTypes, subCommands, |
424 | global, execFunction, responseFunction); |
425 | return result; |
426 | } |
427 | |
428 | QT_END_NAMESPACE |
429 | |