Use V4 binding for non-final properties where possible
[profile/ivi/qtdeclarative.git] / src / qml / qml / qqmlcompiler.cpp
index f862586..6f27925 100644 (file)
@@ -515,6 +515,8 @@ void QQmlCompiler::genLiteralAssignment(QQmlScript::Property *prop,
             {
             Instruction::StoreUrl instr;
             QString string = v->value.asString();
+            // Encoded dir-separators defeat QUrl processing - decode them first
+            string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
             QUrl u = string.isEmpty() ? QUrl() : output->url.resolved(QUrl(string));
             instr.propertyIndex = prop->index;
             instr.value = output->indexForUrl(u);
@@ -1155,7 +1157,7 @@ bool QQmlCompiler::buildObject(QQmlScript::Object *obj, const BindingContext &ct
     return true;
 }
 
-void QQmlCompiler::genObject(QQmlScript::Object *obj)
+void QQmlCompiler::genObject(QQmlScript::Object *obj, bool parentToSuper)
 {
     QQmlCompiledData::TypeReference &tr = output->types[obj->type];
     if (tr.type && obj->metatype->metaObject() == &QQmlComponent::staticMetaObject) {
@@ -1173,6 +1175,7 @@ void QQmlCompiler::genObject(QQmlScript::Object *obj)
         create.type = obj->type;
         create.line = obj->location.start.line;
         create.column = obj->location.start.column;
+        create.parentToSuper = parentToSuper;
         output->addInstruction(create);
 
     } else {
@@ -1186,6 +1189,7 @@ void QQmlCompiler::genObject(QQmlScript::Object *obj)
                 create.data = output->indexForByteArray(obj->custom);
             create.type = obj->type;
             create.isRoot = (compileState->root == obj);
+            create.parentToSuper = parentToSuper;
             output->addInstruction(create);
         } else {
             Instruction::CreateQMLObject create;
@@ -1409,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)
@@ -1667,7 +1673,7 @@ bool QQmlCompiler::buildSignal(QQmlScript::Property *prop, QQmlScript::Object *o
         if (prop->value || !prop->values.isOne())
             COMPILE_EXCEPTION(prop, tr("Incorrectly specified signal assignment"));
 
-        prop->index = sig->coreIndex;
+        prop->index = propertyCacheForObject(obj)->methodIndexToSignalIndex(sig->coreIndex);
         prop->core = *sig;
 
         obj->addSignalProperty(prop);
@@ -1975,31 +1981,25 @@ void QQmlCompiler::genPropertyAssignment(QQmlScript::Property *prop,
                  v->type == Value::ValueInterceptor);
 
         if (v->type == Value::ValueSource) {
-            genObject(v->object);
+            genObject(v->object, valueTypeProperty?true:false);
 
             Instruction::StoreValueSource store;
-            if (valueTypeProperty) {
+            if (valueTypeProperty)
                 store.property = genValueTypeData(prop, valueTypeProperty);
-                store.owner = 1;
-            } else {
+            else
                 store.property = prop->core;
-                store.owner = 0;
-            }
             QQmlType *valueType = toQmlType(v->object);
             store.castValue = valueType->propertyValueSourceCast();
             output->addInstruction(store);
 
         } else if (v->type == Value::ValueInterceptor) {
-            genObject(v->object);
+            genObject(v->object, valueTypeProperty?true:false);
 
             Instruction::StoreValueInterceptor store;
-            if (valueTypeProperty) {
+            if (valueTypeProperty)
                 store.property = genValueTypeData(prop, valueTypeProperty);
-                store.owner = 1;
-            } else {
+            else
                 store.property = prop->core;
-                store.owner = 0;
-            }
             QQmlType *valueType = toQmlType(v->object);
             store.castValue = valueType->propertyValueInterceptorCast();
             output->addInstruction(store);
@@ -2108,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"));
+                        }
+                    }
                 }
             }
 
@@ -2119,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;
@@ -2128,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"));
         }
@@ -2484,6 +2507,12 @@ bool QQmlCompiler::buildPropertyLiteralAssignment(QQmlScript::Property *prop,
     return true;
 }
 
