QQmlError.object now holds the scope object that caused exceptions.
authorMark Visser <mjmvisser@gmail.com>
Tue, 30 Apr 2013 18:12:37 +0000 (14:12 -0400)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Wed, 8 May 2013 08:46:13 +0000 (10:46 +0200)
Exception errors sent via QQmlEngine::warnings lacked a pointer to the
containing scope. I added an object property to QQmlError, and the
necessary code to fill it with the QObject* scope from binding exception
callbacks.

Task-number: QTBUG-30930
Change-Id: I2a987e8cefc3a2a474d338893e9ebcb77c167adf
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
Reviewed-by: Alan Alpert <aalpert@blackberry.com>

src/qml/qml/qqmlerror.cpp
src/qml/qml/qqmlerror.h
src/qml/qml/qqmljavascriptexpression.cpp
src/qml/qml/qqmljavascriptexpression_p.h
src/qml/qml/qqmlproperty.cpp
src/qml/qml/v4/qv4bindings.cpp
src/qml/qml/v8/qv8bindings.cpp

index 98d8259..0c8e46b 100644 (file)
@@ -84,10 +84,11 @@ public:
     QString description;
     quint16 line;
     quint16 column;
+    QObject *object;
 };
 
 QQmlErrorPrivate::QQmlErrorPrivate()
-: line(0), column(0)
+: line(0), column(0), object()
 {
 }
 
@@ -122,6 +123,7 @@ QQmlError &QQmlError::operator=(const QQmlError &other)
         d->description = other.d->description;
         d->line = other.d->line;
         d->column = other.d->column;
+        d->object = other.d->object;
     }
     return *this;
 }
@@ -215,6 +217,27 @@ void QQmlError::setColumn(int column)
 }
 
 /*!
+    Returns the nearest object where this error occurred.
+    Exceptions in bound property expressions set this to the object
+    to which the property belongs. It will be 0 for all
+    other exceptions.
+ */
+QObject *QQmlError::object() const
+{
+    if (d) return d->object;
+    else return 0;
+}
+
+/*!
+    Sets the nearest \a object where this error occurred.
+ */
+void QQmlError::setObject(QObject *object)
+{
+    if (!d) d = new QQmlErrorPrivate;
+    d->object = object;
+}
+
+/*!
     Returns the error as a human readable string.
 */
 QString QQmlError::toString() const
index cea9ee4..cfa0dfc 100644 (file)
@@ -70,6 +70,8 @@ public:
     void setLine(int);
     int column() const;
     void setColumn(int);
+    QObject *object() const;
+    void setObject(QObject *);
 
     QString toString() const;
 private:
index 4568307..2a1ed14 100644 (file)
@@ -79,6 +79,11 @@ void QQmlDelayedError::setErrorDescription(const QString &description)
     m_error.setDescription(description);
 }
 
+void QQmlDelayedError::setErrorObject(QObject *object)
+{
+    m_error.setObject(object);
+}
+
 /*
     Converting from a message to an error is relatively expensive.
 
@@ -348,6 +353,7 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
         error.setDescription(QLatin1String("Exception occurred during function compilation"));
         error.setLine(line);
         error.setUrl(QUrl::fromLocalFile(filename));
+        error.setObject(scope);
         v8::Local<v8::Message> message = tc.Message();
         if (!message.IsEmpty())
             QQmlExpressionPrivate::exceptionToError(message, error);
@@ -360,6 +366,7 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
         error.setDescription(QLatin1String("Exception occurred during function evaluation"));
         error.setLine(line);
         error.setUrl(QUrl::fromLocalFile(filename));
+        error.setObject(scope);
         v8::Local<v8::Message> message = tc.Message();
         if (!message.IsEmpty())
             QQmlExpressionPrivate::exceptionToError(message, error);
@@ -390,6 +397,7 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
         error.setDescription(QLatin1String("Exception occurred during function compilation"));
         error.setLine(line);
         error.setUrl(QUrl::fromLocalFile(filename));
+        error.setObject(scope);
         v8::Local<v8::Message> message = tc.Message();
         if (!message.IsEmpty())
             QQmlExpressionPrivate::exceptionToError(message, error);
@@ -402,6 +410,7 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scope,
         error.setDescription(QLatin1String("Exception occurred during function evaluation"));
         error.setLine(line);
         error.setUrl(QUrl::fromLocalFile(filename));
+        error.setObject(scope);
         v8::Local<v8::Message> message = tc.Message();
         if (!message.IsEmpty())
             QQmlExpressionPrivate::exceptionToError(message, error);
index c48972e..b521ea3 100644 (file)
@@ -84,6 +84,7 @@ public:
     void setMessage(v8::Handle<v8::Message> message);
     void setErrorLocation(const QUrl &url, quint16 line, quint16 column);
     void setErrorDescription(const QString &description);
+    void setErrorObject(QObject *object);
 
 private:
     void convertMessageToError(QQmlEngine *engine) const;
index 0baf450..0936df5 100644 (file)
@@ -1555,6 +1555,7 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
             // we explicitly disallow this case to avoid confusion.  Users can still store one
             // in an array in a var property if they need to, but the common case is user error.
             expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
+            expression->delayedError()->setErrorObject(object);
             return false;
         }
 
@@ -1570,6 +1571,7 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
         if (!result.IsEmpty() && result->IsFunction()
                 && !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
             expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
+            expression->delayedError()->setErrorObject(object);
             return false;
         }
         writeValueProperty(object, core, QVariant::fromValue(v8engine->scriptValueFromInternal(result)), context, flags);
@@ -1580,12 +1582,14 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
         else
             errorStr += QLatin1String(QMetaType::typeName(type));
         expression->delayedError()->setErrorDescription(errorStr);
+        expression->delayedError()->setErrorObject(object);
         return false;
     } else if (result->IsFunction()) {
         if (!result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty())
             expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
         else
             expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var."));
+        expression->delayedError()->setErrorObject(object);
         return false;
     } else if (!writeValueProperty(object, core, value, context, flags)) {
 
@@ -1618,6 +1622,7 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
                                                         QLatin1String(valueType) +
                                                         QLatin1String(" to ") +
                                                         QLatin1String(propertyType));
+        expression->delayedError()->setErrorObject(object);
         return false;
     }
 
index b680bf7..668f7b4 100644 (file)
@@ -793,6 +793,7 @@ static void throwException(int id, QQmlDelayedError *error,
         error->setErrorDescription(QLatin1String("TypeError: Result of expression is not an object"));
     else
         error->setErrorDescription(description);
+    error->setErrorObject(context->contextObject);
     if (id != 0xFF) {
         quint32 e = *((quint32 *)(program->data() + program->exceptionDataOffset) + id);
         error->setErrorLocation(context->url, (e >> 16), (e & 0xFFFF));
index 8d133e7..757d9d9 100644 (file)
@@ -191,6 +191,7 @@ void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags)
                 delayedError()->setErrorLocation(parent->url(), instruction->line, 0);
 
             if (hasError()) {
+                delayedError()->setErrorObject(object());
                 if (!delayedError()->addError(ep)) ep->warning(this->error(context->engine));
             } else {
                 clearError();