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