+struct StaticQtMetaObject : public QObject
+{
+    static const QMetaObject *get()
+        { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
+};
+
 bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
                                                        QQmlScript::Object *obj,
                                                        QQmlScript::Value *v,
@@ -2515,8 +2544,9 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
 
     if (isIntProp) {
         // Allow enum assignment to ints.
-        int enumval = evaluateEnum(typeName, enumValue.toUtf8());
-        if (enumval != -1) {
+        bool ok;
+        int enumval = evaluateEnum(typeName, enumValue.toUtf8(), &ok);
+        if (ok) {
             v->type = Value::Literal;
             v->value = QQmlScript::Variant((double)enumval);
             *isAssignment = true;
@@ -2527,13 +2557,13 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
     QQmlType *type = 0;
     unit->imports().resolveType(typeName, &type, 0, 0, 0, 0);
 
-    if (!type)
+    if (!type && typeName != QLatin1String("Qt"))
         return true;
 
     int value = 0;
-    bool ok;
+    bool ok = false;
 
-    if (toQmlType(obj) == type) {
+    if (type && toQmlType(obj) == type) {
         // When these two match, we can short cut the search
         if (mprop.isFlagType()) {
             value = mprop.enumerator().keysToValue(enumValue.toUtf8().constData(), &ok);
@@ -2542,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->baseMetaObject();
-        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);
+            }
         }
     }
 
@@ -2562,28 +2594,23 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop,
     return true;
 }
 
-struct StaticQtMetaObject : public QObject
-{
-    static const QMetaObject *get()
-        { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
-};
-
 // Similar logic to above, but not knowing target property.
-int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue) const
+int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray& enumValue, bool *ok) const
 {
-    QQmlType *type = 0;
+    Q_ASSERT_X(ok, "QQmlCompiler::evaluateEnum", "ok must not be a null pointer");
+    *ok = false;
+
     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--) {
-        bool ok;
-        int v = mo->enumerator(i).keyToValue(enumValue.constData(), &ok);
-        if (ok)
+        int v = mo->enumerator(i).keyToValue(enumValue.constData(), ok);
+        if (*ok)
             return v;
     }
     return -1;
@@ -2985,7 +3012,7 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod
 
 
     // Dynamic properties (except var and aliases)
-    effectiveMethodIndex = cache->methodIndexCacheStart;
+    int effectiveSignalIndex = cache->signalHandlerIndexCacheStart;
     for (Object::DynamicProperty *p = obj->dynamicProperties.first(); p;
          p = obj->dynamicProperties.next(p)) {
 
@@ -3052,15 +3079,15 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod
                                            p->name.hash());
             if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
             cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
-                                  propertyType, effectiveMethodIndex);
+                                  propertyType, effectiveSignalIndex);
         } else {
             QString propertyName = p->name.toString();
             if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
             cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
-                                  propertyType, effectiveMethodIndex);
+                                  propertyType, effectiveSignalIndex);
         }
 
-        effectiveMethodIndex++;
+        effectiveSignalIndex++;
 
         VMD *vmd = (QQmlVMEMetaData *)dynamicData.data();
         (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType;
@@ -3088,15 +3115,15 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod
                                            p->name.hash());
             if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
             cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
-                                  QMetaType::QVariant, effectiveMethodIndex);
+                                  QMetaType::QVariant, effectiveSignalIndex);
         } else {
             QString propertyName = p->name.toString();
             if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
             cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
-                                  QMetaType::QVariant, effectiveMethodIndex);
+                                  QMetaType::QVariant, effectiveSignalIndex);
         }
 
-        effectiveMethodIndex++;
+        effectiveSignalIndex++;
     }
 
     // Alias property count.  Actual data is setup in buildDynamicMetaAliases
@@ -3151,7 +3178,7 @@ bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj)
     QQmlPropertyCache *cache = obj->synthCache;
     char *cStringData = cache->_dynamicStringData.data();
 
-    int effectiveMethodIndex = cache->methodIndexCacheStart + cache->propertyIndexCache.count();
+    int effectiveSignalIndex = cache->signalHandlerIndexCacheStart + cache->propertyIndexCache.count();
     int effectivePropertyIndex = cache->propertyIndexCacheStart + cache->propertyIndexCache.count();
     int effectiveAliasIndex = 0;
 
@@ -3268,12 +3295,12 @@ bool QQmlCompiler::buildDynamicMetaAliases(QQmlScript::Object *obj)
                                            p->name.hash());
             if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName.toUtf16();
             cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
-                                  type, effectiveMethodIndex++);
+                                  type, effectiveSignalIndex++);
         } else {
             QString propertyName = p->name.toString();
             if (p->isDefaultProperty) cache->_defaultPropertyName = propertyName;
             cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++,
-                                  type, effectiveMethodIndex++);
+                                  type, effectiveSignalIndex++);
         }
     }
 
@@ -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);
 
@@ -3816,7 +3870,7 @@ QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, b
     if (d && !cache->isAllowedInRevision(d)) {
         if (notInRevision) *notInRevision = true;
         return 0;
-    } else if (d) {
+    } else if (d && d->isSignal()) {
         return d;
     }
 
@@ -3825,7 +3879,7 @@ QQmlCompiler::signal(QQmlScript::Object *object, const QHashedStringRef &name, b
 
         d = property(object, propName, notInRevision);
         if (d) 
-            return cache->method(d->notifyIndex);
+            return cache->signal(d->notifyIndex);
     }
 
     return 0;