From 49a3883e86b61d8facfeea9c43037d484cb50b92 Mon Sep 17 00:00:00 2001 From: Matthew Vogt Date: Mon, 4 Jun 2012 14:43:38 +1000 Subject: Use V4 binding for non-final properties where possible When a property referenced in a binding is not marked as final, do not automatically abort optimization. Instead generate both V4 and V8 binidngs, and only fall back to the V8 binding if necessary at run time. Change-Id: I1bcc7e2b495935c5d519a9a223f640c1972cdb4e Reviewed-by: Michael Brasser --- src/qml/qml/v4/qv4bindings.cpp | 54 ++++++++++++++++++++++++++++++++++------ src/qml/qml/v4/qv4bindings_p.h | 13 ++++++---- src/qml/qml/v4/qv4compiler.cpp | 8 +++--- src/qml/qml/v4/qv4compiler_p.h | 2 +- src/qml/qml/v4/qv4compiler_p_p.h | 3 +++ src/qml/qml/v4/qv4irbuilder.cpp | 37 ++++++++++----------------- src/qml/qml/v4/qv4irbuilder_p.h | 3 ++- 7 files changed, 78 insertions(+), 42 deletions(-) (limited to 'src/qml/qml/v4') diff --git a/src/qml/qml/v4/qv4bindings.cpp b/src/qml/qml/v4/qv4bindings.cpp index f011bc8f26..c0e0f22fad 100644 --- a/src/qml/qml/v4/qv4bindings.cpp +++ b/src/qml/qml/v4/qv4bindings.cpp @@ -298,13 +298,14 @@ QV4Bindings::~QV4Bindings() delete [] subscriptions; subscriptions = 0; } -QQmlAbstractBinding *QV4Bindings::configBinding(int index, QObject *target, +QQmlAbstractBinding *QV4Bindings::configBinding(int index, int fallbackIndex, QObject *target, QObject *scope, int property, int line, int column) { Binding *rv = bindings + index; rv->index = index; + rv->fallbackIndex = fallbackIndex; rv->property = property; rv->target = target; rv->scope = scope; @@ -436,6 +437,9 @@ void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags) return; } + bool invalidated = false; + bool *inv = (binding->fallbackIndex != -1) ? &invalidated : 0; + binding->updating = true; if (binding->property & 0xFFFF0000) { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); @@ -445,9 +449,11 @@ void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags) vt->read(*binding->target, binding->property & 0xFFFF); QObject *target = vt; - run(binding->index, binding->executedBlocks, context, binding, binding->scope, target, flags); + run(binding->index, binding->executedBlocks, context, binding, binding->scope, target, flags, inv); - vt->write(*binding->target, binding->property & 0xFFFF, flags); + if (!invalidated) { + vt->write(*binding->target, binding->property & 0xFFFF, flags); + } } else { QQmlData *data = QQmlData::get(*binding->target); QQmlPropertyData *propertyData = (data && data->propertyCache ? data->propertyCache->property(binding->property) : 0); @@ -457,12 +463,20 @@ void QV4Bindings::run(Binding *binding, QQmlPropertyPrivate::WriteFlags flags) v8::HandleScope handle_scope; v8::Context::Scope context_scope(QQmlEnginePrivate::get(context->engine)->v8engine()->context()); - run(binding->index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags); + run(binding->index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags, inv); } else { - run(binding->index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags); + run(binding->index, binding->executedBlocks, context, binding, binding->scope, *binding->target, flags, inv); } } binding->updating = false; + + if (invalidated) { + // This binding is no longer valid - fallback to V8 + Q_ASSERT(binding->fallbackIndex > -1); + QQmlAbstractBinding *b = QQmlPropertyPrivate::activateSharedBinding(context, binding->fallbackIndex, flags); + Q_ASSERT(b == binding); + b->destroy(); + } } @@ -765,6 +779,25 @@ inline quint32 QV4Bindings::toUint32(double n) MARK_REGISTER(reg); \ } +//TODO: avoid construction of name and name-based lookup +#define INVALIDATION_CHECK(inv, obj, index) { \ + if ((inv) != 0) { \ + QQmlData *data = QQmlData::get((obj)); \ + if (data && !data->propertyCache) { \ + data->propertyCache = QQmlEnginePrivate::get(context->engine)->cache(object); \ + if (data->propertyCache) data->propertyCache->addref(); \ + } \ + QQmlPropertyData *prop = (data && data->propertyCache) ? data->propertyCache->property((index)) : 0; \ + if (prop && prop->isOverridden()) { \ + int resolvedIndex = data->propertyCache->property(prop->name(obj))->coreIndex; \ + if (index < resolvedIndex) { \ + *(inv) = true; \ + goto programExit; \ + } \ + } \ + } \ +} + #ifdef QML_THREADED_INTERPRETER void **QV4Bindings::getDecodeInstrTable() { @@ -774,7 +807,7 @@ void **QV4Bindings::getDecodeInstrTable() quint32 executedBlocks = 0; dummy->run(0, executedBlocks, 0, 0, 0, 0, QQmlPropertyPrivate::BypassInterceptor, - &decode_instr); + 0, &decode_instr); dummy->release(); } return decode_instr; @@ -784,7 +817,8 @@ void **QV4Bindings::getDecodeInstrTable() void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, QQmlContextData *context, QQmlDelayedError *error, QObject *scope, QObject *output, - QQmlPropertyPrivate::WriteFlags storeFlags + QQmlPropertyPrivate::WriteFlags storeFlags, + bool *invalidated #ifdef QML_THREADED_INTERPRETER ,void ***table #endif @@ -857,8 +891,10 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, QObject *object = reg.getQObject(); if (!object) { - reg.setUndefined(); + THROW_EXCEPTION(instr->fetchAndSubscribe.exceptionId); } else { + INVALIDATION_CHECK(invalidated, object, instr->fetchAndSubscribe.property.coreIndex); + int subIdx = instr->fetchAndSubscribe.subscription; Subscription *sub = 0; if (subIdx != -1) { @@ -2111,6 +2147,8 @@ void QV4Bindings::run(int instrIndex, quint32 &executedBlocks, if (!object) { THROW_EXCEPTION(instr->fetch.exceptionId); } else { + INVALIDATION_CHECK(invalidated, object, instr->fetch.index); + const Register::Type valueType = (Register::Type)instr->fetch.valueType; reg.init(valueType); if (instr->fetch.valueType >= FirstCleanupType) diff --git a/src/qml/qml/v4/qv4bindings_p.h b/src/qml/qml/v4/qv4bindings_p.h index e9c6301cef..0c92cc4b45 100644 --- a/src/qml/qml/v4/qv4bindings_p.h +++ b/src/qml/qml/v4/qv4bindings_p.h @@ -71,7 +71,7 @@ public: QV4Bindings(const char *program, QQmlContextData *context); virtual ~QV4Bindings(); - QQmlAbstractBinding *configBinding(int index, QObject *target, + QQmlAbstractBinding *configBinding(int index, int fallbackIndex, QObject *target, QObject *scope, int property, int line, int column); @@ -80,8 +80,8 @@ public: #endif struct Binding : public QQmlAbstractBinding, public QQmlDelayedError { - Binding() : QQmlAbstractBinding(V4), enabled(false), updating(0), property(0), - scope(0), target(0), executedBlocks(0), parent(0) {} + Binding() : QQmlAbstractBinding(V4), index(-1), fallbackIndex(-1), enabled(false), + updating(0), property(0), scope(0), target(0), executedBlocks(0), parent(0) {} // Inherited from QQmlAbstractBinding static void destroy(QQmlAbstractBinding *); @@ -96,9 +96,11 @@ public: int targetProperty; }; - int index:30; + int index:15; + int fallbackIndex:15; bool enabled:1; bool updating:1; + // Encoding of property is coreIndex | (propType << 16) | (valueTypeIndex << 24) // propType and valueTypeIndex are only set if the property is a value type property int property; @@ -134,7 +136,8 @@ private: void init(); void run(int instr, quint32 &executedBlocks, QQmlContextData *context, QQmlDelayedError *error, QObject *scope, QObject *output, - QQmlPropertyPrivate::WriteFlags storeFlags + QQmlPropertyPrivate::WriteFlags storeFlags, + bool *invalidated #ifdef QML_THREADED_INTERPRETER , void ***decode_instr = 0 #endif diff --git a/src/qml/qml/v4/qv4compiler.cpp b/src/qml/qml/v4/qv4compiler.cpp index bac1f2c131..8f87583a94 100644 --- a/src/qml/qml/v4/qv4compiler.cpp +++ b/src/qml/qml/v4/qv4compiler.cpp @@ -66,7 +66,7 @@ using namespace QQmlJS; QV4CompilerPrivate::QV4CompilerPrivate() : subscriptionOffset(0) , _function(0) , _block(0) , _discarded(false), registerCount(0) - , bindingLine(0), bindingColumn(0) + , bindingLine(0), bindingColumn(0), invalidatable(false) { } @@ -1258,6 +1258,7 @@ void QV4CompilerPrivate::resetInstanceState() patches.clear(); pool.clear(); currentReg = 0; + invalidatable = false; } /*! @@ -1304,7 +1305,7 @@ bool QV4CompilerPrivate::compile(QQmlJS::AST::Node *node) IR::Function thisFunction(&pool), *function = &thisFunction; QV4IRBuilder irBuilder(expression, engine); - if (!irBuilder(function, node)) + if (!irBuilder(function, node, &invalidatable)) return false; bool discarded = false; @@ -1445,7 +1446,7 @@ bool QV4Compiler::isValid() const /* -1 on failure, otherwise the binding index to use. */ -int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine) +int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine, bool *invalidatable) { if (!expression.expression.asAST()) return false; @@ -1456,6 +1457,7 @@ int QV4Compiler::compile(const Expression &expression, QQmlEnginePrivate *engine d->engine = engine; if (d->compile(expression.expression.asAST())) { + *invalidatable = d->isInvalidatable(); return d->commitCompile(); } else { return -1; diff --git a/src/qml/qml/v4/qv4compiler_p.h b/src/qml/qml/v4/qv4compiler_p.h index cf0d51914b..ec246cacba 100644 --- a/src/qml/qml/v4/qv4compiler_p.h +++ b/src/qml/qml/v4/qv4compiler_p.h @@ -89,7 +89,7 @@ public: }; // -1 on failure, otherwise the binding index to use - int compile(const Expression &, QQmlEnginePrivate *); + int compile(const Expression &, QQmlEnginePrivate *, bool *); // Returns the compiled program QByteArray program() const; diff --git a/src/qml/qml/v4/qv4compiler_p_p.h b/src/qml/qml/v4/qv4compiler_p_p.h index 0c06ade87f..12beaa0fbb 100644 --- a/src/qml/qml/v4/qv4compiler_p_p.h +++ b/src/qml/qml/v4/qv4compiler_p_p.h @@ -127,6 +127,8 @@ public: bool compile(QQmlJS::AST::Node *); + bool isInvalidatable() const { return invalidatable; } + int registerLiteralString(quint8 reg, const QStringRef &); QByteArray data; @@ -235,6 +237,7 @@ private: quint32 currentBlockMask; int bindingLine; int bindingColumn; + bool invalidatable; }; diff --git a/src/qml/qml/v4/qv4irbuilder.cpp b/src/qml/qml/v4/qv4irbuilder.cpp index ddc2264cf7..79090b4269 100644 --- a/src/qml/qml/v4/qv4irbuilder.cpp +++ b/src/qml/qml/v4/qv4irbuilder.cpp @@ -91,14 +91,15 @@ static IR::Type irTypeFromVariantType(int t, QQmlEnginePrivate *engine) } } -QV4IRBuilder::QV4IRBuilder(const QV4Compiler::Expression *expr, - QQmlEnginePrivate *engine) -: m_expression(expr), m_engine(engine), _function(0), _block(0), _discard(false) +QV4IRBuilder::QV4IRBuilder(const QV4Compiler::Expression *expr, + QQmlEnginePrivate *engine) +: m_expression(expr), m_engine(engine), _function(0), _block(0), _discard(false), + _invalidatable(false) { } bool QV4IRBuilder::operator()(QQmlJS::IR::Function *function, - QQmlJS::AST::Node *ast) + QQmlJS::AST::Node *ast, bool *invalidatable) { bool discarded = false; @@ -142,6 +143,7 @@ bool QV4IRBuilder::operator()(QQmlJS::IR::Function *function, qSwap(_function, function); qSwap(_discard, discarded); + *invalidatable = _invalidatable; return !discarded; } @@ -615,12 +617,8 @@ bool QV4IRBuilder::visit(AST::FieldMemberExpression *ast) if (!data || data->isFunction()) return false; // Don't support methods (or non-existing properties ;) - if(!data->isFinal()) { - if (qmlVerboseCompiler()) - qWarning() << "*** non-final attached property:" - << (*baseName->id + QLatin1Char('.') + ast->name.toString()); - return false; // We don't know enough about this property - } + if (!data->isFinal()) + _invalidatable = true; IR::Type irType = irTypeFromVariantType(data->propType, m_engine); _expr.code = _block->SYMBOL(baseName, irType, name, attachedMeta, data, line, column); @@ -654,12 +652,8 @@ bool QV4IRBuilder::visit(AST::FieldMemberExpression *ast) if (!data || data->isFunction()) return false; // Don't support methods (or non-existing properties ;) - if (!data->isFinal()) { - if (qmlVerboseCompiler()) - qWarning() << "*** non-final attached property:" - << (*baseName->id + QLatin1Char('.') + ast->name.toString()); - return false; // We don't know enough about this property - } + if (!data->isFinal()) + _invalidatable = true; IR::Type irType = irTypeFromVariantType(data->propType, m_engine); _expr.code = _block->SYMBOL(baseName, irType, name, baseName->meta, data, line, column); @@ -690,20 +684,15 @@ bool QV4IRBuilder::visit(AST::FieldMemberExpression *ast) break; case IR::Name::Property: - if (baseName->type == IR::ObjectType && !baseName->meta.isNull() && - baseName->property->isFinal()) { + if (baseName->type == IR::ObjectType && !baseName->meta.isNull()) { QQmlMetaObject meta = m_engine->metaObjectForType(baseName->property->propType); QQmlPropertyCache *cache = meta.propertyCache(m_engine); if (!cache) return false; if (QQmlPropertyData *data = cache->property(name)) { - if (!data->isFinal()) { - if (qmlVerboseCompiler()) - qWarning() << "*** non-final property access:" - << (*baseName->id + QLatin1Char('.') + ast->name.toString()); - return false; // We don't know enough about this property - } + if (!baseName->property->isFinal() || !data->isFinal()) + _invalidatable = true; IR::Type irType = irTypeFromVariantType(data->propType, m_engine); _expr.code = _block->SYMBOL(baseName, irType, name, diff --git a/src/qml/qml/v4/qv4irbuilder_p.h b/src/qml/qml/v4/qv4irbuilder_p.h index e73ec22750..e4f75d575c 100644 --- a/src/qml/qml/v4/qv4irbuilder_p.h +++ b/src/qml/qml/v4/qv4irbuilder_p.h @@ -55,7 +55,7 @@ class QV4IRBuilder : public QQmlJS::AST::Visitor public: QV4IRBuilder(const QV4Compiler::Expression *, QQmlEnginePrivate *); - bool operator()(QQmlJS::IR::Function *, QQmlJS::AST::Node *); + bool operator()(QQmlJS::IR::Function *, QQmlJS::AST::Node *, bool *invalidatable); protected: struct ExprResult { @@ -229,6 +229,7 @@ private: QQmlJS::IR::Function *_function; QQmlJS::IR::BasicBlock *_block; bool _discard; + bool _invalidatable; ExprResult _expr; }; -- cgit v1.2.3