Use V4 binding for non-final properties where possible
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlcompiler.cpp
index 13ff9e8..6f27925 100644 (file)
@@ -1413,6 +1413,8 @@ void QQmlCompiler::genValueTypeProperty(QQmlScript::Object *obj,QQmlScript::Prop
     pop.type = prop->type;
     pop.bindingSkipList = 0;
     output->addInstruction(pop);
+
+    genPropertyAssignment(prop, obj);
 }
 
 void QQmlCompiler::genComponent(QQmlScript::Object *obj)
@@ -2106,10 +2108,15 @@ bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop,
         if (prop->type >= 0 && enginePrivate->valueTypes[prop->type]) {
 
             if (!prop->values.isEmpty()) {
-                if (prop->values.first()->location < prop->value->location) {
-                    COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value"));
-                } else {
-                    COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value"));
+                // Only error if we are assigning values, and not e.g. a property interceptor
+                for (Property *dotProp = prop->value->properties.first(); dotProp; dotProp = prop->value->properties.next(dotProp)) {
+                    if (!dotProp->values.isEmpty()) {
+                        if (prop->values.first()->location < prop->value->location) {
+                            COMPILE_EXCEPTION(prop->value, tr( "Property has already been assigned a value"));
+                        } else {
+                            COMPILE_EXCEPTION(prop->values.first(), tr( "Property has already been assigned a value"));
+                        }
+                    }
                 }
             }
 
@@ -2117,7 +2124,6 @@ bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop,
                 COMPILE_EXCEPTION(prop, tr( "Invalid property assignment: \"%1\" is a read-only property").arg(prop->name().toString()));
             }
 
-
             if (prop->isAlias) {
                 for (Property *vtProp = prop->value->properties.first(); vtProp; vtProp = prop->value->properties.next(vtProp)) {
                     vtProp->isAlias = true;
@@ -2126,7 +2132,26 @@ bool QQmlCompiler::buildGroupedProperty(QQmlScript::Property *prop,
 
             COMPILE_CHECK(buildValueTypeProperty(enginePrivate->valueTypes[prop->type],
                                                  prop->value, obj, ctxt.incr()));
+
+            // When building a value type where sub components are declared, this
+            // code path is followed from buildProperty, even if there is a previous
+            // assignment to the value type as a whole. Therefore we need to look
+            // for (and build) assignments to the entire value type before looking
+            // for any onValue assignments.
+            for (Value *v = prop->values.first(); v; v = Property::ValueList::next(v)) {
+                if (v->object) {
+                    COMPILE_EXCEPTION(v->object, tr("Objects cannot be assigned to value types"));
+                }
+                COMPILE_CHECK(buildPropertyLiteralAssignment(prop, obj, v, ctxt));
+            }
+
+            for (Value *v = prop->onValues.first(); v; v = Property::ValueList::next(v)) {
+                Q_ASSERT(v->object);
+                COMPILE_CHECK(buildPropertyOnAssignment(prop, obj, obj, v, ctxt));
+            }
+
             obj->addValueTypeProperty(prop);
+
         } else {
             COMPILE_EXCEPTION(prop, tr("Invalid grouped property access"));
         }
@@ -2536,7 +2561,7 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
         return true;
 
     int value = 0;
-    bool ok;
+    bool ok = false;
 
     if (type && toQmlType(obj) == type) {
         // When these two match, we can short cut the search
@@ -2547,13 +2572,15 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
         }
     } else {
         // Otherwise we have to search the whole type
-        // This matches the logic in QV8TypeWrapper
-        QByteArray enumName = enumValue.toUtf8();
-        const QMetaObject *metaObject = type ? type->baseMetaObject() : StaticQtMetaObject::get();
-        ok = false;
-        for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
-            QMetaEnum e = metaObject->enumerator(ii);
-            value = e.keyToValue(enumName.constData(), &ok);
+        if (type) {
+            value = type->enumValue(QHashedStringRef(enumValue), &ok);
+        } else {
+            QByteArray enumName = enumValue.toUtf8();
+            const QMetaObject *metaObject = StaticQtMetaObject::get();
+            for (int ii = metaObject->enumeratorCount() - 1; !ok && ii >= 0; --ii) {
+                QMetaEnum e = metaObject->enumerator(ii);
+                value = e.keyToValue(enumName.constData(), &ok);
+            }
         }
     }
 
@@ -2572,14 +2599,14 @@ int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray&
 {
     Q_ASSERT_X(ok, "QQmlCompiler::evaluateEnum", "ok must not be a null pointer");
     *ok = false;
-    QQmlType *type = 0;
+
     if (scope != QLatin1String("Qt")) {
+        QQmlType *type = 0;
         unit->imports().resolveType(scope, &type, 0, 0, 0, 0);
-        if (!type)
-            return -1;
-
+        return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1;
     }
-    const QMetaObject *mo = type ? type->metaObject() : StaticQtMetaObject::get();
+
+    const QMetaObject *mo = StaticQtMetaObject::get();
     int i = mo->enumeratorCount();
     while (i--) {
         int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok);
@@ -3431,6 +3458,7 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding,
 
         Instruction::StoreV4Binding store;
         store.value = js.compiledIndex;
+        store.fallbackValue = js.sharedIndex;
         store.context = js.bindingContext.stack;
         store.owner = js.bindingContext.owner;
         store.isAlias = prop->isAlias;
@@ -3446,11 +3474,18 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding,
         store.line = binding->location.start.line;
         store.column = binding->location.start.column;
         output->addInstruction(store);
+
+        if (store.fallbackValue > -1) {
+            //also create v8 instruction (needed to properly configure the fallback v8 binding)
+            JSBindingReference &js = static_cast<JSBindingReference &>(*binding->bindingReference);
+            js.dataType = BindingReference::V8;
+            genBindingAssignment(binding, prop, obj, valueTypeProperty);
+        }
     } else if (ref.dataType == BindingReference::V8) {
         const JSBindingReference &js = static_cast<const JSBindingReference &>(ref);
 
         Instruction::StoreV8Binding store;
-        store.value = js.compiledIndex;
+        store.value = js.sharedIndex;
         store.context = js.bindingContext.stack;
         store.owner = js.bindingContext.owner;
         store.isAlias = prop->isAlias;
@@ -3459,6 +3494,7 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding,
         } else {
             store.isRoot = (compileState->root == obj);
         }
+        store.isFallback = js.compiledIndex > -1;
         store.line = binding->location.start.line;
         store.column = binding->location.start.column;
 
@@ -3487,6 +3523,7 @@ void QQmlCompiler::genBindingAssignment(QQmlScript::Value *binding,
         } else {
             store.isRoot = (compileState->root == obj);
         }
+        store.isFallback = false;
 
         Q_ASSERT(js.bindingContext.owner == 0 ||
                  (js.bindingContext.owner != 0 && valueTypeProperty));
@@ -3552,13 +3589,22 @@ bool QQmlCompiler::completeComponentBuild()
         expr.property = binding.property;
         expr.expression = binding.expression;
 
-        int index = bindingCompiler.compile(expr, enginePrivate);
+        bool needsFallback = false;
+        int index = bindingCompiler.compile(expr, enginePrivate, &needsFallback);
         if (index != -1) {
+            // Ensure the index value fits within the available space
+            Q_ASSERT(index < (1 << 15));
+
             binding.dataType = BindingReference::V4;
             binding.compiledIndex = index;
+            binding.sharedIndex = -1;
             if (componentStats)
                 componentStats->componentStat.optimizedBindings.append(b->value->location);
-            continue;
+
+            if (!needsFallback)
+                continue;
+
+            // Drop through. We need to create a V8 binding in case the V4 binding is invalidated
         }
 
         // Pre-rewrite the expression
@@ -3570,12 +3616,17 @@ bool QQmlCompiler::completeComponentBuild()
         binding.rewrittenExpression = rewriteBinding(binding.expression.asAST(), expression, &isSharable);
 
         if (isSharable && binding.property->type != qMetaTypeId<QQmlBinding*>()) {
-            binding.dataType = BindingReference::V8;
             sharedBindings.append(b);
 
-            if (componentStats)
-                componentStats->componentStat.sharedBindings.append(b->value->location);
+            if (!needsFallback) {
+                binding.dataType = BindingReference::V8;
+                binding.compiledIndex = -1;
+
+                if (componentStats)
+                    componentStats->componentStat.sharedBindings.append(b->value->location);
+            }
         } else {
+            Q_ASSERT(!needsFallback);
             binding.dataType = BindingReference::QtScript;
 
             if (componentStats)
@@ -3612,7 +3663,10 @@ bool QQmlCompiler::completeComponentBuild()
 
             functionArray += expression.toUtf8();
             lineNumber += expression.count(QLatin1Char('\n'));
-            reference->compiledIndex = ii;
+
+            // Ensure the index value fits within the available space
+            Q_ASSERT(ii < (1 << 15));
+            reference->sharedIndex = ii;
         }
         functionArray.append("]", 1);