[new compiler] Add support for QML list models
authorSimon Hausmann <simon.hausmann@digia.com>
Wed, 15 Jan 2014 16:29:04 +0000 (17:29 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 20 Jan 2014 14:10:00 +0000 (15:10 +0100)
List model definitions make heavy use of custom parsers, which requires AST
access as well as a general port to the new QQmlCustomParser API.

Additional fixes in the custom parser support were needed to pass all tests:

 * Fix support for AcceptsSignalHandlers and AcceptsAttachedProperties
 * Don't call setCustomData unless the compiler generated data earlier

Change-Id: Ic42f8a890391267c94f63d35f055b60fdbf3c83d
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
13 files changed:
src/qml/compiler/qqmlcodegenerator.cpp
src/qml/compiler/qqmlcodegenerator_p.h
src/qml/compiler/qqmltypecompiler.cpp
src/qml/compiler/qqmltypecompiler_p.h
src/qml/qml/qqmlcompiler.cpp
src/qml/qml/qqmlcompiler_p.h
src/qml/qml/qqmlcustomparser.cpp
src/qml/qml/qqmlcustomparser_p.h
src/qml/qml/qqmlobjectcreator.cpp
src/qml/types/qqmllistmodel.cpp
src/qml/types/qqmllistmodel_p.h
tests/auto/qml/qqmllanguage/testtypes.cpp
tests/auto/qml/qqmllanguage/testtypes.h

index 46bb07f..6296713 100644 (file)
@@ -1640,7 +1640,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
         if (elementName.isEmpty())
             continue;
         QQmlCompiledData::TypeReference *tr = unit->resolvedTypes.value(obj->inheritedTypeNameIndex);
-        if (tr && tr->type && tr->type->customParser())
+        QQmlCustomParser *customParser = (tr && tr->type) ? tr->type->customParser() : 0;
+        if (customParser && !(customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers))
             continue;
         QQmlPropertyCache *cache = unit->propertyCaches.value(objectIndex);
         Q_ASSERT(cache);
index f1050f4..0b70701 100644 (file)
@@ -173,7 +173,8 @@ struct Pragma
 struct CompiledFunctionOrExpression
 {
     CompiledFunctionOrExpression()
-        : disableAcceleratedLookups(false)
+        : node(0)
+        , disableAcceleratedLookups(false)
     {}
     CompiledFunctionOrExpression(AST::Node *n)
         : node(n)
index a684ebd..00fc9c5 100644 (file)
@@ -201,7 +201,7 @@ bool QQmlTypeCompiler::compile()
     }
 
     // Sanity check property bindings
-    QQmlPropertyValidator validator(this);
+    QQmlPropertyValidator validator(this, runtimeFunctionIndices);
     if (!validator.validate())
         return false;
 
@@ -295,6 +295,16 @@ MemoryPool *QQmlTypeCompiler::memoryPool()
     return parsedQML->jsParserEngine.pool();
 }
 
+const QList<CompiledFunctionOrExpression> &QQmlTypeCompiler::functions() const
+{
+    return parsedQML->functions;
+}
+
+void QQmlTypeCompiler::setCustomParserBindings(const QVector<int> &bindings)
+{
+    compiledData->customParserBindings = bindings;
+}
+
 QQmlCompilePass::QQmlCompilePass(QQmlTypeCompiler *typeCompiler)
     : compiler(typeCompiler)
 {
@@ -1113,19 +1123,23 @@ bool QQmlComponentAndAliasResolver::resolveAliases()
 }
 
 
-QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler)
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QVector<int> &runtimeFunctionIndices)
     : QQmlCompilePass(typeCompiler)
     , qmlUnit(typeCompiler->qmlUnit())
     , resolvedTypes(*typeCompiler->resolvedTypes())
     , propertyCaches(typeCompiler->propertyCaches())
     , objectIndexToIdPerComponent(*typeCompiler->objectIndexToIdPerComponent())
     , customParserData(typeCompiler->customParserData())
+    , runtimeFunctionIndices(runtimeFunctionIndices)
 {
 }
 
 bool QQmlPropertyValidator::validate()
 {
-    return validateObject(qmlUnit->indexOfRootObject);
+    if (!validateObject(qmlUnit->indexOfRootObject))
+        return false;
+    compiler->setCustomParserBindings(customParserBindings);
+    return true;
 }
 
 const QQmlImports &QQmlPropertyValidator::imports() const
