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); }
void QQmlValueTypeProxyBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags) { QQmlAbstractBinding *b = m_bindings.data(); while (b) { b->setEnabled(e, flags); b = b->nextBinding(); } }
QQmlValueTypeProxyBinding::~QQmlValueTypeProxyBinding() { QQmlAbstractBinding *binding = m_bindings.data(); while (binding) { binding->setAddedToObject(false); binding = binding->nextBinding(); } }
QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(int propertyIndex) { QQmlAbstractBinding *binding = m_bindings; while (binding && binding->propertyIndex() != propertyIndex) binding = binding->nextBinding(); return binding; }
QQmlAbstractBinding *QQmlValueTypeProxyBinding::binding(QQmlPropertyIndex propertyIndex) const { QQmlAbstractBinding *binding = m_bindings.data(); while (binding && binding->targetPropertyIndex() != propertyIndex) binding = binding->nextBinding(); return binding; }
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; } }
/*! 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(); } } }
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); } } }
/*! 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(); } } }
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()); } }
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); }