aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/v4
diff options
context:
space:
mode:
authorMatthew Vogt <matthew.vogt@nokia.com>2012-06-04 14:43:38 +1000
committerQt by Nokia <qt-info@nokia.com>2012-07-09 01:36:23 +0200
commit49a3883e86b61d8facfeea9c43037d484cb50b92 (patch)
treeba1afdf6ddc3740f26c5e3c95cb2fffeb953abb4 /src/qml/qml/v4
parent3095493a4ce4bec11b9381a3d1d23f17b313332c (diff)
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 <michael.brasser@nokia.com>
Diffstat (limited to 'src/qml/qml/v4')
-rw-r--r--src/qml/qml/v4/qv4bindings.cpp54
-rw-r--r--src/qml/qml/v4/qv4bindings_p.h13
-rw-r--r--src/qml/qml/v4/qv4compiler.cpp8
-rw-r--r--src/qml/qml/v4/qv4compiler_p.h2
-rw-r--r--src/qml/qml/v4/qv4compiler_p_p.h3
-rw-r--r--src/qml/qml/v4/qv4irbuilder.cpp37
-rw-r--r--src/qml/qml/v4/qv4irbuilder_p.h3
7 files changed, 78 insertions, 42 deletions
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;
};