Centralize deferred binding bit information in CompiledData::Binding

Ultimately the decision which bindings to initialize in a deferred way depends
on the data in the meta-object (deferred property names entry). The hash in
QQmlCompiledData is just caching this information. We are better off storing
this single bit right in the binding itself instead of in a parallel data
structure.

Change-Id: Ib66d3550210af1f882b98b0ba9089391813d69ad
Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>
This commit is contained in:
Simon Hausmann 2016-05-26 16:26:33 +02:00
parent 515efdb8a6
commit f27d058c11
6 changed files with 142 additions and 58 deletions

View File

@ -202,6 +202,12 @@ bool QQmlTypeCompiler::compile()
return false;
}
{
QQmlDeferredBindingScanner deferredBindingScanner(this);
if (!deferredBindingScanner.scanObject())
return false;
}
// Compile JS binding expressions and signal handlers
if (!document->javaScriptCompilationUnit) {
{
@ -366,11 +372,6 @@ const QV4::Compiler::StringTableGenerator *QQmlTypeCompiler::stringPool() const
return &document->jsGenerator.stringTable;
}
void QQmlTypeCompiler::setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject)
{
compiledData->deferredBindingsPerObject = deferredBindingsPerObject;
}
void QQmlTypeCompiler::setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData)
{
compiledData->compilationUnit->bindingPropertyDataPerObject = propertyData;
@ -1661,6 +1662,103 @@ bool QQmlComponentAndAliasResolver::resolveAliases()
return true;
}
QQmlDeferredBindingScanner::QQmlDeferredBindingScanner(QQmlTypeCompiler *typeCompiler)
: QQmlCompilePass(typeCompiler)
, qmlObjects(typeCompiler->qmlObjects())
, propertyCaches(typeCompiler->propertyCaches())
, _seenObjectWithId(false)
{
}
bool QQmlDeferredBindingScanner::scanObject()
{
return scanObject(compiler->rootObjectIndex());
}
bool QQmlDeferredBindingScanner::scanObject(int objectIndex)
{
QmlIR::Object *obj = qmlObjects->at(objectIndex);
if (obj->idNameIndex != 0)
_seenObjectWithId = true;
if (obj->flags & QV4::CompiledData::Object::IsComponent) {
Q_ASSERT(obj->bindingCount() == 1);
const QV4::CompiledData::Binding *componentBinding = obj->firstBinding();
Q_ASSERT(componentBinding->type == QV4::CompiledData::Binding::Type_Object);
return scanObject(componentBinding->value.objectIndex);
}
QQmlPropertyCache *propertyCache = propertyCaches.at(objectIndex).data();
if (!propertyCache)
return true;
QString defaultPropertyName;
QQmlPropertyData *defaultProperty = 0;
if (obj->indexOfDefaultPropertyOrAlias != -1) {
QQmlPropertyCache *cache = propertyCache->parent();
defaultPropertyName = cache->defaultPropertyName();
defaultProperty = cache->defaultProperty();
} else {
defaultPropertyName = propertyCache->defaultPropertyName();
defaultProperty = propertyCache->defaultProperty();
}
QmlIR::PropertyResolver propertyResolver(propertyCache);
QStringList deferredPropertyNames;
{
const QMetaObject *mo = propertyCache->firstCppMetaObject();
const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
if (namesIndex != -1) {
QMetaClassInfo classInfo = mo->classInfo(namesIndex);
deferredPropertyNames = QString::fromUtf8(classInfo.value()).split(QLatin1Char(','));
}
}
for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression
|| binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject)
continue;
QQmlPropertyData *pd = 0;
QString name = stringAt(binding->propertyNameIndex);
if (name.isEmpty()) {
pd = defaultProperty;
name = defaultPropertyName;
} else {
if (name.constData()->isUpper())
continue;
bool notInRevision = false;
pd = propertyResolver.property(name, &notInRevision, QmlIR::PropertyResolver::CheckRevision);
}
if (!pd)
continue;
bool seenSubObjectWithId = false;
if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
qSwap(_seenObjectWithId, seenSubObjectWithId);
const bool subObjectValid = scanObject(binding->value.objectIndex);
qSwap(_seenObjectWithId, seenSubObjectWithId);
if (!subObjectValid)
return false;
_seenObjectWithId |= seenSubObjectWithId;
}
if (!seenSubObjectWithId
&& !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
binding->flags |= QV4::CompiledData::Binding::IsDeferredBinding;
obj->flags |= QV4::CompiledData::Object::HasDeferredBindings;
}
}
return true;
}
QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler)
: QQmlCompilePass(typeCompiler)
, enginePrivate(typeCompiler->enginePrivate())
@ -1669,7 +1767,6 @@ QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler)
, customParsers(typeCompiler->customParserCache())
, propertyCaches(typeCompiler->propertyCaches())
, customParserBindingsPerObject(typeCompiler->customParserBindings())
, _seenObjectWithId(false)
{
}
@ -1678,7 +1775,6 @@ bool QQmlPropertyValidator::validate()
_bindingPropertyDataPerObject.resize(qmlUnit->nObjects);
if (!validateObject(qmlUnit->indexOfRootObject, /*instantiatingBinding*/0))
return false;
compiler->setDeferredBindingsPerObject(_deferredBindingsPerObject);
compiler->setBindingPropertyDataPerObject(_bindingPropertyDataPerObject);
return true;
}
@ -1709,8 +1805,6 @@ struct BindingFinder
bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
{
const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex);
if (obj->idNameIndex != 0)
_seenObjectWithId = true;
if (obj->flags & QV4::CompiledData::Object::IsComponent) {
Q_ASSERT(obj->nBindings == 1);
@ -1757,7 +1851,6 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD
}
QBitArray customParserBindings(obj->nBindings);
QBitArray deferredBindings;
QmlIR::PropertyResolver propertyResolver(propertyCache);
@ -1837,24 +1930,10 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD
return false;
}
bool seenSubObjectWithId = false;
if (binding->type >= QV4::CompiledData::Binding::Type_Object && (pd || binding->isAttachedProperty())) {
qSwap(_seenObjectWithId, seenSubObjectWithId);
const bool subObjectValid = validateObject(binding->value.objectIndex, binding, pd && QQmlValueTypeFactory::metaObjectForMetaType(pd->propType));
qSwap(_seenObjectWithId, seenSubObjectWithId);
if (!subObjectValid)
return false;
_seenObjectWithId |= seenSubObjectWithId;
}
if (!seenSubObjectWithId
&& !deferredPropertyNames.isEmpty() && deferredPropertyNames.contains(name)) {
if (deferredBindings.isEmpty())
deferredBindings.resize(obj->nBindings);
deferredBindings.setBit(i);
}
// Signal handlers were resolved and checked earlier in the signal handler conversion pass.
@ -1973,9 +2052,6 @@ bool QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledD
}
}
if (!deferredBindings.isEmpty())
_deferredBindingsPerObject.insert(objectIndex, deferredBindings);
_bindingPropertyDataPerObject[objectIndex] = collectedBindingPropertyData;
return true;

