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:
Michael Brasser 2012-05-30 10:24:14 +10:00 committed by Qt by Nokia
parent 751c1ca093
commit c734706c69
12 changed files with 106 additions and 52 deletions

View File

@ -238,23 +238,18 @@ void QQmlBinding::update(QQmlPropertyPrivate::WriteFlags flags)
trace.event("writing binding result");
bool needsErrorData = false;
bool needsErrorLocationData = false;
if (!watcher.wasDeleted() && !hasError())
needsErrorData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(),
needsErrorLocationData = !QQmlPropertyPrivate::writeBinding(*m_coreObject, m_core, context(),
this, result, isUndefined, flags);
if (!watcher.wasDeleted()) {
if (needsErrorData) {
QUrl url = QUrl(m_url);
delayedError()->error.setUrl(url);
delayedError()->error.setLine(m_lineNumber);
delayedError()->error.setColumn(m_columnNumber);
}
if (needsErrorLocationData)
delayedError()->setErrorLocation(QUrl(m_url), m_lineNumber, m_columnNumber);
if (hasError()) {
if (!delayedError()->addError(ep)) ep->warning(this->error());
if (!delayedError()->addError(ep)) ep->warning(this->error(context()->engine));
} else {
clearError();
}

View File

@ -344,8 +344,10 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a)
if (s->m_params) s->m_params->setValues(a);
if (s->m_expression && s->m_expression->engine()) {
s->m_expression->evaluate(s->m_params);
if (s->m_expression && s->m_expression->hasError())
QQmlEnginePrivate::warning(s->m_expression->engine(), s->m_expression->error());
if (s->m_expression && s->m_expression->hasError()) {
QQmlEngine *engine = s->m_expression->engine();
QQmlEnginePrivate::warning(engine, s->m_expression->error(engine));
}
}
if (s->m_params) s->m_params->clearValues();

View File

@ -884,7 +884,7 @@ void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionS
if (0 == enginePriv->inProgressCreations) {
while (enginePriv->erroredBindings) {
enginePriv->warning(enginePriv->erroredBindings->error);
enginePriv->warning(enginePriv->erroredBindings);
enginePriv->erroredBindings->removeError();
}
}

View File

