Delay conversion of v8 exceptions to QQmlErrors.
This conversion in relatively expensive, and not required for transient exceptions that occur during startup due to the order of binding evaluation. Change-Id: I29a16c075890c8966c0bad0a77412ad232c791bb Reviewed-by: Matthew Vogt <matthew.vogt@nokia.com>
This commit is contained in:
parent
751c1ca093
commit
c734706c69
|
@ -238,23 +238,18 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
|
||||||
|
|
||||||
trace.event("writing binding result");
|
trace.event("writing binding result");
|
||||||
|
|
||||||
bool needsErrorData = false;
|
bool needsErrorLocationData = false;
|
||||||
if (!watcher.wasDeleted() && !hasError())
|
if (!watcher.wasDeleted() && !hasError())
|
||||||
needsErrorData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(),
|
needsErrorLocationData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(),
|
||||||
this, result, isUndefined, flags);
|
this, result, isUndefined, flags);
|
||||||
|
|
||||||
if (!watcher.wasDeleted()) {
|
if (!watcher.wasDeleted()) {
|
||||||
|
|
||||||
if (needsErrorData) {
|
if (needsErrorLocationData)
|
||||||
QUrl url = QUrl(m_url);
|
delayedError()->setErrorLocation(QUrl(m_url), m_lineNumber, m_columnNumber);
|
||||||
|
|
||||||
delayedError()->error.setUrl(url);
|
|
||||||
delayedError()->error.setLine(m_lineNumber);
|
|
||||||
delayedError()->error.setColumn(m_columnNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasError()) {
|
if (hasError()) {
|
||||||
if (!delayedError()->addError(ep)) ep->warning(this->error());
|
if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine));
|
||||||
} else {
|
} else {
|
||||||
clearError();
|
clearError();
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,8 +344,10 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
|
||||||
if (s->m_params) s->m_params->setValues(a);
|
if (s->m_params) s->m_params->setValues(a);
|
||||||
if (s->m_expression && s->m_expression->engine()) {
|
if (s->m_expression && s->m_expression->engine()) {
|
||||||
s->m_expression->evaluate(s->m_params);
|
s->m_expression->evaluate(s->m_params);
|
||||||
if (s->m_expression && s->m_expression->hasError())
|
if (s->m_expression && s->m_expression->hasError()) {
|
||||||
QQmlEnginePrivate::warning(s->m_expression->engine(), s->m_expression->error());
|
QQmlEngine *engine = s->m_expression->engine();
|
||||||
|
QQmlEnginePrivate::warning(engine, s->m_expression->error(engine));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (s->m_params) s->m_params->clearValues();
|
if (s->m_params) s->m_params->clearValues();
|
||||||
|
|
||||||
|
|
|
@ -884,7 +884,7 @@ void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionS
|
||||||
|
|
||||||
if (0 == enginePriv->inProgressCreations) {
|
if (0 == enginePriv->inProgressCreations) {
|
||||||
while (enginePriv->erroredBindings) {
|
while (enginePriv->erroredBindings) {
|
||||||
enginePriv->warning(enginePriv->erroredBindings->error);
|
enginePriv->warning(enginePriv->erroredBindings);
|
||||||
enginePriv->erroredBindings->removeError();
|
enginePriv->erroredBindings->removeError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1463,6 +1463,12 @@ void QQmlEnginePrivate::warning(const QList<QQmlError> &errors)
|
||||||
dumpwarning(errors);
|
dumpwarning(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QQmlEnginePrivate::warning(QQmlDelayedError *error)
|
||||||
|
{
|
||||||
|
Q_Q(QQmlEngine);
|
||||||
|
warning(error->error(q));
|
||||||
|
}
|
||||||
|
|
||||||
void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error)
|
void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error)
|
||||||
{
|
{
|
||||||
if (engine)
|
if (engine)
|
||||||
|
@ -1479,6 +1485,14 @@ void QQmlEnginePrivate::warning(QQmlEngine *engine, const QList<QQmlError> &erro
|
||||||
dumpwarning(error);
|
dumpwarning(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QQmlEnginePrivate::warning(QQmlEngine *engine, QQmlDelayedError *error)
|
||||||
|
{
|
||||||
|
if (engine)
|
||||||
|
QQmlEnginePrivate::get(engine)->warning(error);
|
||||||
|
else
|
||||||
|
dumpwarning(error->error(0));
|
||||||
|
}
|
||||||
|
|
||||||
void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error)
|
void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QQmlError &error)
|
||||||
{
|
{
|
||||||
if (engine)
|
if (engine)
|
||||||
|
|
|
@ -252,8 +252,10 @@ public:
|
||||||
void sendQuit();
|
void sendQuit();
|
||||||
void warning(const QQmlError &);
|
void warning(const QQmlError &);
|
||||||
void warning(const QList<QQmlError> &);
|
void warning(const QList<QQmlError> &);
|
||||||
|
void warning(QQmlDelayedError *);
|
||||||
static void warning(QQmlEngine *, const QQmlError &);
|
static void warning(QQmlEngine *, const QQmlError &);
|
||||||
static void warning(QQmlEngine *, const QList<QQmlError> &);
|
static void warning(QQmlEngine *, const QList<QQmlError> &);
|
||||||
|
static void warning(QQmlEngine *, QQmlDelayedError *);
|
||||||
static void warning(QQmlEnginePrivate *, const QQmlError &);
|
static void warning(QQmlEnginePrivate *, const QQmlError &);
|
||||||
static void warning(QQmlEnginePrivate *, const QList<QQmlError> &);
|
static void warning(QQmlEnginePrivate *, const QList<QQmlError> &);
|
||||||
|
|
||||||
|
|
|
@ -521,7 +521,7 @@ void QQmlExpression::clearError()
|
||||||
QQmlError QQmlExpression::error() const
|
QQmlError QQmlExpression::error() const
|
||||||
{
|
{
|
||||||
Q_D(const QQmlExpression);
|
Q_D(const QQmlExpression);
|
||||||
return d->error();
|
return d->error(engine());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -354,7 +354,7 @@ finishIncubate:
|
||||||
|
|
||||||
if (0 == enginePriv->inProgressCreations) {
|
if (0 == enginePriv->inProgressCreations) {
|
||||||
while (enginePriv->erroredBindings) {
|
while (enginePriv->erroredBindings) {
|
||||||
enginePriv->warning(enginePriv->erroredBindings->error);
|
enginePriv->warning(enginePriv->erroredBindings);
|
||||||
enginePriv->erroredBindings->removeError();
|
enginePriv->erroredBindings->removeError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -569,7 +569,7 @@ void QQmlIncubator::clear()
|
||||||
enginePriv->inProgressCreations--;
|
enginePriv->inProgressCreations--;
|
||||||
if (0 == enginePriv->inProgressCreations) {
|
if (0 == enginePriv->inProgressCreations) {
|
||||||
while (enginePriv->erroredBindings) {
|
while (enginePriv->erroredBindings) {
|
||||||
enginePriv->warning(enginePriv->erroredBindings->error);
|
enginePriv->warning(enginePriv->erroredBindings);
|
||||||
enginePriv->erroredBindings->removeError();
|
enginePriv->erroredBindings->removeError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,42 @@ bool QQmlDelayedError::addError(QQmlEnginePrivate *e)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QQmlDelayedError::setMessage(v8::Handle<v8::Message> message)
|
||||||
|
{
|
||||||
|
qPersistentDispose(m_message);
|
||||||
|
m_message = qPersistentNew<v8::Message>(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QQmlDelayedError::setErrorLocation(const QUrl &url, int line, int column)
|
||||||
|
{
|
||||||
|
m_error.setUrl(url);
|
||||||
|
m_error.setLine(line);
|
||||||
|
m_error.setColumn(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QQmlDelayedError::setErrorDescription(const QString &description)
|
||||||
|
{
|
||||||
|
m_error.setDescription(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Converting from a message to an error is relatively expensive.
|
||||||
|
|
||||||
|
We don't want to do this work for transient exceptions (exceptions
|
||||||
|
that occur during startup because of the order of binding
|
||||||
|
execution, but have gone away by the time startup has finished), so we
|
||||||
|
delay conversion until it is required for displaying the error.
|
||||||
|
*/
|
||||||
|
void QQmlDelayedError::convertMessageToError(QQmlEngine *engine) const
|
||||||
|
{
|
||||||
|
if (!m_message.IsEmpty() && engine) {
|
||||||
|
v8::HandleScope handle_scope;
|
||||||
|
v8::Context::Scope context_scope(QQmlEnginePrivate::getV8Engine(engine)->context());
|
||||||
|
QQmlExpressionPrivate::exceptionToError(m_message, m_error);
|
||||||
|
qPersistentDispose(m_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QQmlJavaScriptExpression::QQmlJavaScriptExpression(VTable *v)
|
QQmlJavaScriptExpression::QQmlJavaScriptExpression(VTable *v)
|
||||||
: m_vtable(v)
|
: m_vtable(v)
|
||||||
{
|
{
|
||||||
|
@ -142,12 +178,12 @@ QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
|
||||||
v8::Context::Scope scope(ep->v8engine()->context());
|
v8::Context::Scope scope(ep->v8engine()->context());
|
||||||
v8::Local<v8::Message> message = try_catch.Message();
|
v8::Local<v8::Message> message = try_catch.Message();
|
||||||
if (!message.IsEmpty()) {
|
if (!message.IsEmpty()) {
|
||||||
QQmlExpressionPrivate::exceptionToError(message, delayedError()->error);
|
delayedError()->setMessage(message);
|
||||||
} else {
|
} else {
|
||||||
if (hasDelayedError()) delayedError()->error = QQmlError();
|
if (hasDelayedError()) delayedError()->clearError();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (hasDelayedError()) delayedError()->error = QQmlError();
|
if (hasDelayedError()) delayedError()->clearError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,14 +273,14 @@ void QQmlJavaScriptExpression::GuardCapture::captureProperty(QObject *o, int c,
|
||||||
void QQmlJavaScriptExpression::clearError()
|
void QQmlJavaScriptExpression::clearError()
|
||||||
{
|
{
|
||||||
if (m_vtable.hasValue()) {
|
if (m_vtable.hasValue()) {
|
||||||
m_vtable.value().error = QQmlError();
|
m_vtable.value().clearError();
|
||||||
m_vtable.value().removeError();
|
m_vtable.value().removeError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlError QQmlJavaScriptExpression::error() const
|
QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const
|
||||||
{
|
{
|
||||||
if (m_vtable.hasValue()) return m_vtable.constValue()->error;
|
if (m_vtable.hasValue()) return m_vtable.constValue()->error(engine);
|
||||||
else return QQmlError();
|
else return QQmlError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,9 +65,7 @@ class QQmlDelayedError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
inline QQmlDelayedError() : nextError(0), prevError(0) {}
|
inline QQmlDelayedError() : nextError(0), prevError(0) {}
|
||||||
inline ~QQmlDelayedError() { removeError(); }
|
inline ~QQmlDelayedError() { qPersistentDispose(m_message); removeError(); }
|
||||||
|
|
||||||
QQmlError error;
|
|
||||||
|
|
||||||
bool addError(QQmlEnginePrivate *);
|
bool addError(QQmlEnginePrivate *);
|
||||||
|
|
||||||
|
@ -79,7 +77,20 @@ public:
|
||||||
prevError = 0;
|
prevError = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isValid() const { return !m_message.IsEmpty() || m_error.isValid(); }
|
||||||
|
inline const QQmlError &error(QQmlEngine *engine) const { convertMessageToError(engine); return m_error; }
|
||||||
|
inline void clearError() { qPersistentDispose(m_message); m_error = QQmlError(); }
|
||||||
|
|
||||||
|
void setMessage(v8::Handle<v8::Message> message);
|
||||||
|
void setErrorLocation(const QUrl &url, int line, int column);
|
||||||
|
void setErrorDescription(const QString &description);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void convertMessageToError(QQmlEngine *engine) const;
|
||||||
|
|
||||||
|
mutable QQmlError m_error;
|
||||||
|
mutable v8::Persistent<v8::Message> m_message;
|
||||||
|
|
||||||
QQmlDelayedError *nextError;
|
QQmlDelayedError *nextError;
|
||||||
QQmlDelayedError **prevError;
|
QQmlDelayedError **prevError;
|
||||||
};
|
};
|
||||||
|
@ -128,7 +139,7 @@ public:
|
||||||
|
|
||||||
inline bool hasError() const;
|
inline bool hasError() const;
|
||||||
inline bool hasDelayedError() const;
|
inline bool hasDelayedError() const;
|
||||||
QQmlError error() const;
|
QQmlError error(QQmlEngine *) const;
|
||||||
void clearError();
|
void clearError();
|
||||||
QQmlDelayedError *delayedError();
|
QQmlDelayedError *delayedError();
|
||||||
|
|
||||||
|
@ -242,7 +253,7 @@ void QQmlJavaScriptExpression::setScopeObject(QObject *v)
|
||||||
|
|
||||||
bool QQmlJavaScriptExpression::hasError() const
|
bool QQmlJavaScriptExpression::hasError() const
|
||||||
{
|
{
|
||||||
return m_vtable.hasValue() && m_vtable.constValue()->error.isValid();
|
return m_vtable.hasValue() && m_vtable.constValue()->isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QQmlJavaScriptExpression::hasDelayedError() const
|
bool QQmlJavaScriptExpression::hasDelayedError() const
|
||||||
|
|
|
@ -1545,7 +1545,7 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
|
||||||
&& !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
|
&& !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
|
||||||
// we explicitly disallow this case to avoid confusion. Users can still store one
|
// 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.
|
// in an array in a var property if they need to, but the common case is user error.
|
||||||
expression->delayedError()->error.setDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
|
expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1561,7 +1561,7 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
|
||||||
} else if (type == qMetaTypeId<QJSValue>()) {
|
} else if (type == qMetaTypeId<QJSValue>()) {
|
||||||
if (!result.IsEmpty() && result->IsFunction()
|
if (!result.IsEmpty() && result->IsFunction()
|
||||||
&& !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
|
&& !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
|
||||||
expression->delayedError()->error.setDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
|
expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
writeValueProperty(object, engine, core, QVariant::fromValue(v8engine->scriptValueFromInternal(result)), context, flags);
|
writeValueProperty(object, engine, core, QVariant::fromValue(v8engine->scriptValueFromInternal(result)), context, flags);
|
||||||
|
@ -1571,13 +1571,13 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
|
||||||
errorStr += QLatin1String("[unknown property type]");
|
errorStr += QLatin1String("[unknown property type]");
|
||||||
else
|
else
|
||||||
errorStr += QLatin1String(QMetaType::typeName(type));
|
errorStr += QLatin1String(QMetaType::typeName(type));
|
||||||
expression->delayedError()->error.setDescription(errorStr);
|
expression->delayedError()->setErrorDescription(errorStr);
|
||||||
return false;
|
return false;
|
||||||
} else if (result->IsFunction()) {
|
} else if (result->IsFunction()) {
|
||||||
if (!result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty())
|
if (!result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty())
|
||||||
expression->delayedError()->error.setDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
|
expression->delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration."));
|
||||||
else
|
else
|
||||||
expression->delayedError()->error.setDescription(QLatin1String("Unable to assign a function to a property of any type other than var."));
|
expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign a function to a property of any type other than var."));
|
||||||
return false;
|
return false;
|
||||||
} else if (!writeValueProperty(object, engine, core, value, context, flags)) {
|
} else if (!writeValueProperty(object, engine, core, value, context, flags)) {
|
||||||
|
|
||||||
|
@ -1606,10 +1606,10 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
|
||||||
if (!propertyType)
|
if (!propertyType)
|
||||||
propertyType = "[unknown property type]";
|
propertyType = "[unknown property type]";
|
||||||
|
|
||||||
expression->delayedError()->error.setDescription(QLatin1String("Unable to assign ") +
|
expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign ") +
|
||||||
QLatin1String(valueType) +
|
QLatin1String(valueType) +
|
||||||
QLatin1String(" to ") +
|
QLatin1String(" to ") +
|
||||||
QLatin1String(propertyType));
|
QLatin1String(propertyType));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -667,21 +667,18 @@ static void throwException(int id, QQmlDelayedError *error,
|
||||||
QV4Program *program, QQmlContextData *context,
|
QV4Program *program, QQmlContextData *context,
|
||||||
const QString &description = QString())
|
const QString &description = QString())
|
||||||
{
|
{
|
||||||
error->error.setUrl(context->url);
|
|
||||||
if (description.isEmpty())
|
if (description.isEmpty())
|
||||||
error->error.setDescription(QLatin1String("TypeError: Result of expression is not an object"));
|
error->setErrorDescription(QLatin1String("TypeError: Result of expression is not an object"));
|
||||||
else
|
else
|
||||||
error->error.setDescription(description);
|
error->setErrorDescription(description);
|
||||||
if (id != 0xFF) {
|
if (id != 0xFF) {
|
||||||
quint64 e = *((quint64 *)(program->data() + program->exceptionDataOffset) + id);
|
quint64 e = *((quint64 *)(program->data() + program->exceptionDataOffset) + id);
|
||||||
error->error.setLine((e >> 32) & 0xFFFFFFFF);
|
error->setErrorLocation(context->url, (e >> 32) & 0xFFFFFFFF, e & 0xFFFFFFFF);
|
||||||
error->error.setColumn(e & 0xFFFFFFFF);
|
|
||||||
} else {
|
} else {
|
||||||
error->error.setLine(-1);
|
error->setErrorLocation(context->url, -1, -1);
|
||||||
error->error.setColumn(-1);
|
|
||||||
}
|
}
|
||||||
if (!context->engine || !error->addError(QQmlEnginePrivate::get(context->engine)))
|
if (!context->engine || !error->addError(QQmlEnginePrivate::get(context->engine)))
|
||||||
QQmlEnginePrivate::warning(context->engine, error->error);
|
QQmlEnginePrivate::warning(context->engine, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const double QV4Bindings::D32 = 4294967296.0;
|
const double QV4Bindings::D32 = 4294967296.0;
|
||||||
|
|
|
@ -169,23 +169,20 @@ void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags)
|
||||||
&isUndefined);
|
&isUndefined);
|
||||||
|
|
||||||
trace.event("writing V8 result");
|
trace.event("writing V8 result");
|
||||||
bool needsErrorData = false;
|
bool needsErrorLocationData = false;
|
||||||
if (!watcher.wasDeleted() && !destroyedFlag() && !hasError()) {
|
if (!watcher.wasDeleted() && !destroyedFlag() && !hasError()) {
|
||||||
typedef QQmlPropertyPrivate PP;
|
typedef QQmlPropertyPrivate PP;
|
||||||
needsErrorData = !PP::writeBinding(*target, instruction->property, context, this, result,
|
needsErrorLocationData = !PP::writeBinding(*target, instruction->property, context, this, result,
|
||||||
isUndefined, flags);
|
isUndefined, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!watcher.wasDeleted() && !destroyedFlag()) {
|
if (!watcher.wasDeleted() && !destroyedFlag()) {
|
||||||
|
|
||||||
if (needsErrorData) {
|
if (needsErrorLocationData)
|
||||||
delayedError()->error.setUrl(parent->url());
|
delayedError()->setErrorLocation(parent->url(), instruction->line, -1);
|
||||||
delayedError()->error.setLine(instruction->line);
|
|
||||||
delayedError()->error.setColumn(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasError()) {
|
if (hasError()) {
|
||||||
if (!delayedError()->addError(ep)) ep->warning(delayedError()->error);
|
if (!delayedError()->addError(ep)) ep->warning(this->error(context->engine));
|
||||||
} else {
|
} else {
|
||||||
clearError();
|
clearError();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue