Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlnativedebugservice.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <private/qqmldebugconnector_p.h>
7#include <private/qqmldebugserviceinterfaces_p.h>
8#include <private/qv4debugging_p.h>
9#include <private/qv4debugging_p.h>
10#include <private/qv4engine_p.h>
11#include <private/qv4identifierhash_p.h>
12#include <private/qv4identifiertable_p.h>
13#include <private/qv4objectiterator_p.h>
14#include <private/qv4runtime_p.h>
15#include <private/qv4script_p.h>
16#include <private/qv4stackframe_p.h>
17#include <private/qv4string_p.h>
18#include <private/qversionedpacket_p.h>
19
20#include <QtQml/qjsengine.h>
21#include <QtCore/qjsonarray.h>
22#include <QtCore/qjsondocument.h>
23#include <QtCore/qjsonobject.h>
24#include <QtCore/qjsonvalue.h>
25#include <QtCore/qvector.h>
26#include <QtCore/qpointer.h>
27
28//#define TRACE_PROTOCOL(s) qDebug() << s
29#define TRACE_PROTOCOL(s)
30
32
33using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
34
36{
37public:
38 BreakPoint() : id(-1), lineNumber(-1), enabled(false), ignoreCount(0), hitCount(0) {}
39 bool isValid() const { return lineNumber >= 0 && !fileName.isEmpty(); }
40
41 int id;
44 bool enabled;
47
49};
50
51inline size_t qHash(const BreakPoint &b, size_t seed = 0) noexcept
52{
53 return qHash(b.fileName, seed) ^ b.lineNumber;
54}
55
56inline bool operator==(const BreakPoint &a, const BreakPoint &b)
57{
58 return a.lineNumber == b.lineNumber && a.fileName == b.fileName
59 && a.enabled == b.enabled && a.condition == b.condition
61}
62
63static void setError(QJsonObject *response, const QString &msg)
64{
65 response->insert(QStringLiteral("type"), QStringLiteral("error"));
66 response->insert(QStringLiteral("msg"), msg);
67}
68
69class NativeDebugger;
70
72{
73public:
74 Collector(QV4::ExecutionEngine *engine)
76 {}
77
78 void collect(QJsonArray *output, const QString &parentIName, const QString &name,
79 const QV4::Value &value);
80
81 bool isExpanded(const QString &iname) const { return m_expanded.contains(iname); }
82
83public:
87};
88
89// Encapsulate Breakpoint handling
90// Could be made per-NativeDebugger (i.e. per execution engine, if needed)
92{
93public:
95
96 void handleSetBreakpoint(QJsonObject *response, const QJsonObject &arguments);
97 void handleRemoveBreakpoint(QJsonObject *response, const QJsonObject &arguments);
98
99 void removeBreakPoint(int id);
100 void enableBreakPoint(int id, bool onoff);
101
102 void setBreakOnThrow(bool onoff);
107};
108
109void BreakPointHandler::handleSetBreakpoint(QJsonObject *response, const QJsonObject &arguments)
110{
111 TRACE_PROTOCOL("SET BREAKPOINT" << arguments);
112 QString type = arguments.value(QLatin1String("type")).toString();
113
114 QString fileName = arguments.value(QLatin1String("file")).toString();
115 if (fileName.isEmpty()) {
116 setError(response, QStringLiteral("breakpoint has no file name"));
117 return;
118 }
119
120 int line = arguments.value(QLatin1String("line")).toInt(-1);
121 if (line < 0) {
122 setError(response, QStringLiteral("breakpoint has an invalid line number"));
123 return;
124 }
125
126 BreakPoint bp;
127 bp.id = m_lastBreakpoint++;
128 bp.fileName = fileName.mid(fileName.lastIndexOf('/') + 1);
129 bp.lineNumber = line;
130 bp.enabled = arguments.value(QLatin1String("enabled")).toBool(true);
131 bp.condition = arguments.value(QLatin1String("condition")).toString();
132 bp.ignoreCount = arguments.value(QLatin1String("ignorecount")).toInt();
133 m_breakPoints.append(bp);
134
135 m_haveBreakPoints = true;
136
137 response->insert(QStringLiteral("type"), type);
138 response->insert(QStringLiteral("breakpoint"), bp.id);
139}
140
141void BreakPointHandler::handleRemoveBreakpoint(QJsonObject *response, const QJsonObject &arguments)
142{
143 int id = arguments.value(QLatin1String("id")).toInt();
145 response->insert(QStringLiteral("id"), id);
146}
147
149{
150public:
151 NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine);
152
153 void signalEmitted(const QString &signal);
154
155 QV4::ExecutionEngine *engine() const { return m_engine; }
156
158 return m_pauseRequested
159 || m_service->m_breakHandler->m_haveBreakPoints
160 || m_stepping >= StepOver;
161 }
162
165 void leavingFunction(const QV4::ReturnedValue &retVal) override;
167
168 void handleCommand(QJsonObject *response, const QString &cmd, const QJsonObject &arguments);
169
170private:
171 void handleBacktrace(QJsonObject *response, const QJsonObject &arguments);
172 void handleVariables(QJsonObject *response, const QJsonObject &arguments);
173 void handleExpressions(QJsonObject *response, const QJsonObject &arguments);
174
175 void handleDebuggerDeleted(QObject *debugger);
176
177 QV4::ReturnedValue evaluateExpression(const QString &expression);
178 bool checkCondition(const QString &expression);
179
180 QStringList breakOnSignals;
181
182 enum Speed {
183 NotStepping = 0,
184 StepOut,
185 StepOver,
186 StepIn,
187 };
188
189 void pauseAndWait();
190 void pause();
191 void handleContinue(QJsonObject *reponse, Speed speed);
192
193 QV4::Function *getFunction() const;
194
195 bool reallyHitTheBreakPoint(const QV4::Function *function, int lineNumber);
196
197 QV4::ExecutionEngine *m_engine;
199 QV4::CppStackFrame *m_currentFrame = nullptr;
200 Speed m_stepping;
201 bool m_pauseRequested;
202 bool m_runningJob;
203
204 QV4::PersistentValue m_returnedValue;
205};
206
207bool NativeDebugger::checkCondition(const QString &expression)
208{
209 QV4::Scope scope(m_engine);
210 QV4::ScopedValue r(scope, evaluateExpression(expression));
211 return r->booleanValue();
212}
213
214QV4::ReturnedValue NativeDebugger::evaluateExpression(const QString &expression)
215{
216 QV4::Scope scope(m_engine);
217 m_runningJob = true;
218
219 QV4::ExecutionContext *ctx = m_engine->currentStackFrame ? m_engine->currentContext()
220 : m_engine->scriptContext();
221
222 QV4::Script script(ctx, QV4::Compiler::ContextType::Eval, expression);
223 if (const QV4::Function *function = m_engine->currentStackFrame
224 ? m_engine->currentStackFrame->v4Function : m_engine->globalCode) {
225 script.setStrictMode(function->isStrict());
226 }
227 // In order for property lookups in QML to work, we need to disable fast v4 lookups.
228 // That is a side-effect of inheritContext.
229 script.setInheritContext();
230 script.parse();
231 if (!m_engine->hasException) {
232 if (m_engine->currentStackFrame) {
233 QV4::ScopedValue thisObject(scope, m_engine->currentStackFrame->thisObject());
234 script.run(thisObject);
235 } else {
236 script.run();
237 }
238 }
239
240 m_runningJob = false;
241 return QV4::Encode::undefined();
242}
243
244NativeDebugger::NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine)
246{
247 m_stepping = NotStepping;
248 m_pauseRequested = false;
249 m_runningJob = false;
250 m_service = service;
251 m_engine = engine;
252 TRACE_PROTOCOL("Creating native debugger");
253}
254
255void NativeDebugger::signalEmitted(const QString &signal)
256{
257 //This function is only called by QQmlBoundSignal
258 //only if there is a slot connected to the signal. Hence, there
259 //is no need for additional check.
260
261 //Parse just the name and remove the class info
262 //Normalize to Lower case.
263 QString signalName = signal.left(signal.indexOf(QLatin1Char('('))).toLower();
264
265 for (const QString &signal : std::as_const(breakOnSignals)) {
266 if (signal == signalName) {
267 // TODO: pause debugger
268 break;
269 }
270 }
271}
272
273void NativeDebugger::handleCommand(QJsonObject *response, const QString &cmd,
274 const QJsonObject &arguments)
275{
276 if (cmd == QLatin1String("backtrace"))
277 handleBacktrace(response, arguments);
278 else if (cmd == QLatin1String("variables"))
279 handleVariables(response, arguments);
280 else if (cmd == QLatin1String("expressions"))
281 handleExpressions(response, arguments);
282 else if (cmd == QLatin1String("stepin"))
283 handleContinue(response, StepIn);
284 else if (cmd == QLatin1String("stepout"))
285 handleContinue(response, StepOut);
286 else if (cmd == QLatin1String("stepover"))
287 handleContinue(response, StepOver);
288 else if (cmd == QLatin1String("continue"))
289 handleContinue(response, NotStepping);
290}
291
292static QString encodeFrame(QV4::CppStackFrame *f)
293{
294 QQmlDebugPacket ds;
295 ds << quintptr(f);
296 return QString::fromLatin1(ds.data().toHex());
297}
298
299static void decodeFrame(const QString &f, QV4::CppStackFrame **frame)
300{
301 quintptr rawFrame;
302 QQmlDebugPacket ds(QByteArray::fromHex(f.toLatin1()));
303 ds >> rawFrame;
304 *frame = reinterpret_cast<QV4::CppStackFrame *>(rawFrame);
305}
306
307void NativeDebugger::handleBacktrace(QJsonObject *response, const QJsonObject &arguments)
308{
309 int limit = arguments.value(QLatin1String("limit")).toInt(0);
310
311 QJsonArray frameArray;
312 QV4::CppStackFrame *f= m_engine->currentStackFrame;
313 for (int i = 0; i < limit && f; ++i) {
314 QV4::Function *function = f->v4Function;
315
316 QJsonObject frame;
317 frame.insert(QStringLiteral("language"), QStringLiteral("js"));
318 frame.insert(QStringLiteral("context"), encodeFrame(f));
319
320 if (QV4::Heap::String *functionName = function->name())
321 frame.insert(QStringLiteral("function"), functionName->toQString());
322 frame.insert(QStringLiteral("file"), function->sourceFile());
323
324 int line = f->lineNumber();
325 frame.insert(QStringLiteral("line"), (line < 0 ? -line : line));
326
327 frameArray.push_back(frame);
328
329 f = f->parentFrame();
330 }
331
332 response->insert(QStringLiteral("frames"), frameArray);
333}
334
335void Collector::collect(QJsonArray *out, const QString &parentIName, const QString &name,
336 const QV4::Value &value)
337{
338 QJsonObject dict;
339 QV4::Scope scope(m_engine);
340
341 QString nonEmptyName = name.isEmpty() ? QString::fromLatin1("@%1").arg(m_anonCount++) : name;
342 QString iname = parentIName + QLatin1Char('.') + nonEmptyName;
343 dict.insert(QStringLiteral("iname"), iname);
344 dict.insert(QStringLiteral("name"), nonEmptyName);
345
346 QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(m_engine, value));
347 dict.insert(QStringLiteral("type"), typeString->toQStringNoThrow());
348
349 switch (value.type()) {
350 case QV4::Value::Empty_Type:
351 dict.insert(QStringLiteral("valueencoded"), QStringLiteral("empty"));
352 dict.insert(QStringLiteral("haschild"), false);
353 break;
354 case QV4::Value::Undefined_Type:
355 dict.insert(QStringLiteral("valueencoded"), QStringLiteral("undefined"));
356 dict.insert(QStringLiteral("haschild"), false);
357 break;
358 case QV4::Value::Null_Type:
359 dict.insert(QStringLiteral("type"), QStringLiteral("object"));
360 dict.insert(QStringLiteral("valueencoded"), QStringLiteral("null"));
361 dict.insert(QStringLiteral("haschild"), false);
362 break;
363 case QV4::Value::Boolean_Type:
364 dict.insert(QStringLiteral("value"), value.booleanValue());
365 dict.insert(QStringLiteral("haschild"), false);
366 break;
367 case QV4::Value::Managed_Type:
368 if (const QV4::String *string = value.as<QV4::String>()) {
369 dict.insert(QStringLiteral("value"), string->toQStringNoThrow());
370 dict.insert(QStringLiteral("haschild"), false);
371 dict.insert(QStringLiteral("valueencoded"), QStringLiteral("utf16"));
372 dict.insert(QStringLiteral("quoted"), true);
373 } else if (const QV4::ArrayObject *array = value.as<QV4::ArrayObject>()) {
374 // The size of an array is number of its numerical properties.
375 // We don't consider free form object properties here.
376 const uint n = array->getLength();
377 dict.insert(QStringLiteral("value"), qint64(n));
378 dict.insert(QStringLiteral("valueencoded"), QStringLiteral("itemcount"));
379 dict.insert(QStringLiteral("haschild"), qint64(n));
380 if (isExpanded(iname)) {
381 QJsonArray children;
382 for (uint i = 0; i < n; ++i) {
383 QV4::ReturnedValue v = array->get(i);
384 QV4::ScopedValue sval(scope, v);
385 collect(&children, iname, QString::number(i), *sval);
386 }
387 dict.insert(QStringLiteral("children"), children);
388 }
389 } else if (const QV4::Object *object = value.as<QV4::Object>()) {
390 QJsonArray children;
391 bool expanded = isExpanded(iname);
392 qint64 numProperties = 0;
393 QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
394 QV4::ScopedProperty p(scope);
395 QV4::ScopedPropertyKey name(scope);
396 while (true) {
397 QV4::PropertyAttributes attrs;
398 name = it.next(p, &attrs);
399 if (!name->isValid())
400 break;
401 if (name->isStringOrSymbol()) {
402 ++numProperties;
403 if (expanded) {
404 QV4::Value v = p.property->value;
405 collect(&children, iname, name->toQString(), v);
406 }
407 }
408 }
409 dict.insert(QStringLiteral("value"), numProperties);
410 dict.insert(QStringLiteral("valueencoded"), QStringLiteral("itemcount"));
411 dict.insert(QStringLiteral("haschild"), numProperties > 0);
412 if (expanded)
413 dict.insert(QStringLiteral("children"), children);
414 }
415 break;
416 case QV4::Value::Integer_Type:
417 dict.insert(QStringLiteral("value"), value.integerValue());
418 dict.insert(QStringLiteral("haschild"), false);
419 break;
420 default: // double
421 dict.insert(QStringLiteral("value"), value.doubleValue());
422 dict.insert(QStringLiteral("haschild"), false);
423 }
424
425 out->append(dict);
426}
427
428void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &arguments)
429{
430 TRACE_PROTOCOL("Build variables");
431 QV4::CppStackFrame *frame = nullptr;
432 decodeFrame(arguments.value(QLatin1String("context")).toString(), &frame);
433 if (!frame) {
434 setError(response, QStringLiteral("No stack frame passed"));
435 return;
436 }
437 TRACE_PROTOCOL("Context: " << frame);
438
439 QV4::ExecutionEngine *engine = frame->v4Function->internalClass->engine;
440 if (!engine) {
441 setError(response, QStringLiteral("No execution engine passed"));
442 return;
443 }
444 TRACE_PROTOCOL("Engine: " << engine);
445
446 Collector collector(engine);
447 const QJsonArray expanded = arguments.value(QLatin1String("expanded")).toArray();
448 for (const QJsonValue ex : expanded)
449 collector.m_expanded.append(ex.toString());
450 TRACE_PROTOCOL("Expanded: " << collector.m_expanded);
451
452 QJsonArray output;
453 QV4::Scope scope(engine);
454
455 QV4::ScopedValue thisObject(scope, frame->thisObject());
456 collector.collect(&output, QString(), QStringLiteral("this"), thisObject);
457 QV4::Scoped<QV4::CallContext> callContext(scope, frame->callContext());
458 if (callContext) {
459 QV4::Heap::InternalClass *ic = callContext->internalClass();
460 QV4::ScopedValue v(scope);
461 for (uint i = 0; i < ic->size; ++i) {
462 QV4::ScopedValue stringOrSymbol(scope, ic->keyAt(i));
463 QV4::ScopedString propName(scope, stringOrSymbol->toString(scope.engine));
464 v = callContext->getProperty(propName);
465 collector.collect(&output, QString(), propName->toQString(), v);
466 }
467 }
468
469 response->insert(QStringLiteral("variables"), output);
470}
471
472void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject &arguments)
473{
474 TRACE_PROTOCOL("Evaluate expressions");
475 QV4::CppStackFrame *frame = nullptr;
476 decodeFrame(arguments.value(QLatin1String("context")).toString(), &frame);
477 if (!frame) {
478 setError(response, QStringLiteral("No stack frame passed"));
479 return;
480 }
481 TRACE_PROTOCOL("Context: " << executionContext);
482
483 QV4::ExecutionEngine *engine = frame->v4Function->internalClass->engine;
484 if (!engine) {
485 setError(response, QStringLiteral("No execution engine passed"));
486 return;
487 }
488 TRACE_PROTOCOL("Engines: " << engine << m_engine);
489
490 Collector collector(engine);
491 const QJsonArray expanded = arguments.value(QLatin1String("expanded")).toArray();
492 for (const QJsonValue ex : expanded)
493 collector.m_expanded.append(ex.toString());
494 TRACE_PROTOCOL("Expanded: " << collector.m_expanded);
495
496 QJsonArray output;
497 QV4::Scope scope(engine);
498
499 const QJsonArray expressions = arguments.value(QLatin1String("expressions")).toArray();
500 for (const QJsonValue expr : expressions) {
501 QString expression = expr.toObject().value(QLatin1String("expression")).toString();
502 QString name = expr.toObject().value(QLatin1String("name")).toString();
503 TRACE_PROTOCOL("Evaluate expression: " << expression);
504 m_runningJob = true;
505
506 QV4::ScopedValue result(scope, evaluateExpression(expression));
507
508 m_runningJob = false;
509 if (result->isUndefined()) {
510 QJsonObject dict;
511 dict.insert(QStringLiteral("name"), name);
512 dict.insert(QStringLiteral("valueencoded"), QStringLiteral("undefined"));
513 output.append(dict);
514 } else if (result.ptr && result.ptr->rawValue()) {
515 collector.collect(&output, QString(), name, *result);
516 } else {
517 QJsonObject dict;
518 dict.insert(QStringLiteral("name"), name);
519 dict.insert(QStringLiteral("valueencoded"), QStringLiteral("notaccessible"));
520 output.append(dict);
521 }
522 TRACE_PROTOCOL("EXCEPTION: " << engine->hasException);
523 engine->hasException = false;
524 }
525
526 response->insert(QStringLiteral("expressions"), output);
527}
528
530{
531 for (int i = 0; i != m_breakPoints.size(); ++i) {
532 if (m_breakPoints.at(i).id == id) {
533 m_breakPoints.remove(i);
534 m_haveBreakPoints = !m_breakPoints.isEmpty();
535 return;
536 }
537 }
538}
539
540void BreakPointHandler::enableBreakPoint(int id, bool enabled)
541{
542 m_breakPoints[id].enabled = enabled;
543}
544
545void NativeDebugger::pause()
546{
547 m_pauseRequested = true;
548}
549
550void NativeDebugger::handleContinue(QJsonObject *response, Speed speed)
551{
552 Q_UNUSED(response);
553
554 if (!m_returnedValue.isUndefined())
555 m_returnedValue.set(m_engine, QV4::Encode::undefined());
556
557 m_currentFrame = m_engine->currentStackFrame;
558 m_stepping = speed;
559}
560
562{
563 if (m_runningJob) // do not re-enter when we're doing a job for the debugger.
564 return;
565
566 if (m_stepping == StepOver) {
567 if (m_currentFrame == m_engine->currentStackFrame)
568 pauseAndWait();
569 return;
570 }
571
572 if (m_stepping == StepIn) {
573 pauseAndWait();
574 return;
575 }
576
577 if (m_pauseRequested) { // Serve debugging requests from the agent
578 m_pauseRequested = false;
579 pauseAndWait();
580 return;
581 }
582
583 if (m_service->m_breakHandler->m_haveBreakPoints) {
584 if (QV4::Function *function = getFunction()) {
585 // lineNumber will be negative for Ret instructions, so those won't match
586 const int lineNumber = m_engine->currentStackFrame->lineNumber();
587 if (reallyHitTheBreakPoint(function, lineNumber))
588 pauseAndWait();
589 }
590 }
591}
592
594{
595 if (m_runningJob)
596 return;
597
598 if (m_stepping == StepIn) {
599 m_currentFrame = m_engine->currentStackFrame;
600 }
601}
602
603void NativeDebugger::leavingFunction(const QV4::ReturnedValue &retVal)
604{
605 if (m_runningJob)
606 return;
607
608 if (m_stepping != NotStepping && m_currentFrame == m_engine->currentStackFrame) {
609 m_currentFrame = m_currentFrame->parentFrame();
610 m_stepping = StepOver;
611 m_returnedValue.set(m_engine, retVal);
612 }
613}
614
616{
617 if (!m_service->m_breakHandler->m_breakOnThrow)
618 return;
619
620 if (m_runningJob) // do not re-enter when we're doing a job for the debugger.
621 return;
622
623 QJsonObject event;
624 // TODO: complete this!
625 event.insert(QStringLiteral("event"), QStringLiteral("exception"));
626 m_service->emitAsynchronousMessageToClient(event);
627}
628
629QV4::Function *NativeDebugger::getFunction() const
630{
631 if (m_engine->currentStackFrame)
632 return m_engine->currentStackFrame->v4Function;
633 else
634 return m_engine->globalCode;
635}
636
637void NativeDebugger::pauseAndWait()
638{
639 QJsonObject event;
640
641 event.insert(QStringLiteral("event"), QStringLiteral("break"));
642 event.insert(QStringLiteral("language"), QStringLiteral("js"));
643 if (QV4::CppStackFrame *frame = m_engine->currentStackFrame) {
644 QV4::Function *function = frame->v4Function;
645 event.insert(QStringLiteral("file"), function->sourceFile());
646 int line = frame->lineNumber();
647 event.insert(QStringLiteral("line"), (line < 0 ? -line : line));
648 }
649
650 m_service->emitAsynchronousMessageToClient(event);
651}
652
653bool NativeDebugger::reallyHitTheBreakPoint(const QV4::Function *function, int lineNumber)
654{
655 for (int i = 0, n = m_service->m_breakHandler->m_breakPoints.size(); i != n; ++i) {
656 const BreakPoint &bp = m_service->m_breakHandler->m_breakPoints.at(i);
657 if (bp.lineNumber == lineNumber) {
658 const QString base = QUrl(function->sourceFile()).fileName();
659 if (bp.fileName.endsWith(base)) {
660 if (bp.condition.isEmpty() || checkCondition(bp.condition)) {
661 BreakPoint &mbp = m_service->m_breakHandler->m_breakPoints[i];
662 ++mbp.hitCount;
663 if (mbp.hitCount > mbp.ignoreCount)
664 return true;
665 }
666 }
667 }
668 }
669 return false;
670}
671
674{
675 m_breakHandler = new BreakPointHandler;
676}
677
679{
680 delete m_breakHandler;
681}
682
684{
685 TRACE_PROTOCOL("Adding engine" << engine);
686 if (engine) {
687 QV4::ExecutionEngine *ee = engine->handle();
688 TRACE_PROTOCOL("Adding execution engine" << ee);
689 if (ee) {
690 NativeDebugger *debugger = new NativeDebugger(this, ee);
691 if (state() == Enabled)
692 ee->setDebugger(debugger);
693 m_debuggers.append(QPointer<NativeDebugger>(debugger));
694 }
695 }
696 QQmlDebugService::engineAboutToBeAdded(engine);
697}
698
700{
701 TRACE_PROTOCOL("Removing engine" << engine);
702 if (engine) {
703 QV4::ExecutionEngine *executionEngine = engine->handle();
704 const auto debuggersCopy = m_debuggers;
705 for (NativeDebugger *debugger : debuggersCopy) {
706 if (debugger->engine() == executionEngine)
707 m_debuggers.removeAll(debugger);
708 }
709 }
710 QQmlDebugService::engineAboutToBeRemoved(engine);
711}
712
713void QQmlNativeDebugServiceImpl::stateAboutToBeChanged(QQmlDebugService::State state)
714{
715 if (state == Enabled) {
716 for (NativeDebugger *debugger : std::as_const(m_debuggers)) {
717 QV4::ExecutionEngine *engine = debugger->engine();
718 if (!engine->debugger())
719 engine->setDebugger(debugger);
720 }
721 }
722 QQmlDebugService::stateAboutToBeChanged(state);
723}
724
725void QQmlNativeDebugServiceImpl::messageReceived(const QByteArray &message)
726{
727 TRACE_PROTOCOL("Native message received: " << message);
728 QJsonObject request = QJsonDocument::fromJson(message).object();
729 QJsonObject response;
730 QJsonObject arguments = request.value(QLatin1String("arguments")).toObject();
731 QString cmd = request.value(QLatin1String("command")).toString();
732
733 if (cmd == QLatin1String("setbreakpoint")) {
734 m_breakHandler->handleSetBreakpoint(&response, arguments);
735 } else if (cmd == QLatin1String("removebreakpoint")) {
736 m_breakHandler->handleRemoveBreakpoint(&response, arguments);
737 } else if (cmd == QLatin1String("echo")) {
738 response.insert(QStringLiteral("result"), arguments);
739 } else {
740 for (NativeDebugger *debugger : std::as_const(m_debuggers))
741 if (debugger)
742 debugger->handleCommand(&response, cmd, arguments);
743 }
744 QJsonDocument doc;
745 doc.setObject(response);
746 QByteArray ba = doc.toJson(QJsonDocument::Compact);
747 TRACE_PROTOCOL("Sending synchronous response:" << ba.constData() << endl);
748 emit messageToClient(s_key, ba);
749}
750
752{
753 QJsonDocument doc;
754 doc.setObject(message);
755 QByteArray ba = doc.toJson(QJsonDocument::Compact);
756 TRACE_PROTOCOL("Sending asynchronous message:" << ba.constData() << endl);
757 emit messageToClient(s_key, ba);
758}
759
760QT_END_NAMESPACE
void setBreakOnThrow(bool onoff)
void enableBreakPoint(int id, bool onoff)
QVector< BreakPoint > m_breakPoints
void handleRemoveBreakpoint(QJsonObject *response, const QJsonObject &arguments)
void handleSetBreakpoint(QJsonObject *response, const QJsonObject &arguments)
Collector(QV4::ExecutionEngine *engine)
void collect(QJsonArray *output, const QString &parentIName, const QString &name, const QV4::Value &value)
QV4::ExecutionEngine * m_engine
bool isExpanded(const QString &iname) const
void maybeBreakAtInstruction() override
void aboutToThrow() override
void handleCommand(QJsonObject *response, const QString &cmd, const QJsonObject &arguments)
QV4::ExecutionEngine * engine() const
NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine)
void leavingFunction(const QV4::ReturnedValue &retVal) override
void enteringFunction() override
bool pauseAtNextOpportunity() const override
void signalEmitted(const QString &signal)
void stateAboutToBeChanged(State state) override
void engineAboutToBeRemoved(QJSEngine *engine) override
void engineAboutToBeAdded(QJSEngine *engine) override
void emitAsynchronousMessageToClient(const QJsonObject &message)
void messageReceived(const QByteArray &message) override
static void setError(QJsonObject *response, const QString &msg)
static QString encodeFrame(QV4::CppStackFrame *f)
size_t qHash(const BreakPoint &b, size_t seed=0) noexcept
bool operator==(const BreakPoint &a, const BreakPoint &b)
static void decodeFrame(const QString &f, QV4::CppStackFrame **frame)
#define TRACE_PROTOCOL(x)