@ -1463,6 +1463,12 @@ void QQmlEnginePrivate::warning(const QList<QQmlError> &errors)
dumpwarning(errors);
}
void QQmlEnginePrivate::warning(QQmlDelayedError *error)
{
Q_Q(QQmlEngine);
warning(error->error(q));
}
void QQmlEnginePrivate::warning(QQmlEngine *engine, const QQmlError &error)
{
if (engine)
@ -1479,6 +1485,14 @@ void QQmlEnginePrivate::warning(QQmlEngine *engine, const QList<QQmlError> &erro
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)
{
if (engine)

View File

@ -252,8 +252,10 @@ public:
void sendQuit();
void warning(const QQmlError &);
void warning(const QList<QQmlError> &);
void warning(QQmlDelayedError *);
static void warning(QQmlEngine *, const QQmlError &);
static void warning(QQmlEngine *, const QList<QQmlError> &);
static void warning(QQmlEngine *, QQmlDelayedError *);
static void warning(QQmlEnginePrivate *, const QQmlError &);
static void warning(QQmlEnginePrivate *, const QList<QQmlError> &);

View File

@ -521,7 +521,7 @@ void QQmlExpression::clearError()
QQmlError QQmlExpression::error() const
{
Q_D(const QQmlExpression);
return d->error();
return d->error(engine());
}
/*!

View File

@ -354,7 +354,7 @@ finishIncubate:
if (0 == enginePriv->inProgressCreations) {
while (enginePriv->erroredBindings) {
enginePriv->warning(enginePriv->erroredBindings->error);
enginePriv->warning(enginePriv->erroredBindings);
enginePriv->erroredBindings->removeError();
}
}
@ -569,7 +569,7 @@ void QQmlIncubator::clear()
enginePriv->inProgressCreations--;
if (0 == enginePriv->inProgressCreations) {
while (enginePriv->erroredBindings) {
enginePriv->warning(enginePriv->erroredBindings->error);
enginePriv->warning(enginePriv->erroredBindings);
enginePriv->erroredBindings->removeError();
}
}

View File

@ -61,6 +61,42 @@ bool QQmlDelayedError::addError(QQmlEnginePrivate *e)
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)
: m_vtable(v)
{
@ -142,12 +178,12 @@ QQmlJavaScriptExpression::evaluate(QQmlContextData *context,
v8::Context::Scope scope(ep->v8engine()->context());
v8::Local<v8::Message> message = try_catch.Message();
if (!message.IsEmpty()) {
QQmlExpressionPrivate::exceptionToError(message, delayedError()->error);
delayedError()->setMessage(message);
} else {
if (hasDelayedError()) delayedError()->error = QQmlError();
if (hasDelayedError()) delayedError()->clearError();
}
} 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()
{
if (m_vtable.hasValue()) {
m_vtable.value().error = QQmlError();
m_vtable.value().clearError();
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();
}

View File

@ -65,9 +65,7 @@ class QQmlDelayedError
{
public:
inline QQmlDelayedError() : nextError(0), prevError(0) {}
inline ~QQmlDelayedError() { removeError(); }
QQmlError error;
inline ~QQmlDelayedError() { qPersistentDispose(m_message); removeError(); }
bool addError(QQmlEnginePrivate *);
@ -79,7 +77,20 @@ public:
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:
void convertMessageToError(QQmlEngine *engine) const;
mutable QQmlError m_error;
mutable v8::Persistent<v8::Message> m_message;
QQmlDelayedError *nextError;
QQmlDelayedError **prevError;
};
@ -128,7 +139,7 @@ public:
inline bool hasError() const;
inline bool hasDelayedError() const;
QQmlError error() const;
QQmlError error(QQmlEngine *) const;
void clearError();
QQmlDelayedError *delayedError();
@ -242,7 +253,7 @@ void QQmlJavaScriptExpression::setScopeObject(QObject *v)
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

View File

@ -1545,7 +1545,7 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
&& !result->ToObject()->GetHiddenValue(v8engine->bindingFlagKey()).IsEmpty()) {
// 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()->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;
}
@ -1561,7 +1561,7 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
} else if (type == qMetaTypeId<QJSValue>()) {
if (!result.IsEmpty() && result->IsFunction()
&& !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;
}
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]");
else
errorStr += QLatin1String(QMetaType::typeName(type));
expression->delayedError()->error.setDescription(errorStr);
expression->delayedError()->setErrorDescription(errorStr);
return false;
} else if (result->IsFunction()) {
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
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;
} else if (!writeValueProperty(object, engine, core, value, context, flags)) {
@ -1606,10 +1606,10 @@ bool QQmlPropertyPrivate::writeBinding(QObject *object,
if (!propertyType)
propertyType = "[unknown property type]";
expression->delayedError()->error.setDescription(QLatin1String("Unable to assign ") +
QLatin1String(valueType) +
QLatin1String(" to ") +
QLatin1String(propertyType));
expression->delayedError()->setErrorDescription(QLatin1String("Unable to assign ") +
QLatin1String(valueType) +
QLatin1String(" to ") +
QLatin1String(propertyType));
return false;
}

View File

@ -667,21 +667,18 @@ static void throwException(int id, QQmlDelayedError *error,
QV4Program *program, QQmlContextData *context,
const QString &description = QString())
{
error->error.setUrl(context->url);
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
error->error.setDescription(description);
error->setErrorDescription(description);
if (id != 0xFF) {
quint64 e = *((quint64 *)(program->data() + program->exceptionDataOffset) + id);
error->error.setLine((e >> 32) & 0xFFFFFFFF);
error->error.setColumn(e & 0xFFFFFFFF);
error->setErrorLocation(context->url, (e >> 32) & 0xFFFFFFFF, e & 0xFFFFFFFF);
} else {
error->error.setLine(-1);
error->error.setColumn(-1);
error->setErrorLocation(context->url, -1, -1);
}
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;

View File

@ -169,23 +169,20 @@ void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags)
&isUndefined);
trace.event("writing V8 result");
bool needsErrorData = false;
bool needsErrorLocationData = false;
if (!watcher.wasDeleted() && !destroyedFlag() && !hasError()) {
typedef QQmlPropertyPrivate PP;
needsErrorData = !PP::writeBinding(*target, instruction->property, context, this, result,
needsErrorLocationData = !PP::writeBinding(*target, instruction->property, context, this, result,
isUndefined, flags);
}
if (!watcher.wasDeleted() && !destroyedFlag()) {
if (needsErrorData) {
delayedError()->error.setUrl(parent->url());
delayedError()->error.setLine(instruction->line);
delayedError()->error.setColumn(-1);
}
if (needsErrorLocationData)
delayedError()->setErrorLocation(parent->url(), instruction->line, -1);
if (hasError()) {
if (!delayedError()->addError(ep)) ep->warning(delayedError()->error);
if (!delayedError()->addError(ep)) ep->warning(this->error(context->engine));
} else {
clearError();
}