@@ -1133,6 +1147,22 @@ const QQmlImports &QQmlPropertyValidator::imports() const
     return *compiler->imports();
 }
 
+AST::Node *QQmlPropertyValidator::astForBinding(int scriptIndex) const
+{
+    // ####
+    int reverseIndex = runtimeFunctionIndices.indexOf(scriptIndex);
+    if (reverseIndex == -1)
+        return 0;
+    return compiler->functions().value(reverseIndex).node;
+}
+
+QQmlBinding::Identifier QQmlPropertyValidator::bindingIdentifier(const QV4::CompiledData::Binding *binding, QQmlCustomParser *)
+{
+    int id = customParserBindings.count();
+    customParserBindings.append(binding->value.compiledScriptIndex);
+    return id;
+}
+
 bool QQmlPropertyValidator::validateObject(int objectIndex)
 {
     const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex);
@@ -1158,16 +1188,30 @@ bool QQmlPropertyValidator::validateObject(int objectIndex)
 
     const QV4::CompiledData::Binding *binding = obj->bindingTable();
     for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
-        if (binding->type >= QV4::CompiledData::Binding::Type_Object && !customParser) {
-            if (!validateObject(binding->value.objectIndex))
-                return false;
-        }
 
-        if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty
-            || binding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
-            if (customParser)
+        if (customParser) {
+            if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
+                if (customParser->flags() & QQmlCustomParser::AcceptsAttachedProperties) {
+                    customBindings << binding;
+                    continue;
+                }
+            } else if ((binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)
+                       && (!customParser->flags() & QQmlCustomParser::AcceptsSignalHandlers)) {
                 customBindings << binding;
-            continue;
+                continue;
+            } else if (binding->type == QV4::CompiledData::Binding::Type_Object
+                       || binding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
+                customBindings << binding;
+                continue;
+            }
+        }
+
+        if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+            if (!validateObject(binding->value.objectIndex))
+                return false;
+            // Nothing further to check for attached properties.
+            if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty)
+                continue;
         }
 
         const QString name = stringAt(binding->propertyNameIndex);
index e6a34fa..4340279 100644 (file)
@@ -92,6 +92,8 @@ struct QQmlTypeCompiler
     QHash<int, QHash<int, int> > *objectIndexToIdPerComponent();
     QHash<int, QByteArray> *customParserData();
     QQmlJS::MemoryPool *memoryPool();
+    const QList<CompiledFunctionOrExpression> &functions() const;
+    void setCustomParserBindings(const QVector<int> &bindings);
 
 private:
     QList<QQmlError> errors;
@@ -176,13 +178,14 @@ class QQmlPropertyValidator : public QQmlCompilePass, public QQmlCustomParserCom
 {
     Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
 public:
-    QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler);
+    QQmlPropertyValidator(QQmlTypeCompiler *typeCompiler, const QVector<int> &runtimeFunctionIndices);
 
     bool validate();
 
     // Re-implemented for QQmlCustomParser
     virtual const QQmlImports &imports() const;
-
+    virtual QQmlJS::AST::Node *astForBinding(int scriptIndex) const;
+    virtual QQmlBinding::Identifier bindingIdentifier(const QV4::CompiledData::Binding *binding, QQmlCustomParser *parser);
 
 private:
     bool validateObject(int objectIndex);
@@ -194,6 +197,8 @@ private:
     const QVector<QQmlPropertyCache *> &propertyCaches;
     const QHash<int, QHash<int, int> > objectIndexToIdPerComponent;
     QHash<int, QByteArray> *customParserData;
+    QVector<int> customParserBindings;
+    const QVector<int> &runtimeFunctionIndices;
 };
 
 QT_END_NAMESPACE
index 069345e..a0c1bbc 100644 (file)
@@ -2665,30 +2665,6 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
     return true;
 }
 
