Пример #1
0
void QQmlBind::eval()
{
    Q_D(QQmlBind);
    if (!d->prop.isValid() || d->value.isNull || !d->componentComplete)
        return;

    if (d->when.isValid()) {
        if (!d->when) {
            //restore any previous binding
            if (d->prevBind) {
                QQmlAbstractBinding *tmp = d->prevBind;
                d->prevBind = 0;
                tmp = QQmlPropertyPrivate::setBinding(d->prop, tmp);
                if (tmp) //should this ever be true?
                    tmp->destroy();
            }
            return;
        }

        //save any set binding for restoration
        QQmlAbstractBinding *tmp;
        tmp = QQmlPropertyPrivate::setBinding(d->prop, 0);
        if (tmp && d->prevBind)
            tmp->destroy();
        else if (!d->prevBind)
            d->prevBind = tmp;
    }

    d->prop.write(d->value.value);
}
Пример #2
0
void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
{
    QQmlAbstractBinding *b = m_bindings.data();
    while (b) {
        b->setEnabled(e, flags);
        b = b->nextBinding();
    }
}
Пример #3
0
QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding()
{
    QQmlAbstractBinding *binding = m_bindings.data();
    while (binding) {
        binding->setAddedToObject(false);
        binding = binding->nextBinding();
    }
}
Пример #4
0
QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex)
{
    QQmlAbstractBinding *binding = m_bindings;

    while (binding && binding->propertyIndex() != propertyIndex)
        binding = binding->nextBinding();

    return binding;
}
Пример #5
0
QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(QQmlPropertyIndex propertyIndex) const
{
    QQmlAbstractBinding *binding = m_bindings.data();

    while (binding && binding->targetPropertyIndex() != propertyIndex)
        binding = binding->nextBinding();

    return binding;
}
Пример #6
0
QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding()
{
    QQmlAbstractBinding *binding = m_bindings;
    // This must be identical to the logic in QQmlData::destroyed()
    while (binding) {
        QQmlAbstractBinding *next = binding->nextBinding();
        binding->setAddedToObject(false);
        binding->setNextBinding(0);
        binding->destroy();
        binding = next;
    }
}
Пример #7
0
/*!
Removes a collection of bindings, corresponding to the set bits in \a mask.
*/
void QQmlValueTypeProxyBinding::removeBindings(quint32 mask)
{
    QQmlAbstractBinding *binding = m_bindings.data();
    QQmlAbstractBinding *lastBinding = 0;

    while (binding) {
        const int valueTypeIndex = binding->targetPropertyIndex().valueTypeIndex();
        if (valueTypeIndex != -1 && (mask & (1 << valueTypeIndex))) {
            QQmlAbstractBinding *remove = binding;
            remove->setAddedToObject(false);
            binding = remove->nextBinding();

            if (lastBinding == 0)
                m_bindings = remove->nextBinding();
            else
                lastBinding->setNextBinding(remove->nextBinding());
        } else {
            lastBinding = binding;
            binding = binding->nextBinding();
        }
    }
}
Пример #8
0
void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
{
    Q_ASSERT(m->as<QQmlValueTypeWrapper>());
    ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine();
    Scope scope(v4);
    if (scope.hasException())
        return;

    Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m));
    Scoped<QQmlValueTypeReference> reference(scope, m->d());

    int writeBackPropertyType = -1;

    if (reference) {
        QMetaProperty writebackProperty = reference->d()->object->metaObject()->property(reference->d()->property);

        if (!writebackProperty.isWritable() || !reference->readReferenceValue())
            return;

        writeBackPropertyType = writebackProperty.userType();
    }

    const QMetaObject *metaObject = r->d()->propertyCache->metaObject();
    const QQmlPropertyData *pd = r->d()->propertyCache->property(name, 0, 0);
    if (!pd)
        return;
    QMetaProperty property = metaObject->property(pd->coreIndex);
    Q_ASSERT(property.isValid());

    QQmlBinding *newBinding = 0;

    QV4::ScopedFunctionObject f(scope, value);
    if (reference && f) {
        if (!f->isBinding()) {
            // assigning a JS function to a non-var-property is not allowed.
            QString error = QStringLiteral("Cannot assign JavaScript function to value-type property");
            ScopedString e(scope, v4->newString(error));
            v4->throwError(e);
            return;
        }

        QQmlContextData *context = QmlContextWrapper::callingContext(v4);

        QQmlPropertyData cacheData;
        cacheData.setFlags(QQmlPropertyData::IsWritable |
                           QQmlPropertyData::IsValueTypeVirtual);
        cacheData.propType = writeBackPropertyType;
        cacheData.coreIndex = reference->d()->property;
        cacheData.valueTypeFlags = 0;
        cacheData.valueTypeCoreIndex = pd->coreIndex;
        cacheData.valueTypePropType = property.userType();

        QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
        bindingFunction->initBindingLocation();

        newBinding = new QQmlBinding(value, reference->d()->object, context);
        newBinding->setTarget(reference->d()->object, cacheData, context);
    }

    if (reference) {
        QQmlAbstractBinding *oldBinding =
                QQmlPropertyPrivate::setBinding(reference->d()->object, reference->d()->property, pd->coreIndex, newBinding);
        if (oldBinding)
            oldBinding->destroy();
    }

    if (newBinding)
        return;

    QVariant v = v4->toVariant(value, property.userType());

    if (property.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double)
        v = v.toInt();

    void *gadget = r->d()->gadgetPtr;
    property.writeOnGadget(gadget, v);


    if (reference) {
        if (writeBackPropertyType == QMetaType::QVariant) {
            QVariant variantReferenceValue = r->d()->toVariant();

            int flags = 0;
            int status = -1;
            void *a[] = { &variantReferenceValue, 0, &status, &flags };
            QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a);

        } else {
            int flags = 0;
            int status = -1;
            void *a[] = { r->d()->gadgetPtr, 0, &status, &flags };
            QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a);
        }
    }
}
Пример #9
0
/*!
Removes a collection of bindings, corresponding to the set bits in \a mask.
*/
void QQmlValueTypeProxyBinding::removeBindings(quint32 mask)
{
    QQmlAbstractBinding *binding = m_bindings;
    QQmlAbstractBinding *lastBinding = 0;

    while (binding) {
        int valueTypeIndex = QQmlPropertyData::decodeValueTypePropertyIndex(binding->propertyIndex());
        if (valueTypeIndex != -1 && (mask & (1 << valueTypeIndex))) {
            QQmlAbstractBinding *remove = binding;
            binding = remove->nextBinding();

            if (lastBinding == 0)
                m_bindings = remove->nextBinding();
            else
                lastBinding->setNextBinding(remove->nextBinding());

            remove->setAddedToObject(false);
            remove->setNextBinding(0);
            remove->destroy();
        } else {
            lastBinding = binding;
            binding = binding->nextBinding();
        }
    }
}
Пример #10
0
void tst_QQmlEngineDebugService::recursiveObjectTest(
        QObject *o, const QmlDebugObjectReference &oref, bool recursive) const
{
    const QMetaObject *meta = o->metaObject();

    QQmlType *type = QQmlMetaType::qmlType(meta);
    QString className = type ? QString(type->qmlTypeName())
                             : QString(meta->className());
    className = className.mid(className.lastIndexOf(QLatin1Char('/'))+1);

    QCOMPARE(oref.debugId, QQmlDebugService::idForObject(o));
    QCOMPARE(oref.name, o->objectName());
    QCOMPARE(oref.className, className);
    QCOMPARE(oref.contextDebugId, QQmlDebugService::idForObject(
                 qmlContext(o)));

    const QObjectList &children = o->children();
    for (int i=0; i<children.count(); i++) {
        QObject *child = children[i];
        if (!qmlContext(child))
            continue;
        int debugId = QQmlDebugService::idForObject(child);
        QVERIFY(debugId >= 0);

        QmlDebugObjectReference cref;
        foreach (const QmlDebugObjectReference &ref, oref.children) {
            if (ref.debugId == debugId) {
                cref = ref;
                break;
            }
        }
        QVERIFY(cref.debugId >= 0);

        if (recursive)
            recursiveObjectTest(child, cref, true);
    }

    foreach (const QmlDebugPropertyReference &p, oref.properties) {
        QCOMPARE(p.objectDebugId, QQmlDebugService::idForObject(o));

        // signal properties are fake - they are generated from QQmlAbstractBoundSignal children
        if (p.name.startsWith("on") && p.name.length() > 2 && p.name[2].isUpper()) {
            QString signal = p.value.toString();
            QQmlBoundSignalExpression *expr = QQmlPropertyPrivate::signalExpression(QQmlProperty(o, p.name));
            QVERIFY(expr && expr->expression() == signal);
            QVERIFY(p.valueTypeName.isEmpty());
            QVERIFY(p.binding.isEmpty());
            QVERIFY(!p.hasNotifySignal);
            continue;
        }

        QMetaProperty pmeta = meta->property(meta->indexOfProperty(p.name.toUtf8().constData()));

        QCOMPARE(p.name, QString::fromUtf8(pmeta.name()));

        if (pmeta.type() < QVariant::UserType && pmeta.userType() !=
                QMetaType::QVariant) // TODO test complex types
            QCOMPARE(p.value , pmeta.read(o));

        if (p.name == "parent")
            QVERIFY(p.valueTypeName == "QGraphicsObject*" ||
                    p.valueTypeName == "QQuickItem*");
        else
            QCOMPARE(p.valueTypeName, QString::fromUtf8(pmeta.typeName()));

        QQmlAbstractBinding *binding =
                QQmlPropertyPrivate::binding(
                    QQmlProperty(o, p.name));
        if (binding)
            QCOMPARE(binding->expression(), p.binding);

        QCOMPARE(p.hasNotifySignal, pmeta.hasNotifySignal());

        QVERIFY(pmeta.isValid());
    }
}
Пример #11
0
int QQmlVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a)
{
    int id = _id;
    if (c == QMetaObject::WriteProperty && interceptors &&
       !(*reinterpret_cast<int*>(a[3]) & QQmlPropertyPrivate::BypassInterceptor)) {

        for (QQmlPropertyValueInterceptor *vi = interceptors; vi; vi = vi->m_next) {
            if (vi->m_coreIndex != id)
                continue;

            int valueIndex = vi->m_valueTypeCoreIndex;
            int type = QQmlData::get(object)->propertyCache->property(id)->propType;

            if (type != QVariant::Invalid) {
                if (valueIndex != -1) {
                    QQmlValueType *valueType = QQmlValueTypeFactory::valueType(type);
                    Q_ASSERT(valueType);

                    //
                    // Consider the following case:
                    //  color c = { 0.1, 0.2, 0.3 }
                    //  interceptor exists on c.r
                    //  write { 0.2, 0.4, 0.6 }
                    //
                    // The interceptor may choose not to update the r component at this
                    // point (for example, a behavior that creates an animation). But we
                    // need to ensure that the g and b components are updated correctly.
                    //
                    // So we need to perform a full write where the value type is:
                    //    r = old value, g = new value, b = new value
                    //
                    // And then call the interceptor which may or may not write the
                    // new value to the r component.
                    //
                    // This will ensure that the other components don't contain stale data
                    // and any relevant signals are emitted.
                    //
                    // To achieve this:
                    //   (1) Store the new value type as a whole (needed due to
                    //       aliasing between a[0] and static storage in value type).
                    //   (2) Read the entire existing value type from object -> valueType temp.
                    //   (3) Read the previous value of the component being changed
                    //       from the valueType temp.
                    //   (4) Write the entire new value type into the temp.
                    //   (5) Overwrite the component being changed with the old value.
                    //   (6) Perform a full write to the value type (which may emit signals etc).
                    //   (7) Issue the interceptor call with the new component value.
                    //

                    QMetaProperty valueProp = valueType->metaObject()->property(valueIndex);
                    QVariant newValue(type, a[0]);

                    valueType->read(object, id);
                    QVariant prevComponentValue = valueProp.read(valueType);

                    valueType->setValue(newValue);
                    QVariant newComponentValue = valueProp.read(valueType);

                    // Don't apply the interceptor if the intercepted value has not changed
                    bool updated = false;
                    if (newComponentValue != prevComponentValue) {
                        valueProp.write(valueType, prevComponentValue);
                        valueType->write(object, id, QQmlPropertyPrivate::DontRemoveBinding | QQmlPropertyPrivate::BypassInterceptor);

                        vi->write(newComponentValue);
                        updated = true;
                    }

                    if (updated)
                        return -1;
                } else {
                    vi->write(QVariant(type, a[0]));
                    return -1;
                }
            }
        }
    }
    if (c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty || c == QMetaObject::ResetProperty) {
        if (id >= propOffset()) {
            id -= propOffset();

            if (id < metaData->propertyCount) {
               int t = (metaData->propertyData() + id)->propertyType;
                bool needActivate = false;

                if (id >= firstVarPropertyIndex) {
                    Q_ASSERT(t == QMetaType::QVariant);
                    // the context can be null if accessing var properties from cpp after re-parenting an item.
                    QQmlEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QQmlEnginePrivate::get(ctxt->engine);
                    QV8Engine *v8e = (ep == 0) ? 0 : ep->v8engine();
                    if (v8e) {
                        if (c == QMetaObject::ReadProperty) {
                            *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
                        } else if (c == QMetaObject::WriteProperty) {
                            writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
                        }
                    } else if (c == QMetaObject::ReadProperty) {
                        // if the context was disposed, we just return an invalid variant from read.
                        *reinterpret_cast<QVariant *>(a[0]) = QVariant();
                    }

                } else {

                    if (c == QMetaObject::ReadProperty) {
                        switch(t) {
                        case QVariant::Int:
                            *reinterpret_cast<int *>(a[0]) = data[id].asInt();
                            break;
                        case QVariant::Bool:
                            *reinterpret_cast<bool *>(a[0]) = data[id].asBool();
                            break;
                        case QVariant::Double:
                            *reinterpret_cast<double *>(a[0]) = data[id].asDouble();
                            break;
                        case QVariant::String:
                            *reinterpret_cast<QString *>(a[0]) = data[id].asQString();
                            break;
                        case QVariant::Url:
                            *reinterpret_cast<QUrl *>(a[0]) = data[id].asQUrl();
                            break;
                        case QVariant::Date:
                            *reinterpret_cast<QDate *>(a[0]) = data[id].asQDate();
                            break;
                        case QVariant::DateTime:
                            *reinterpret_cast<QDateTime *>(a[0]) = data[id].asQDateTime();
                            break;
                        case QVariant::RectF:
                            *reinterpret_cast<QRectF *>(a[0]) = data[id].asQRectF();
                            break;
                        case QVariant::SizeF:
                            *reinterpret_cast<QSizeF *>(a[0]) = data[id].asQSizeF();
                            break;
                        case QVariant::PointF:
                            *reinterpret_cast<QPointF *>(a[0]) = data[id].asQPointF();
                            break;
                        case QMetaType::QObjectStar:
                            *reinterpret_cast<QObject **>(a[0]) = data[id].asQObject();
                            break;
                        case QMetaType::QVariant:
                            *reinterpret_cast<QVariant *>(a[0]) = readPropertyAsVariant(id);
                            break;
                        default:
                            QQml_valueTypeProvider()->readValueType(data[id].dataType(), data[id].dataPtr(), data->dataSize(), t, a[0]);
                            break;
                        }
                        if (t == qMetaTypeId<QQmlListProperty<QObject> >()) {
                            int listIndex = data[id].asInt();
                            const List *list = &listProperties.at(listIndex);
                            *reinterpret_cast<QQmlListProperty<QObject> *>(a[0]) =
                                QQmlListProperty<QObject>(object, (void *)list,
                                                                  list_append, list_count, list_at,
                                                                  list_clear);
                        }

                    } else if (c == QMetaObject::WriteProperty) {

                        switch(t) {
                        case QVariant::Int:
                            needActivate = *reinterpret_cast<int *>(a[0]) != data[id].asInt();
                            data[id].setValue(*reinterpret_cast<int *>(a[0]));
                            break;
                        case QVariant::Bool:
                            needActivate = *reinterpret_cast<bool *>(a[0]) != data[id].asBool();
                            data[id].setValue(*reinterpret_cast<bool *>(a[0]));
                            break;
                        case QVariant::Double:
                            needActivate = *reinterpret_cast<double *>(a[0]) != data[id].asDouble();
                            data[id].setValue(*reinterpret_cast<double *>(a[0]));
                            break;
                        case QVariant::String:
                            needActivate = *reinterpret_cast<QString *>(a[0]) != data[id].asQString();
                            data[id].setValue(*reinterpret_cast<QString *>(a[0]));
                            break;
                        case QVariant::Url:
                            needActivate = *reinterpret_cast<QUrl *>(a[0]) != data[id].asQUrl();
                            data[id].setValue(*reinterpret_cast<QUrl *>(a[0]));
                            break;
                        case QVariant::Date:
                            needActivate = *reinterpret_cast<QDate *>(a[0]) != data[id].asQDate();
                            data[id].setValue(*reinterpret_cast<QDate *>(a[0]));
                            break;
                        case QVariant::DateTime:
                            needActivate = *reinterpret_cast<QDateTime *>(a[0]) != data[id].asQDateTime();
                            data[id].setValue(*reinterpret_cast<QDateTime *>(a[0]));
                            break;
                        case QVariant::RectF:
                            needActivate = *reinterpret_cast<QRectF *>(a[0]) != data[id].asQRectF();
                            data[id].setValue(*reinterpret_cast<QRectF *>(a[0]));
                            break;
                        case QVariant::SizeF:
                            needActivate = *reinterpret_cast<QSizeF *>(a[0]) != data[id].asQSizeF();
                            data[id].setValue(*reinterpret_cast<QSizeF *>(a[0]));
                            break;
                        case QVariant::PointF:
                            needActivate = *reinterpret_cast<QPointF *>(a[0]) != data[id].asQPointF();
                            data[id].setValue(*reinterpret_cast<QPointF *>(a[0]));
                            break;
                        case QMetaType::QObjectStar:
                            needActivate = *reinterpret_cast<QObject **>(a[0]) != data[id].asQObject();
                            data[id].setValue(*reinterpret_cast<QObject **>(a[0]), this, id);
                            break;
                        case QMetaType::QVariant:
                            writeProperty(id, *reinterpret_cast<QVariant *>(a[0]));
                            break;
                        default:
                            data[id].ensureValueType(t);
                            needActivate = !QQml_valueTypeProvider()->equalValueType(t, a[0], data[id].dataPtr(), data[id].dataSize());
                            QQml_valueTypeProvider()->writeValueType(t, a[0], data[id].dataPtr(), data[id].dataSize());
                            break;
                        }
                    }

                }

                if (c == QMetaObject::WriteProperty && needActivate) {
                    activate(object, methodOffset() + id, 0);
                }

                return -1;
            }

            id -= metaData->propertyCount;

            if (id < metaData->aliasCount) {

                QQmlVMEMetaData::AliasData *d = metaData->aliasData() + id;

                if (d->flags & QML_ALIAS_FLAG_PTR && c == QMetaObject::ReadProperty)
                        *reinterpret_cast<void **>(a[0]) = 0;

                if (!ctxt) return -1;

                QQmlContext *context = ctxt->asQQmlContext();
                QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(context);

                QObject *target = ctxtPriv->data->idValues[d->contextIdx].data();
                if (!target)
                    return -1;

                connectAlias(id);

                if (d->isObjectAlias()) {
                    *reinterpret_cast<QObject **>(a[0]) = target;
                    return -1;
                }

                // Remove binding (if any) on write
                if(c == QMetaObject::WriteProperty) {
                    int flags = *reinterpret_cast<int*>(a[3]);
                    if (flags & QQmlPropertyPrivate::RemoveBindingOnAliasWrite) {
                        QQmlData *targetData = QQmlData::get(target);
                        if (targetData && targetData->hasBindingBit(d->propertyIndex())) {
                            QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(target, d->propertyIndex(), d->isValueTypeAlias()?d->valueTypeIndex():-1, 0);
                            if (binding) binding->destroy();
                        }
                    }
                }

                if (d->isValueTypeAlias()) {
                    // Value type property
                    QQmlValueType *valueType = QQmlValueTypeFactory::valueType(d->valueType());
                    Q_ASSERT(valueType);

                    valueType->read(target, d->propertyIndex());
                    int rv = QMetaObject::metacall(valueType, c, d->valueTypeIndex(), a);

                    if (c == QMetaObject::WriteProperty)
                        valueType->write(target, d->propertyIndex(), 0x00);

                    return rv;

                } else {
                    return QMetaObject::metacall(target, c, d->propertyIndex(), a);
                }

            }
            return -1;

        }

    } else if(c == QMetaObject::InvokeMetaMethod) {

        if (id >= methodOffset()) {

            id -= methodOffset();
            int plainSignals = metaData->signalCount + metaData->propertyCount +
                               metaData->aliasCount;
            if (id < plainSignals) {
                activate(object, _id, a);
                return -1;
            }

            id -= plainSignals;

            if (id < metaData->methodCount) {
                if (!ctxt->engine)
                    return -1; // We can't run the method

                QQmlEnginePrivate *ep = QQmlEnginePrivate::get(ctxt->engine);
                ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation.
                QV4::Scope scope(ep->v4engine());


                QV4::Scoped<QV4::FunctionObject> function(scope, method(id));
                if (!function) {
                    // The function was not compiled.  There are some exceptional cases which the
                    // expression rewriter does not rewrite properly (e.g., \r-terminated lines
                    // are not rewritten correctly but this bug is deemed out-of-scope to fix for
                    // performance reasons; see QTBUG-24064) and thus compilation will have failed.
                    QQmlError e;
                    e.setDescription(QString(QLatin1String("Exception occurred during compilation of function: %1")).
                                     arg(QLatin1String(QMetaObject::method(_id).methodSignature().constData())));
                    ep->warning(e);
                    return -1; // The dynamic method with that id is not available.
                }

                QQmlVMEMetaData::MethodData *data = metaData->methodData() + id;

                QV4::ScopedCallData callData(scope, data->parameterCount);
                callData->thisObject = ep->v8engine()->global();

                for (int ii = 0; ii < data->parameterCount; ++ii)
                    callData->args[ii] = ep->v8engine()->fromVariant(*(QVariant *)a[ii + 1]);

                QV4::ScopedValue result(scope);
                QV4::ExecutionContext *ctx = function->engine()->currentContext();
                result = function->call(callData);
                if (scope.hasException()) {
                    QQmlError error = QV4::ExecutionEngine::catchExceptionAsQmlError(ctx);
                    if (error.isValid())
                        ep->warning(error);
                    if (a[0]) *(QVariant *)a[0] = QVariant();
                } else {
                    if (a[0]) *(QVariant *)a[0] = ep->v8engine()->toVariant(result, 0);
                }

                ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete.
                return -1;
            }
            return -1;
        }
    }

    if (parent.isT1())
        return parent.asT1()->metaCall(object, c, _id, a);
    else
        return object->qt_metacall(c, _id, a);
}