View File

@ -107,7 +107,6 @@ public:
QQmlJS::MemoryPool *memoryPool();
QStringRef newStringRef(const QString &string);
const QV4::Compiler::StringTableGenerator *stringPool() const;
void setDeferredBindingsPerObject(const QHash<int, QBitArray> &deferredBindingsPerObject);
void setBindingPropertyDataPerObject(const QVector<QV4::CompiledData::BindingPropertyData> &propertyData);
const QHash<int, QQmlCustomParser*> &customParserCache() const { return customParsers; }
@ -281,6 +280,22 @@ protected:
QQmlPropertyCacheVector propertyCaches;
};
class QQmlDeferredBindingScanner : public QQmlCompilePass
{
public:
QQmlDeferredBindingScanner(QQmlTypeCompiler *typeCompiler);
bool scanObject();
private:
bool scanObject(int objectIndex);
QList<QmlIR::Object*> *qmlObjects;
QQmlPropertyCacheVector propertyCaches;
bool _seenObjectWithId;
};
class QQmlPropertyValidator : public QQmlCompilePass
{
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
@ -307,8 +322,6 @@ private:
QHash<int, QBitArray> *customParserBindingsPerObject;
// collected state variables, essentially write-only
mutable QHash<int, QBitArray> _deferredBindingsPerObject;
mutable bool _seenObjectWithId;
mutable QVector<QV4::CompiledData::BindingPropertyData> _bindingPropertyDataPerObject;
};

View File