-int QQmlCompiler::bindingIdentifier(const QString &name, const Variant &value, const BindingContext &ctxt)
-{
-    JSBindingReference *reference = pool->New<JSBindingReference>();
-    reference->expression = value;
-    reference->property = pool->New<Property>();
-    reference->property->setName(name);
-    reference->value = 0;
-    reference->bindingContext = ctxt;
-    reference->bindingContext.owner++;
-    // Unfortunately this is required for example for PropertyChanges where the bindings
-    // will be executed in the dynamic scope of the target, so we can't resolve any lookups
-    // at run-time.
-    reference->disableLookupAcceleration = true;
-
-    const int id = output->customParserBindings.count();
-    output->customParserBindings.append(0); // Filled in later.
-    reference->customParserBindingsIndex = id;
-
-    compileState->totalBindingsCount++;
-    compileState->bindings.prepend(reference);
-
-    return id;
-}
-
 QQmlBinding::Identifier QQmlCompiler::bindingIdentifier(const Variant &value, const QString &name, QQmlCustomParser *customParser)
 {
     JSBindingReference *reference = pool->New<JSBindingReference>();
index 58943b2..0feebed 100644 (file)
@@ -349,7 +349,6 @@ public:
     static bool isAttachedPropertyName(const QHashedStringRef &);
     static bool isSignalPropertyName(const QHashedStringRef &);
 
-    int bindingIdentifier(const QString &name, const QQmlScript::Variant& value, const QQmlCompilerTypes::BindingContext &ctxt); // for QQmlCustomParser::bindingIndex
     virtual QQmlBinding::Identifier bindingIdentifier(const QQmlScript::Variant&value, const QString&name, QQmlCustomParser *customParser);
 
     virtual const QQmlImports &imports() const { return unit->imports(); }
index ca23451..90bd2fd 100644 (file)
@@ -230,13 +230,6 @@ void QQmlCustomParser::clearErrors()
     exceptions.clear();
 }
 
