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
4#include "qv4debugjob.h"
5
6#include <private/qqmlcontext_p.h>
7#include <private/qqmldebugservice_p.h>
8#include <private/qv4jscall_p.h>
9#include <private/qv4qmlcontext_p.h>
10#include <private/qv4qobjectwrapper_p.h>
11#include <private/qv4script_p.h>
12#include <private/qv4stackframe_p.h>
13
14#include <QtQml/qqmlengine.h>
15
16#include <QtCore/qpointer.h>
17
18QT_BEGIN_NAMESPACE
19
20QV4DebugJob::~QV4DebugJob()
21{
22}
23
24JavaScriptJob::JavaScriptJob(QV4::ExecutionEngine *engine, int frameNr, int context,
25 const QString &script) :
26 engine(engine), frameNr(frameNr), context(context), script(script),
27 resultIsException(false)
28{}
29
30void JavaScriptJob::run()
31{
32 QV4::Scope scope(engine);
33
34 QV4::ScopedContext ctx(scope, engine->currentStackFrame ? engine->currentContext()
35 : engine->scriptContext());
36
37 QV4::CppStackFrame *frame = engine->currentStackFrame;
38
39 for (int i = 0; frame && i < frameNr; ++i)
40 frame = frame->parentFrame();
41 if (frameNr > 0 && frame)
42 ctx = frame->context();
43
44 if (context >= 0) {
45 QObject *forId = QQmlDebugService::objectForId(id: context);
46 QQmlContext *extraContext = qmlContext(forId);
47 if (extraContext)
48 ctx = QV4::QmlContext::create(parent: ctx, context: QQmlContextData::get(context: extraContext), scopeObject: forId);
49 } else if (frameNr < 0) { // Use QML context if available
50 QQmlEngine *qmlEngine = engine->qmlEngine();
51 if (qmlEngine) {
52 QQmlContext *qmlRootContext = qmlEngine->rootContext();
53 QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context: qmlRootContext);
54
55 QV4::ScopedObject withContext(scope, engine->newObject());
56 QV4::ScopedString k(scope);
57 QV4::ScopedValue v(scope);
58 const QList<QPointer<QObject>> instances = ctxtPriv->instances();
59 for (const QPointer<QObject> &object : instances) {
60 if (QQmlContext *context = qmlContext(object.data())) {
61 if (QQmlRefPointer<QQmlContextData> cdata = QQmlContextData::get(context)) {
62 v = QV4::QObjectWrapper::wrap(engine, object);
63 k = engine->newString(s: cdata->findObjectId(obj: object));
64 withContext->put(name: k, v);
65 }
66 }
67 }
68 if (!engine->qmlContext())
69 ctx = QV4::QmlContext::create(parent: ctx, context: QQmlContextData::get(context: qmlRootContext), scopeObject: nullptr);
70 }
71 }
72
73 QV4::Script script(ctx, QV4::Compiler::ContextType::Eval, this->script);
74 if (const QV4::Function *function = frame ? frame->v4Function : engine->globalCode)
75 script.strictMode = function->isStrict();
76
77 // In order for property lookups in QML to work, we need to disable fast v4 lookups. That
78 // is a side-effect of inheritContext.
79 script.inheritContext = true;
80 script.parse();
81 QV4::ScopedValue result(scope);
82 if (!scope.hasException()) {
83 if (frame) {
84 QV4::ScopedValue thisObject(scope, frame->thisObject());
85 result = script.run(thisObject);
86 } else {
87 result = script.run();
88 }
89 }
90 if (scope.hasException()) {
91 result = scope.engine->catchException();
92 resultIsException = true;
93 }
94 handleResult(result);
95}
96
97bool JavaScriptJob::hasExeption() const
98{
99 return resultIsException;
100}
101
102BacktraceJob::BacktraceJob(QV4DataCollector *collector, int fromFrame, int toFrame) :
103 CollectJob(collector), fromFrame(fromFrame), toFrame(toFrame)
104{
105}
106
107void BacktraceJob::run()
108{
109 QJsonArray frameArray;
110 QVector<QV4::StackFrame> frames = collector->engine()->stackTrace(frameLimit: toFrame);
111 for (int i = fromFrame; i < toFrame && i < frames.size(); ++i)
112 frameArray.push_back(t: collector->buildFrame(stackFrame: frames[i], frameNr: i));
113 if (frameArray.isEmpty()) {
114 result.insert(QStringLiteral("totalFrames"), value: 0);
115 } else {
116 result.insert(QStringLiteral("fromFrame"), value: fromFrame);
117 result.insert(QStringLiteral("toFrame"), value: fromFrame + frameArray.size());
118 result.insert(QStringLiteral("frames"), value: frameArray);
119 }
120}
121
122FrameJob::FrameJob(QV4DataCollector *collector, int frameNr) :
123 CollectJob(collector), frameNr(frameNr), success(false)
124{
125}
126
127void FrameJob::run()
128{
129 QVector<QV4::StackFrame> frames = collector->engine()->stackTrace(frameLimit: frameNr + 1);
130 if (frameNr >= frames.size()) {
131 success = false;
132 } else {
133 result = collector->buildFrame(stackFrame: frames[frameNr], frameNr);
134 success = true;
135 }
136}
137
138bool FrameJob::wasSuccessful() const
139{
140 return success;
141}
142
143ScopeJob::ScopeJob(QV4DataCollector *collector, int frameNr, int scopeNr) :
144 CollectJob(collector), frameNr(frameNr), scopeNr(scopeNr), success(false)
145{
146}
147
148void ScopeJob::run()
149{
150 QJsonObject object;
151 success = collector->collectScope(dict: &object, frameNr, scopeNr);
152
153 if (success) {
154 QVector<QV4::Heap::ExecutionContext::ContextType> scopeTypes =
155 collector->getScopeTypes(frame: frameNr);
156 result[QLatin1String("type")] = QV4DataCollector::encodeScopeType(scopeType: scopeTypes[scopeNr]);
157 } else {
158 result[QLatin1String("type")] = -1;
159 }
160 result[QLatin1String("index")] = scopeNr;
161 result[QLatin1String("frameIndex")] = frameNr;
162 result[QLatin1String("object")] = object;
163}
164
165bool ScopeJob::wasSuccessful() const
166{
167 return success;
168}
169
170ValueLookupJob::ValueLookupJob(const QJsonArray &handles, QV4DataCollector *collector) :
171 CollectJob(collector), handles(handles) {}
172
173void ValueLookupJob::run()
174{
175 // Open a QML context if we don't have one, yet. We might run into QML objects when looking up
176 // refs and that will crash without a valid QML context. Mind that engine->qmlContext() is only
177 // set if the engine is currently executing QML code.
178 QScopedPointer<QObject> scopeObject;
179 QV4::ExecutionEngine *engine = collector->engine();
180 QV4::Scope scope(engine);
181 QV4::Heap::ExecutionContext *qmlContext = engine->qmlContext();
182 if (engine->qmlEngine() && !qmlContext) {
183 scopeObject.reset(other: new QObject);
184 qmlContext = QV4::QmlContext::create(parent: engine->currentContext(),
185 context: QQmlContextData::get(context: engine->qmlEngine()->rootContext()),
186 scopeObject: scopeObject.data());
187 }
188 QV4::Scoped<QV4::ExecutionContext> scopedContext(scope, qmlContext);
189 QV4::ScopedStackFrame frame(scope, scopedContext);
190 for (const QJsonValue handle : handles) {
191 QV4DataCollector::Ref ref = handle.toInt();
192 if (!collector->isValidRef(ref)) {
193 exception = QString::fromLatin1(ba: "Invalid Ref: %1").arg(a: ref);
194 break;
195 }
196 result[QString::number(ref)] = collector->lookupRef(ref);
197 }
198}
199
200const QString &ValueLookupJob::exceptionMessage() const
201{
202 return exception;
203}
204
205ExpressionEvalJob::ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr,
206 int context, const QString &expression,
207 QV4DataCollector *collector) :
208 JavaScriptJob(engine, frameNr, context, expression), collector(collector)
209{
210}
211
212void ExpressionEvalJob::handleResult(QV4::ScopedValue &value)
213{
214 if (hasExeption())
215 exception = value->toQStringNoThrow();
216 result = collector->lookupRef(ref: collector->addValueRef(value));
217}
218
219const QString &ExpressionEvalJob::exceptionMessage() const
220{
221 return exception;
222}
223
224const QJsonObject &ExpressionEvalJob::returnValue() const
225{
226 return result;
227}
228
229GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine)
230 : engine(engine)
231{}
232
233void GatherSourcesJob::run()
234{
235 const auto compilationUnits = engine->compilationUnits();
236 for (const auto &unit : compilationUnits) {
237 QString fileName = unit->fileName();
238 if (!fileName.isEmpty())
239 sources.append(t: fileName);
240 }
241}
242
243const QStringList &GatherSourcesJob::result() const
244{
245 return sources;
246}
247
248EvalJob::EvalJob(QV4::ExecutionEngine *engine, const QString &script) :
249 JavaScriptJob(engine, /*frameNr*/-1, /*context*/ -1, script), result(false)
250{}
251
252void EvalJob::handleResult(QV4::ScopedValue &result)
253{
254 this->result = result->toBoolean();
255}
256
257bool EvalJob::resultAsBoolean() const
258{
259 return result;
260}
261
262QT_END_NAMESPACE
263

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp