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 "qqmlinfo.h"
41 
42 #include "qqmldata_p.h"
43 #include "qqmlcontext.h"
44 #include "qqmlcontext_p.h"
45 #include "qqmlmetatype_p.h"
46 #include "qqmlengine_p.h"
47 #include "qqmlsourcecoordinate_p.h"
48 
49 #include <QCoreApplication>
50 
51 QT_BEGIN_NAMESPACE
52 
53 /*!
54     \namespace QtQml
55     \inmodule QtQml
56     \brief Provides functions for producing logging messages for QML types.
57 */
58 
59 /*!
60     \fn QQmlInfo QtQml::qmlDebug(const QObject *object)
61     \since 5.9
62 
63     Prints debug messages that include the file and line number for the
64     specified QML \a object.
65 
66 //! [qqmlinfo-desc]
67     When QML types produce logging messages, it improves traceability
68     if they include the QML file and line number on which the
69     particular instance was instantiated.
70 
71     To include the file and line number, an object must be passed.  If
72     the file and line number is not available for that instance
73     (either it was not instantiated by the QML engine or location
74     information is disabled), "unknown location" will be used instead.
75 //! [qqmlinfo-desc]
76 
77     For example,
78 
79     \code
80     qmlDebug(object) << "Internal state: 42";
81     \endcode
82 
83     prints
84 
85     \badcode
86     QML MyCustomType (unknown location): Internal state: 42
87     \endcode
88 
89     \sa QtQml::qmlInfo, QtQml::qmlWarning
90 */
91 
92 /*!
93     \fn QQmlInfo QtQml::qmlInfo(const QObject *object)
94 
95     Prints informational messages that include the file and line number for the
96     specified QML \a object.
97 
98     \include qqmlinfo.cpp qqmlinfo-desc
99 
100     For example,
101 
102     \code
103     qmlInfo(object) << tr("component property is a write-once property");
104     \endcode
105 
106     prints
107 
108     \badcode
109     QML MyCustomType (unknown location): component property is a write-once property
110     \endcode
111 
112     \note In versions prior to Qt 5.9, qmlInfo reported messages using a warning
113     QtMsgType. For Qt 5.9 and above, qmlInfo uses an info QtMsgType. To send
114     warnings, use qmlWarning.
115 
116     \sa QtQml::qmlDebug, QtQml::qmlWarning
117 */
118 
119 /*!
120     \fn QQmlInfo QtQml::qmlWarning(const QObject *object)
121     \since 5.9
122 
123     Prints warning messages that include the file and line number for the
124     specified QML \a object.
125 
126     \include qqmlinfo.cpp qqmlinfo-desc
127 
128     For example,
129 
130     \code
131     qmlInfo(object) << tr("property cannot be set to 0");
132     \endcode
133 
134     prints
135 
136     \badcode
137     QML MyCustomType (unknown location): property cannot be set to 0
138     \endcode
139 
140     \sa QtQml::qmlDebug, QtQml::qmlInfo
141 */
142 
143 /*!
144     \fn QQmlInfo QtQml::qmlDebug(const QObject *object, const QQmlError &error)
145     \internal
146 */
147 
148 /*!
149     \fn QQmlInfo QtQml::qmlDebug(const QObject *object, const QList<QQmlError> &errors)
150     \internal
151 */
152 
153 /*!
154     \fn QQmlInfo QtQml::qmlInfo(const QObject *object, const QQmlError &error)
155     \internal
156 */
157 
158 /*!
159     \fn QQmlInfo QtQml::qmlInfo(const QObject *object, const QList<QQmlError> &errors)
160     \internal
161 */
162 
163 /*!
164     \fn QQmlInfo QtQml::qmlWarning(const QObject *object, const QQmlError &error)
165     \internal
166 */
167 
168 /*!
169     \fn QQmlInfo QtQml::qmlWarning(const QObject *object, const QList<QQmlError> &errors)
170     \internal
171 */
172 
173 class QQmlInfoPrivate
174 {
175 public:
QQmlInfoPrivate(QtMsgType type)176     QQmlInfoPrivate(QtMsgType type)
177         : ref (1)
178         , msgType(type)
179         , object(nullptr)
180     {}
181 
182     int ref;
183     QtMsgType msgType;
184     const QObject *object;
185     QString buffer;
186     QList<QQmlError> errors;
187 };
188 
QQmlInfo(QQmlInfoPrivate * p)189 QQmlInfo::QQmlInfo(QQmlInfoPrivate *p)
190 : QDebug(&p->buffer), d(p)
191 {
192     nospace();
193 }
194 
QQmlInfo(const QQmlInfo & other)195 QQmlInfo::QQmlInfo(const QQmlInfo &other)
196 : QDebug(other), d(other.d)
197 {
198     d->ref++;
199 }
200 
~QQmlInfo()201 QQmlInfo::~QQmlInfo()
202 {
203     if (0 == --d->ref) {
204         QList<QQmlError> errors = d->errors;
205 
206         QQmlEngine *engine = nullptr;
207 
208         if (!d->buffer.isEmpty()) {
209             QQmlError error;
210             error.setMessageType(d->msgType);
211 
212             QObject *object = const_cast<QObject *>(d->object);
213 
214             if (object) {
215                 // Some objects (e.g. like attached objects created in C++) won't have an associated engine,
216                 // but we can still try to look for a parent object that does.
217                 QObject *objectWithEngine = object;
218                 while (objectWithEngine) {
219                     engine = qmlEngine(objectWithEngine);
220                     if (engine)
221                         break;
222                     objectWithEngine = objectWithEngine->parent();
223                 }
224 
225                 if (!objectWithEngine || objectWithEngine == object) {
226                     d->buffer.prepend(QLatin1String("QML ") + QQmlMetaType::prettyTypeName(object) + QLatin1String(": "));
227                 } else {
228                     d->buffer.prepend(QLatin1String("QML ") + QQmlMetaType::prettyTypeName(objectWithEngine)
229                         + QLatin1String(" (parent or ancestor of ") + QQmlMetaType::prettyTypeName(object) + QLatin1String("): "));
230                 }
231 
232                 QQmlData *ddata = QQmlData::get(objectWithEngine ? objectWithEngine : object, false);
233                 if (ddata && ddata->outerContext) {
234                     error.setUrl(ddata->outerContext->url());
235                     error.setLine(qmlConvertSourceCoordinate<quint16, int>(ddata->lineNumber));
236                     error.setColumn(qmlConvertSourceCoordinate<quint16, int>(ddata->columnNumber));
237                 }
238             }
239 
240             error.setDescription(d->buffer);
241 
242             errors.prepend(error);
243         }
244 
245         QQmlEnginePrivate::warning(engine, errors);
246 
247         delete d;
248     }
249 }
250 
251 namespace QtQml {
252 
253 #define MESSAGE_FUNCS(FuncName, MessageLevel) \
254     QQmlInfo FuncName(const QObject *me) \
255     { \
256         QQmlInfoPrivate *d = new QQmlInfoPrivate(MessageLevel); \
257         d->object = me; \
258         return QQmlInfo(d); \
259     } \
260     QQmlInfo FuncName(const QObject *me, const QQmlError &error) \
261     { \
262         QQmlInfoPrivate *d = new QQmlInfoPrivate(MessageLevel); \
263         d->object = me; \
264         d->errors << error; \
265         return QQmlInfo(d); \
266     } \
267     QQmlInfo FuncName(const QObject *me, const QList<QQmlError> &errors) \
268     { \
269         QQmlInfoPrivate *d = new QQmlInfoPrivate(MessageLevel); \
270         d->object = me; \
271         d->errors = errors; \
272         return QQmlInfo(d); \
273     }
274 
275 MESSAGE_FUNCS(qmlDebug, QtMsgType::QtDebugMsg)
276 MESSAGE_FUNCS(qmlInfo, QtMsgType::QtInfoMsg)
277 MESSAGE_FUNCS(qmlWarning, QtMsgType::QtWarningMsg)
278 
279 
280 } // namespace QtQml
281 
282 #if QT_DEPRECATED_SINCE(5, 1)
283 
284 // Also define symbols outside namespace to keep binary compatibility with Qt 5.0
285 
qmlInfo(const QObject * me)286 QQmlInfo qmlInfo(const QObject *me)
287 {
288     return QtQml::qmlInfo(me);
289 }
290 
qmlInfo(const QObject * me,const QQmlError & error)291 QQmlInfo qmlInfo(const QObject *me, const QQmlError &error)
292 {
293     return QtQml::qmlInfo(me, error);
294 }
295 
qmlInfo(const QObject * me,const QList<QQmlError> & errors)296 QQmlInfo qmlInfo(const QObject *me, const QList<QQmlError> &errors)
297 {
298     return QtQml::qmlInfo(me, errors);
299 }
300 
301 #endif // QT_DEPRECATED_SINCE(5, 1)
302 
303 QT_END_NAMESPACE
304