-QByteArray QQmlCustomParser::compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
-{
-    Q_UNUSED(qmlUnit)
-    Q_UNUSED(bindings)
-    return QByteArray();
-}
-
 /*!
     Reports an error with the given \a description.
 
@@ -349,6 +342,16 @@ QQmlBinding::Identifier QQmlCustomParser::bindingIdentifier(const QQmlScript::Va
     return compiler->bindingIdentifier(value, name, this);
 }
 
+QQmlBinding::Identifier QQmlCustomParser::bindingIdentifier(const QV4::CompiledData::Binding *binding)
+{
+    return compiler->bindingIdentifier(binding, this);
+}
+
+AST::Node *QQmlCustomParser::astForBinding(int scriptIndex) const
+{
+    return compiler->astForBinding(scriptIndex);
+}
+
 struct StaticQtMetaObject : public QObject
 {
     static const QMetaObject *get()
index 2f1a9dd..b4d716b 100644 (file)
@@ -117,6 +117,9 @@ struct QQmlCustomParserCompilerBackend
     const QMetaObject *resolveType(const QString& name) const;
 
     virtual QQmlBinding::Identifier bindingIdentifier(const QQmlScript::Variant&, const QString&, QQmlCustomParser *) { return QQmlBinding::Invalid; }
+    virtual QQmlBinding::Identifier bindingIdentifier(const QV4::CompiledData::Binding *, QQmlCustomParser *) { return QQmlBinding::Invalid; }
+
+    virtual QQmlJS::AST::Node *astForBinding(int) const { return 0; }
 };
 
 class Q_QML_PRIVATE_EXPORT QQmlCustomParser
@@ -137,7 +140,7 @@ public:
     Flags flags() const { return m_flags; }
 
     virtual QByteArray compile(const QList<QQmlCustomParserProperty> &)=0;
-    virtual QByteArray compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings); // ### make pure virtual
+    virtual QByteArray compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) = 0;
     virtual void setCustomData(QObject *, const QByteArray &)=0;
 
     QList<QQmlError> errors() const { return exceptions; }
@@ -154,6 +157,9 @@ protected:
     const QMetaObject *resolveType(const QString&) const;
 
     QQmlBinding::Identifier bindingIdentifier(const QQmlScript::Variant&, const QString&);
+    QQmlBinding::Identifier bindingIdentifier(const QV4::CompiledData::Binding *binding);
+
+    QQmlJS::AST::Node *astForBinding(int scriptIndex) const;
 
 private:
     QList<QQmlError> exceptions;
index b1f4a8e..2cacb0e 100644 (file)
@@ -997,8 +997,9 @@ QObject *QmlObjectCreator::createInstance(int index, QObject *parent)
         context->setIdProperty(idEntry.value(), instance);
 
     if (customParser) {
-        QByteArray data = compiledData->customParserData.value(index);
-        customParser->setCustomData(instance, data);
+        QHash<int, QByteArray>::ConstIterator entry = compiledData->customParserData.find(index);
+        if (entry != compiledData->customParserData.constEnd())
+            customParser->setCustomData(instance, *entry);
     }
 
     if (isComponent)
index eeb4aa0..03dfb66 100644 (file)
@@ -2384,6 +2384,136 @@ bool QQmlListModelParser::compileProperty(const QQmlCustomParserProperty &prop,
     return true;
 }
 
+bool QQmlListModelParser::compileProperty(const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding, QList<QQmlListModelParser::ListInstruction> &instr, QByteArray &data)
+{
+    if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
+        const QV4::CompiledData::Object *target = qmlUnit->objectAt(binding->value.objectIndex);
+        QString objName = qmlUnit->header.stringAt(target->inheritedTypeNameIndex);
+        if (objName != listElementTypeName) {
+            const QMetaObject *mo = resolveType(objName);
+            if (mo != &QQmlListElement::staticMetaObject) {
+                error(target, QQmlListModel::tr("ListElement: cannot contain nested elements"));
+                return false;
+            }
+            listElementTypeName = objName; // cache right name for next time
+        }
+
+        {
+            ListInstruction li;
+            li.type = ListInstruction::Push;
+            li.dataIdx = -1;
+            instr << li;
+        }
+
+        if (!qmlUnit->header.stringAt(target->idIndex).isEmpty()) {
+            error(binding, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property"));
+            return false;
+        }
+
+        const QV4::CompiledData::Binding *binding = target->bindingTable();
+        for (quint32 i = 0; i < target->nBindings; ++i, ++binding) {
+            QString propName = qmlUnit->header.stringAt(binding->propertyNameIndex);
+            if (propName.isEmpty()) {
+                error(binding, QQmlListModel::tr("ListElement: cannot contain nested elements"));
+                return false;
+            }
+            ListInstruction li;
+            int ref = data.count();
+            data.append(propName.toUtf8());
+            data.append('\0');
+            li.type = ListInstruction::Set;
+            li.dataIdx = ref;
+            instr << li;
+
+            if (!compileProperty(qmlUnit, binding, instr, data))
+                return false;
+
+            li.type = ListInstruction::Pop;
+            li.dataIdx = -1;
+            instr << li;
+        }
+
+        {
+            ListInstruction li;
+            li.type = ListInstruction::Pop;
+            li.dataIdx = -1;
+            instr << li;
+        }
+
+    } else {
+        int ref = data.count();
+
+        QByteArray d;
+
+        if (binding->type == QV4::CompiledData::Binding::Type_String) {
+            d += char(QQmlScript::Variant::String);
+            d += binding->valueAsString(&qmlUnit->header).toUtf8();
+        } else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
+            d += char(QQmlScript::Variant::Number);
+            d += QByteArray::number(binding->valueAsNumber(),'g',20);
+        } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
+            d += char(QQmlScript::Variant::Boolean);
+            d += char(binding->valueAsBoolean());
+        } else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
+            QString scriptStr = binding->valueAsScriptString(&qmlUnit->header);
+            if (definesEmptyList(scriptStr)) {
+                d[0] = char(QQmlScript::Variant::Invalid); // marks empty list
+            } else {
+                QByteArray script = scriptStr.toUtf8();
+                bool ok;
+                int v = evaluateEnum(script, &ok);
+                if (!ok) {
+                    using namespace QQmlJS;
+                    AST::Node *node = astForBinding(binding->value.compiledScriptIndex);
+                    if (AST::ExpressionStatement *stmt = AST::cast<AST::ExpressionStatement*>(node))
+                        node = stmt->expression;
+                    AST::StringLiteral *literal = 0;
+                    if (AST::CallExpression *callExpr = AST::cast<AST::CallExpression *>(node)) {
+                        if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(callExpr->base)) {
+                            if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) {
+                                if (callExpr->arguments && !callExpr->arguments->next)
+                                    literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->expression);
+                                if (!literal) {
+                                    error(binding, QQmlListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString()));
+                                    return false;
+                                }
+                            } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) {
+                                if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next)
+                                    literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->next->expression);
+                                if (!literal) {
+                                    error(binding, QQmlListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP"));
+                                    return false;
+                                }
+                            }
+                        }
+                    }
+
+                    if (literal) {
+                        d[0] = char(QQmlScript::Variant::String);
+                        d += literal->value.toUtf8();
+                    } else {
+                        error(binding, QQmlListModel::tr("ListElement: cannot use script for property value"));
+                        return false;
+                    }
+                } else {
+                    d[0] = char(QQmlScript::Variant::Number);
+                    d += QByteArray::number(v);
+                }
+            }
+        }
+
+        d.append('\0');
+        data.append(d);
+
+        ListInstruction li;
+        li.type = ListInstruction::Value;
+        li.dataIdx = ref;
+        instr << li;
+    }
+
+    return true;
+}
+
 QByteArray QQmlListModelParser::compile(const QList<QQmlCustomParserProperty> &customProps)
 {
     QList<ListInstruction> instr;
@@ -2420,6 +2550,40 @@ QByteArray QQmlListModelParser::compile(const QList<QQmlCustomParserProperty> &c
     return rv;
 }
 
+QByteArray QQmlListModelParser::compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+{
+    QList<ListInstruction> instr;
+    QByteArray data;
+    listElementTypeName = QString(); // unknown
+
+    foreach (const QV4::CompiledData::Binding *binding, bindings) {
+        QString propName = qmlUnit->header.stringAt(binding->propertyNameIndex);
+        if (!propName.isEmpty()) { // isn't default property
+            error(binding, QQmlListModel::tr("ListModel: undefined property '%1'").arg(propName));
+            return QByteArray();
+        }
+        if (!compileProperty(qmlUnit, binding, instr, data))
+            return QByteArray();
+    }
+
+    int size = sizeof(ListModelData) +
+               instr.count() * sizeof(ListInstruction) +
+               data.count();
+
+    QByteArray rv;
+    rv.resize(size);
+
+    ListModelData *lmd = (ListModelData *)rv.data();
+    lmd->dataOffset = sizeof(ListModelData) +
+                     instr.count() * sizeof(ListInstruction);
+    lmd->instrCount = instr.count();
+    for (int ii = 0; ii < instr.count(); ++ii)
+        lmd->instructions()[ii] = instr.at(ii);
+    ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());
+
+    return rv;
+}
+
 void QQmlListModelParser::setCustomData(QObject *obj, const QByteArray &d)
 {
     QQmlListModel *rv = static_cast<QQmlListModel *>(obj);
index a748753..172d857 100644 (file)
@@ -160,6 +160,7 @@ class QQmlListModelParser : public QQmlCustomParser
 public:
     QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {}
     QByteArray compile(const QList<QQmlCustomParserProperty> &);
+    QByteArray compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings);
     void setCustomData(QObject *, const QByteArray &);
 
 private:
@@ -175,6 +176,7 @@ private:
         ListInstruction *instructions() const;
     };
     bool compileProperty(const QQmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data);
+    bool compileProperty(const QV4::CompiledData::QmlUnit *qmlUnit, const QV4::CompiledData::Binding *binding, QList<ListInstruction> &instr, QByteArray &data);
 
     bool definesEmptyList(const QString &);
 
index 4a4ab3b..98a803a 100644 (file)
@@ -126,6 +126,26 @@ QByteArray CustomBindingParser::compile(const QList<QQmlCustomParserProperty> &p
     return result;
 }
 
+QByteArray CustomBindingParser::compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+{
+    QByteArray result;
+    QDataStream ds(&result, QIODevice::WriteOnly);
+
+    ds << bindings.count();
+    for (int i = 0; i < bindings.count(); ++i) {
+        const QV4::CompiledData::Binding *binding = bindings.at(i);
+        ds << qmlUnit->header.stringAt(binding->propertyNameIndex);
+
+        Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
+        int bindingId = bindingIdentifier(binding);
+        ds << bindingId;
+
+        ds << binding->location.line;
+    }
+
+    return result;
+}
+
 void CustomBindingParser::setCustomData(QObject *object, const QByteArray &data)
 {
     CustomBinding *customBinding = qobject_cast<CustomBinding*>(object);
index a968d9a..1b51042 100644 (file)
@@ -719,6 +719,7 @@ class MyCustomParserTypeParser : public QQmlCustomParser
 {
 public:
     QByteArray compile(const QList<QQmlCustomParserProperty> &) { return QByteArray(); }
+    QByteArray compile(const QV4::CompiledData::QmlUnit *, const QList<const QV4::CompiledData::Binding *> &) { return QByteArray(); }
     void setCustomData(QObject *, const QByteArray &) {}
 };
 
@@ -1090,6 +1091,7 @@ public:
 class CustomBindingParser : public QQmlCustomParser
 {
     virtual QByteArray compile(const QList<QQmlCustomParserProperty> &properties);
+    virtual QByteArray compile(const QV4::CompiledData::QmlUnit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings);
     virtual void setCustomData(QObject *object, const QByteArray &data);
 };