@ -235,7 +235,8 @@ struct Q_QML_PRIVATE_EXPORT Binding
InitializerForReadOnlyDeclaration = 0x8,
IsResolvedEnum = 0x10,
IsListItem = 0x20,
IsBindingToAlias = 0x40
IsBindingToAlias = 0x40,
IsDeferredBinding = 0x80
};
quint32 flags : 16;
@ -394,7 +395,8 @@ struct Object
{
enum Flags {
NoFlag = 0x0,
IsComponent = 0x1 // object was identified to be an explicit or implicit component boundary
IsComponent = 0x1, // object was identified to be an explicit or implicit component boundary
HasDeferredBindings = 0x2 // any of the bindings are deferred
};
// Depending on the use, this may be the type name to instantiate before instantiating this

View File

@ -133,7 +133,6 @@ public:
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
// hash key is object index, value is indicies of bindings covered by custom parser
QHash<int, QBitArray> customParserBindings;
QHash<int, QBitArray> deferredBindingsPerObject; // index is object index
int totalBindingsCount; // Number of bindings used in this type
int totalParserStatusCount; // Number of instantiated types that are QQmlParserStatus subclasses
int totalObjectCount; // Number of objects explicitly instantiated

View File

@ -255,11 +255,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance)
qSwap(_bindingTarget, bindingTarget);
qSwap(_vmeMetaObject, vmeMetaObject);
QBitArray bindingSkipList = compiledData->deferredBindingsPerObject.value(_compiledObjectIndex);
for (int i = 0; i < bindingSkipList.count(); ++i)
bindingSkipList.setBit(i, !bindingSkipList.testBit(i));
setupBindings(bindingSkipList);
setupBindings(/*binding skip list*/QBitArray(), /*applyDeferredBindings=*/true);
qSwap(_vmeMetaObject, vmeMetaObject);
qSwap(_bindingTarget, bindingTarget);
@ -626,7 +622,7 @@ static QQmlType *qmlTypeForObject(QObject *object)
return type;
}
void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip)
void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip, bool applyDeferredBindings)
{
QQmlListProperty<void> savedList;
qSwap(_currentList, savedList);
@ -680,6 +676,14 @@ void QQmlObjectCreator::setupBindings(const QBitArray &bindingsToSkip)
if (static_cast<int>(i) < bindingsToSkip.size() && bindingsToSkip.testBit(i))
continue;
if (binding->flags & QV4::CompiledData::Binding::IsDeferredBinding) {
if (!applyDeferredBindings)
continue;
} else {
if (applyDeferredBindings)
continue;
}
const QQmlPropertyData *property = propertyData.at(i);
if (property && property->isQList()) {
@ -1299,28 +1303,18 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
qSwap(_propertyCache, cache);
qSwap(_vmeMetaObject, vmeMetaObject);
QBitArray bindingSkipList = bindingsToSkip;
{
QHash<int, QBitArray>::ConstIterator deferredBindings = compiledData->deferredBindingsPerObject.constFind(_compiledObjectIndex);
if (deferredBindings != compiledData->deferredBindingsPerObject.constEnd()) {
if (bindingSkipList.isEmpty())
bindingSkipList.resize(deferredBindings->count());
for (int i = 0; i < deferredBindings->count(); ++i)
if (deferredBindings->testBit(i))
bindingSkipList.setBit(i);
QQmlData::DeferredData *deferData = new QQmlData::DeferredData;
deferData->deferredIdx = _compiledObjectIndex;
deferData->compiledData = compiledData;
deferData->compiledData->addref();
deferData->context = context;
_ddata->deferredData = deferData;
}
if (_compiledObject->flags & QV4::CompiledData::Object::HasDeferredBindings) {
QQmlData::DeferredData *deferData = new QQmlData::DeferredData;
deferData->deferredIdx = _compiledObjectIndex;
deferData->compiledData = compiledData;
deferData->compiledData->addref();
deferData->context = context;
_ddata->deferredData = deferData;
}
if (_compiledObject->nFunctions > 0)
setupFunctions();
setupBindings(bindingSkipList);
setupBindings(bindingsToSkip);
qSwap(_vmeMetaObject, vmeMetaObject);
qSwap(_bindingTarget, bindingTarget);

View File

@ -114,7 +114,7 @@ private:
QObject *bindingTarget, const QQmlPropertyData *valueTypeProperty,
const QBitArray &bindingsToSkip = QBitArray());
void setupBindings(const QBitArray &bindingsToSkip);
void setupBindings(const QBitArray &bindingsToSkip, bool applyDeferredBindings = false);
bool setPropertyBinding(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
void setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
void setupFunctions();