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

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