/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** ** This file is part of the QtDeclarative module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdeclarativevmemetaobject_p.h" #include "qdeclarative.h" #include #include "qdeclarativeexpression.h" #include "qdeclarativeexpression_p.h" #include "qdeclarativecontext_p.h" #include "qdeclarativebinding_p.h" #include "qdeclarativepropertyvalueinterceptor_p.h" #include Q_DECLARE_METATYPE(QJSValue); QT_BEGIN_NAMESPACE class QDeclarativeVMEVariant { public: inline QDeclarativeVMEVariant(); inline ~QDeclarativeVMEVariant(); inline const void *dataPtr() const; inline void *dataPtr(); inline int dataType() const; inline QObject *asQObject(); inline const QVariant &asQVariant(); inline int asInt(); inline bool asBool(); inline double asDouble(); inline const QString &asQString(); inline const QUrl &asQUrl(); inline const QColor &asQColor(); inline const QTime &asQTime(); inline const QDate &asQDate(); inline const QDateTime &asQDateTime(); inline const QJSValue &asQJSValue(); inline void setValue(QObject *); inline void setValue(const QVariant &); inline void setValue(int); inline void setValue(bool); inline void setValue(double); inline void setValue(const QString &); inline void setValue(const QUrl &); inline void setValue(const QColor &); inline void setValue(const QTime &); inline void setValue(const QDate &); inline void setValue(const QDateTime &); inline void setValue(const QJSValue &); private: int type; void *data[4]; // Large enough to hold all types inline void cleanup(); }; class QDeclarativeVMEMetaObjectEndpoint : public QDeclarativeNotifierEndpoint { public: QDeclarativeVMEMetaObjectEndpoint(); static void vmecallback(QDeclarativeNotifierEndpoint *); void tryConnect(); QFlagPointer metaObject; }; QDeclarativeVMEVariant::QDeclarativeVMEVariant() : type(QVariant::Invalid) { } QDeclarativeVMEVariant::~QDeclarativeVMEVariant() { cleanup(); } void QDeclarativeVMEVariant::cleanup() { if (type == QVariant::Invalid) { } else if (type == QMetaType::Int || type == QMetaType::Bool || type == QMetaType::Double) { type = QVariant::Invalid; } else if (type == QMetaType::QObjectStar) { ((QDeclarativeGuard*)dataPtr())->~QDeclarativeGuard(); type = QVariant::Invalid; } else if (type == QMetaType::QString) { ((QString *)dataPtr())->~QString(); type = QVariant::Invalid; } else if (type == QMetaType::QUrl) { ((QUrl *)dataPtr())->~QUrl(); type = QVariant::Invalid; } else if (type == QMetaType::QColor) { ((QColor *)dataPtr())->~QColor(); type = QVariant::Invalid; } else if (type == QMetaType::QTime) { ((QTime *)dataPtr())->~QTime(); type = QVariant::Invalid; } else if (type == QMetaType::QDate) { ((QDate *)dataPtr())->~QDate(); type = QVariant::Invalid; } else if (type == QMetaType::QDateTime) { ((QDateTime *)dataPtr())->~QDateTime(); type = QVariant::Invalid; } else if (type == qMetaTypeId()) { ((QVariant *)dataPtr())->~QVariant(); type = QVariant::Invalid; } else if (type == qMetaTypeId()) { ((QJSValue *)dataPtr())->~QJSValue(); type = QVariant::Invalid; } } int QDeclarativeVMEVariant::dataType() const { return type; } const void *QDeclarativeVMEVariant::dataPtr() const { return &data; } void *QDeclarativeVMEVariant::dataPtr() { return &data; } QObject *QDeclarativeVMEVariant::asQObject() { if (type != QMetaType::QObjectStar) setValue((QObject *)0); return *(QDeclarativeGuard *)(dataPtr()); } const QVariant &QDeclarativeVMEVariant::asQVariant() { if (type != QMetaType::QVariant) setValue(QVariant()); return *(QVariant *)(dataPtr()); } int QDeclarativeVMEVariant::asInt() { if (type != QMetaType::Int) setValue(int(0)); return *(int *)(dataPtr()); } bool QDeclarativeVMEVariant::asBool() { if (type != QMetaType::Bool) setValue(bool(false)); return *(bool *)(dataPtr()); } double QDeclarativeVMEVariant::asDouble() { if (type != QMetaType::Double) setValue(double(0)); return *(double *)(dataPtr()); } const QString &QDeclarativeVMEVariant::asQString() { if (type != QMetaType::QString) setValue(QString()); return *(QString *)(dataPtr()); } const QUrl &QDeclarativeVMEVariant::asQUrl() { if (type != QMetaType::QUrl) setValue(QUrl()); return *(QUrl *)(dataPtr()); } const QColor &QDeclarativeVMEVariant::asQColor() { if (type != QMetaType::QColor) setValue(QColor()); return *(QColor *)(dataPtr()); } const QTime &QDeclarativeVMEVariant::asQTime() { if (type != QMetaType::QTime) setValue(QTime()); return *(QTime *)(dataPtr()); } const QDate &QDeclarativeVMEVariant::asQDate() { if (type != QMetaType::QDate) setValue(QDate()); return *(QDate *)(dataPtr()); } const QDateTime &QDeclarativeVMEVariant::asQDateTime() { if (type != QMetaType::QDateTime) setValue(QDateTime()); return *(QDateTime *)(dataPtr()); } const QJSValue &QDeclarativeVMEVariant::asQJSValue() { if (type != qMetaTypeId()) setValue(QJSValue()); return *(QJSValue *)(dataPtr()); } void QDeclarativeVMEVariant::setValue(QObject *v) { if (type != QMetaType::QObjectStar) { cleanup(); type = QMetaType::QObjectStar; new (dataPtr()) QDeclarativeGuard(); } *(QDeclarativeGuard*)(dataPtr()) = v; } void QDeclarativeVMEVariant::setValue(const QVariant &v) { if (type != qMetaTypeId()) { cleanup(); type = qMetaTypeId(); new (dataPtr()) QVariant(v); } else { *(QVariant *)(dataPtr()) = v; } } void QDeclarativeVMEVariant::setValue(int v) { if (type != QMetaType::Int) { cleanup(); type = QMetaType::Int; } *(int *)(dataPtr()) = v; } void QDeclarativeVMEVariant::setValue(bool v) { if (type != QMetaType::Bool) { cleanup(); type = QMetaType::Bool; } *(bool *)(dataPtr()) = v; } void QDeclarativeVMEVariant::setValue(double v) { if (type != QMetaType::Double) { cleanup(); type = QMetaType::Double; } *(double *)(dataPtr()) = v; } void QDeclarativeVMEVariant::setValue(const QString &v) { if (type != QMetaType::QString) { cleanup(); type = QMetaType::QString; new (dataPtr()) QString(v); } else { *(QString *)(dataPtr()) = v; } } void QDeclarativeVMEVariant::setValue(const QUrl &v) { if (type != QMetaType::QUrl) { cleanup(); type = QMetaType::QUrl; new (dataPtr()) QUrl(v); } else { *(QUrl *)(dataPtr()) = v; } } void QDeclarativeVMEVariant::setValue(const QColor &v) { if (type != QMetaType::QColor) { cleanup(); type = QMetaType::QColor; new (dataPtr()) QColor(v); } else { *(QColor *)(dataPtr()) = v; } } void QDeclarativeVMEVariant::setValue(const QTime &v) { if (type != QMetaType::QTime) { cleanup(); type = QMetaType::QTime; new (dataPtr()) QTime(v); } else { *(QTime *)(dataPtr()) = v; } } void QDeclarativeVMEVariant::setValue(const QDate &v) { if (type != QMetaType::QDate) { cleanup(); type = QMetaType::QDate; new (dataPtr()) QDate(v); } else { *(QDate *)(dataPtr()) = v; } } void QDeclarativeVMEVariant::setValue(const QDateTime &v) { if (type != QMetaType::QDateTime) { cleanup(); type = QMetaType::QDateTime; new (dataPtr()) QDateTime(v); } else { *(QDateTime *)(dataPtr()) = v; } } void QDeclarativeVMEVariant::setValue(const QJSValue &v) { if (type != qMetaTypeId()) { cleanup(); type = qMetaTypeId(); new (dataPtr()) QJSValue(v); } else { *(QJSValue *)(dataPtr()) = v; } } QDeclarativeVMEMetaObjectEndpoint::QDeclarativeVMEMetaObjectEndpoint() { callback = &vmecallback; } void QDeclarativeVMEMetaObjectEndpoint::vmecallback(QDeclarativeNotifierEndpoint *e) { QDeclarativeVMEMetaObjectEndpoint *vmee = static_cast(e); vmee->tryConnect(); } void QDeclarativeVMEMetaObjectEndpoint::tryConnect() { int aliasId = this - metaObject->aliasEndpoints; if (metaObject.flag()) { // This is actually notify int sigIdx = metaObject->methodOffset + aliasId + metaObject->metaData->propertyCount; QMetaObject::activate(metaObject->object, sigIdx, 0); } else { QDeclarativeVMEMetaData::AliasData *d = metaObject->metaData->aliasData() + aliasId; if (!d->isObjectAlias()) { QDeclarativeContextData *ctxt = metaObject->ctxt; QObject *target = ctxt->idValues[d->contextIdx].data(); if (!target) return; QMetaProperty prop = target->metaObject()->property(d->propertyIndex()); if (prop.hasNotifySignal()) connect(target, prop.notifySignalIndex()); } metaObject.setFlag(); } } QDeclarativeVMEMetaObject::QDeclarativeVMEMetaObject(QObject *obj, const QMetaObject *other, const QDeclarativeVMEMetaData *meta, QDeclarativeCompiledData *cdata) : QV8GCCallback::Node(GcPrologueCallback), object(obj), compiledData(cdata), ctxt(QDeclarativeData::get(obj, true)->outerContext), metaData(meta), data(0), aliasEndpoints(0), firstVarPropertyIndex(-1), varPropertiesInitialized(false), v8methods(0), parent(0) { compiledData->addref(); *static_cast(this) = *other; this->d.superdata = obj->metaObject(); QObjectPrivate *op = QObjectPrivate::get(obj); if (op->metaObject) parent = static_cast(op->metaObject); op->metaObject = this; propOffset = QAbstractDynamicMetaObject::propertyOffset(); methodOffset = QAbstractDynamicMetaObject::methodOffset(); data = new QDeclarativeVMEVariant[metaData->propertyCount - metaData->varPropertyCount]; aConnected.resize(metaData->aliasCount); int list_type = qMetaTypeId >(); // ### Optimize for (int ii = 0; ii < metaData->propertyCount - metaData->varPropertyCount; ++ii) { int t = (metaData->propertyData() + ii)->propertyType; if (t == list_type) { listProperties.append(List(methodOffset + ii)); data[ii].setValue(listProperties.count() - 1); } } firstVarPropertyIndex = metaData->propertyCount - metaData->varPropertyCount; if (metaData->varPropertyCount) QV8GCCallback::addGcCallbackNode(this); } QDeclarativeVMEMetaObject::~QDeclarativeVMEMetaObject() { compiledData->release(); delete parent; delete [] data; delete [] aliasEndpoints; for (int ii = 0; v8methods && ii < metaData->methodCount; ++ii) { qPersistentDispose(v8methods[ii]); } delete [] v8methods; if (metaData->varPropertyCount) qPersistentDispose(varProperties); // if not weak, will not have been cleaned up by the callback. } int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) { int id = _id; if(c == QMetaObject::WriteProperty) { int flags = *reinterpret_cast(a[3]); if (!(flags & QDeclarativePropertyPrivate::BypassInterceptor) && !aInterceptors.isEmpty() && aInterceptors.testBit(id)) { QPair pair = interceptors.value(id); int valueIndex = pair.first; QDeclarativePropertyValueInterceptor *vi = pair.second; int type = property(id).userType(); if (type != QVariant::Invalid) { if (valueIndex != -1) { QDeclarativeEnginePrivate *ep = ctxt?QDeclarativeEnginePrivate::get(ctxt->engine):0; QDeclarativeValueType *valueType = 0; if (ep) valueType = ep->valueTypes[type]; else valueType = QDeclarativeValueTypeFactory::valueType(type); Q_ASSERT(valueType); valueType->setValue(QVariant(type, a[0])); QMetaProperty valueProp = valueType->metaObject()->property(valueIndex); vi->write(valueProp.read(valueType)); if (!ep) delete valueType; 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. QDeclarativeEnginePrivate *ep = (ctxt == 0 || ctxt->engine == 0) ? 0 : QDeclarativeEnginePrivate::get(ctxt->engine); QV8Engine *v8e = (ep == 0) ? 0 : ep->v8engine(); if (v8e) { v8::HandleScope handleScope; v8::Context::Scope contextScope(v8e->context()); if (c == QMetaObject::ReadProperty) { *reinterpret_cast(a[0]) = readPropertyAsVariant(id); } else if (c == QMetaObject::WriteProperty) { writeProperty(id, *reinterpret_cast(a[0])); } } else if (c == QMetaObject::ReadProperty) { // if the context was disposed, we just return an invalid variant from read. *reinterpret_cast(a[0]) = QVariant(); } } else { if (c == QMetaObject::ReadProperty) { switch(t) { case QVariant::Int: *reinterpret_cast(a[0]) = data[id].asInt(); break; case QVariant::Bool: *reinterpret_cast(a[0]) = data[id].asBool(); break; case QVariant::Double: *reinterpret_cast(a[0]) = data[id].asDouble(); break; case QVariant::String: *reinterpret_cast(a[0]) = data[id].asQString(); break; case QVariant::Url: *reinterpret_cast(a[0]) = data[id].asQUrl(); break; case QVariant::Color: *reinterpret_cast(a[0]) = data[id].asQColor(); break; case QVariant::Date: *reinterpret_cast(a[0]) = data[id].asQDate(); break; case QVariant::DateTime: *reinterpret_cast(a[0]) = data[id].asQDateTime(); break; case QMetaType::QObjectStar: *reinterpret_cast(a[0]) = data[id].asQObject(); break; case QMetaType::QVariant: *reinterpret_cast(a[0]) = readPropertyAsVariant(id); break; default: break; } if (t == qMetaTypeId >()) { int listIndex = data[id].asInt(); const List *list = &listProperties.at(listIndex); *reinterpret_cast *>(a[0]) = QDeclarativeListProperty(object, (void *)list, list_append, list_count, list_at, list_clear); } } else if (c == QMetaObject::WriteProperty) { switch(t) { case QVariant::Int: needActivate = *reinterpret_cast(a[0]) != data[id].asInt(); data[id].setValue(*reinterpret_cast(a[0])); break; case QVariant::Bool: needActivate = *reinterpret_cast(a[0]) != data[id].asBool(); data[id].setValue(*reinterpret_cast(a[0])); break; case QVariant::Double: needActivate = *reinterpret_cast(a[0]) != data[id].asDouble(); data[id].setValue(*reinterpret_cast(a[0])); break; case QVariant::String: needActivate = *reinterpret_cast(a[0]) != data[id].asQString(); data[id].setValue(*reinterpret_cast(a[0])); break; case QVariant::Url: needActivate = *reinterpret_cast(a[0]) != data[id].asQUrl(); data[id].setValue(*reinterpret_cast(a[0])); break; case QVariant::Color: needActivate = *reinterpret_cast(a[0]) != data[id].asQColor(); data[id].setValue(*reinterpret_cast(a[0])); break; case QVariant::Date: needActivate = *reinterpret_cast(a[0]) != data[id].asQDate(); data[id].setValue(*reinterpret_cast(a[0])); break; case QVariant::DateTime: needActivate = *reinterpret_cast(a[0]) != data[id].asQDateTime(); data[id].setValue(*reinterpret_cast(a[0])); break; case QMetaType::QObjectStar: needActivate = *reinterpret_cast(a[0]) != data[id].asQObject(); data[id].setValue(*reinterpret_cast(a[0])); break; case QMetaType::QVariant: writeProperty(id, *reinterpret_cast(a[0])); break; default: break; } } } if (c == QMetaObject::WriteProperty && needActivate) { activate(object, methodOffset + id, 0); } return -1; } id -= metaData->propertyCount; if (id < metaData->aliasCount) { QDeclarativeVMEMetaData::AliasData *d = metaData->aliasData() + id; if (d->flags & QML_ALIAS_FLAG_PTR && c == QMetaObject::ReadProperty) *reinterpret_cast(a[0]) = 0; if (!ctxt) return -1; QDeclarativeContext *context = ctxt->asQDeclarativeContext(); QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(context); QObject *target = ctxtPriv->data->idValues[d->contextIdx].data(); if (!target) return -1; connectAlias(id); if (d->isObjectAlias()) { *reinterpret_cast(a[0]) = target; return -1; } // Remove binding (if any) on write if(c == QMetaObject::WriteProperty) { int flags = *reinterpret_cast(a[3]); if (flags & QDeclarativePropertyPrivate::RemoveBindingOnAliasWrite) { QDeclarativeData *targetData = QDeclarativeData::get(target); if (targetData && targetData->hasBindingBit(d->propertyIndex())) { QDeclarativeAbstractBinding *binding = QDeclarativePropertyPrivate::setBinding(target, d->propertyIndex(), d->isValueTypeAlias()?d->valueTypeIndex():-1, 0); if (binding) binding->destroy(); } } } if (d->isValueTypeAlias()) { // Value type property QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine); QDeclarativeValueType *valueType = ep->valueTypes[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) { QMetaObject::activate(object, _id, a); return -1; } id -= plainSignals; if (id < metaData->methodCount) { if (!ctxt->engine) return -1; // We can't run the method QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(ctxt->engine); ep->referenceScarceResources(); // "hold" scarce resources in memory during evaluation. v8::Handle function = method(id); if (function.IsEmpty()) { // 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. QDeclarativeError e; e.setDescription(QString(QLatin1String("Exception occurred during compilation of function: %1")). arg(QLatin1String(QMetaObject::method(_id).signature()))); ep->warning(e); return -1; // The dynamic method with that id is not available. } QDeclarativeVMEMetaData::MethodData *data = metaData->methodData() + id; v8::HandleScope handle_scope; v8::Context::Scope scope(ep->v8engine()->context()); v8::Handle *args = 0; if (data->parameterCount) { args = new v8::Handle[data->parameterCount]; for (int ii = 0; ii < data->parameterCount; ++ii) args[ii] = ep->v8engine()->fromVariant(*(QVariant *)a[ii + 1]); } v8::TryCatch try_catch; v8::Local result = function->Call(ep->v8engine()->global(), data->parameterCount, args); QVariant rv; if (try_catch.HasCaught()) { QDeclarativeError error; QDeclarativeExpressionPrivate::exceptionToError(try_catch.Message(), error); 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) return parent->metaCall(c, _id, a); else return object->qt_metacall(c, _id, a); } v8::Handle QDeclarativeVMEMetaObject::method(int index) { if (!v8methods) v8methods = new v8::Persistent[metaData->methodCount]; if (v8methods[index].IsEmpty()) { QDeclarativeVMEMetaData::MethodData *data = metaData->methodData() + index; const char *body = ((const char*)metaData) + data->bodyOffset; int bodyLength = data->bodyLength; // XXX We should evaluate all methods in a single big script block to // improve the call time between dynamic methods defined on the same // object v8methods[index] = QDeclarativeExpressionPrivate::evalFunction(ctxt, object, body, bodyLength, ctxt->urlString, data->lineNumber); } return v8methods[index]; } v8::Handle QDeclarativeVMEMetaObject::readVarProperty(int id) { Q_ASSERT(id >= firstVarPropertyIndex); ensureVarPropertiesAllocated(); return varProperties->Get(id - firstVarPropertyIndex); } QVariant QDeclarativeVMEMetaObject::readPropertyAsVariant(int id) { if (id >= firstVarPropertyIndex) { ensureVarPropertiesAllocated(); return QDeclarativeEnginePrivate::get(ctxt->engine)->v8engine()->toVariant(varProperties->Get(id - firstVarPropertyIndex), -1); } else { if (data[id].dataType() == QMetaType::QObjectStar) { return QVariant::fromValue(data[id].asQObject()); } else { return data[id].asQVariant(); } } } void QDeclarativeVMEMetaObject::writeVarProperty(int id, v8::Handle value) { Q_ASSERT(id >= firstVarPropertyIndex); ensureVarPropertiesAllocated(); // Importantly, if the current value is a scarce resource, we need to ensure that it // gets automatically released by the engine if no other references to it exist. v8::Local oldv = varProperties->Get(id - firstVarPropertyIndex); if (oldv->IsObject()) { QV8VariantResource *r = v8_resource_cast(v8::Handle::Cast(oldv)); if (r) { r->removeVmePropertyReference(); } } // And, if the new value is a scarce resource, we need to ensure that it does not get // automatically released by the engine until no other references to it exist. if (value->IsObject()) { QV8VariantResource *r = v8_resource_cast(v8::Handle::Cast(value)); if (r) { r->addVmePropertyReference(); } } // Write the value and emit change signal as appropriate. varProperties->Set(id - firstVarPropertyIndex, value); activate(object, methodOffset + id, 0); } void QDeclarativeVMEMetaObject::writeProperty(int id, const QVariant &value) { if (id >= firstVarPropertyIndex) { ensureVarPropertiesAllocated(); // Importantly, if the current value is a scarce resource, we need to ensure that it // gets automatically released by the engine if no other references to it exist. v8::Local oldv = varProperties->Get(id - firstVarPropertyIndex); if (oldv->IsObject()) { QV8VariantResource *r = v8_resource_cast(v8::Handle::Cast(oldv)); if (r) { r->removeVmePropertyReference(); } } // And, if the new value is a scarce resource, we need to ensure that it does not get // automatically released by the engine until no other references to it exist. v8::Handle newv = QDeclarativeEnginePrivate::get(ctxt->engine)->v8engine()->fromVariant(value); if (newv->IsObject()) { QV8VariantResource *r = v8_resource_cast(v8::Handle::Cast(newv)); if (r) { r->addVmePropertyReference(); } } // Write the value and emit change signal as appropriate. QVariant currentValue = readPropertyAsVariant(id); varProperties->Set(id - firstVarPropertyIndex, newv); if ((currentValue.userType() != value.userType() || currentValue != value)) activate(object, methodOffset + id, 0); } else { bool needActivate = false; if (value.userType() == QMetaType::QObjectStar) { QObject *o = qvariant_cast(value); needActivate = (data[id].dataType() != QMetaType::QObjectStar || data[id].asQObject() != o); data[id].setValue(qvariant_cast(value)); } else { needActivate = (data[id].dataType() != qMetaTypeId() || data[id].asQVariant().userType() != value.userType() || data[id].asQVariant() != value); data[id].setValue(value); } if (needActivate) activate(object, methodOffset + id, 0); } } void QDeclarativeVMEMetaObject::listChanged(int id) { activate(object, methodOffset + id, 0); } void QDeclarativeVMEMetaObject::list_append(QDeclarativeListProperty *prop, QObject *o) { List *list = static_cast(prop->data); list->append(o); QMetaObject::activate(prop->object, list->notifyIndex, 0); } int QDeclarativeVMEMetaObject::list_count(QDeclarativeListProperty *prop) { return static_cast(prop->data)->count(); } QObject *QDeclarativeVMEMetaObject::list_at(QDeclarativeListProperty *prop, int index) { return static_cast(prop->data)->at(index); } void QDeclarativeVMEMetaObject::list_clear(QDeclarativeListProperty *prop) { List *list = static_cast(prop->data); list->clear(); QMetaObject::activate(prop->object, list->notifyIndex, 0); } void QDeclarativeVMEMetaObject::registerInterceptor(int index, int valueIndex, QDeclarativePropertyValueInterceptor *interceptor) { if (aInterceptors.isEmpty()) aInterceptors.resize(propertyCount() + metaData->propertyCount); aInterceptors.setBit(index); interceptors.insert(index, qMakePair(valueIndex, interceptor)); } int QDeclarativeVMEMetaObject::vmeMethodLineNumber(int index) { if (index < methodOffset) { Q_ASSERT(parent); return static_cast(parent)->vmeMethodLineNumber(index); } int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; Q_ASSERT(index >= (methodOffset + plainSignals) && index < (methodOffset + plainSignals + metaData->methodCount)); int rawIndex = index - methodOffset - plainSignals; QDeclarativeVMEMetaData::MethodData *data = metaData->methodData() + rawIndex; return data->lineNumber; } v8::Handle QDeclarativeVMEMetaObject::vmeMethod(int index) { if (index < methodOffset) { Q_ASSERT(parent); return static_cast(parent)->vmeMethod(index); } int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; Q_ASSERT(index >= (methodOffset + plainSignals) && index < (methodOffset + plainSignals + metaData->methodCount)); return method(index - methodOffset - plainSignals); } // Used by debugger void QDeclarativeVMEMetaObject::setVmeMethod(int index, v8::Persistent value) { if (index < methodOffset) { Q_ASSERT(parent); return static_cast(parent)->setVmeMethod(index, value); } int plainSignals = metaData->signalCount + metaData->propertyCount + metaData->aliasCount; Q_ASSERT(index >= (methodOffset + plainSignals) && index < (methodOffset + plainSignals + metaData->methodCount)); if (!v8methods) v8methods = new v8::Persistent[metaData->methodCount]; int methodIndex = index - methodOffset - plainSignals; if (!v8methods[methodIndex].IsEmpty()) qPersistentDispose(v8methods[methodIndex]); v8methods[methodIndex] = value; } v8::Handle QDeclarativeVMEMetaObject::vmeProperty(int index) { if (index < propOffset) { Q_ASSERT(parent); return static_cast(parent)->vmeProperty(index); } return readVarProperty(index - propOffset); } void QDeclarativeVMEMetaObject::setVMEProperty(int index, v8::Handle v) { if (index < propOffset) { Q_ASSERT(parent); static_cast(parent)->setVMEProperty(index, v); return; } return writeVarProperty(index - propOffset, v); } void QDeclarativeVMEMetaObject::ensureVarPropertiesAllocated() { if (!varPropertiesInitialized) allocateVarPropertiesArray(); } // see also: QV8GCCallback::garbageCollectorPrologueCallback() void QDeclarativeVMEMetaObject::allocateVarPropertiesArray() { v8::HandleScope handleScope; v8::Context::Scope cs(QDeclarativeEnginePrivate::get(ctxt->engine)->v8engine()->context()); varProperties = qPersistentNew(v8::Array::New(metaData->varPropertyCount)); varProperties.MakeWeak(static_cast(this), VarPropertiesWeakReferenceCallback); varPropertiesInitialized = true; } /* The "var" properties are stored in a v8::Array which will be strong persistent if the object has cpp-ownership and the root QObject in the parent chain does not have JS-ownership. In the weak persistent handle case, this callback will dispose the handle when the v8object which owns the lifetime of the var properties array is cleared as a result of all other handles to that v8object being released. See QV8GCCallback::garbageCollectorPrologueCallback() for more information. */ void QDeclarativeVMEMetaObject::VarPropertiesWeakReferenceCallback(v8::Persistent object, void* parameter) { QDeclarativeVMEMetaObject *vmemo = static_cast(parameter); Q_ASSERT(vmemo); qPersistentDispose(object); vmemo->varProperties.Clear(); } void QDeclarativeVMEMetaObject::GcPrologueCallback(QV8GCCallback::Node *node) { QDeclarativeVMEMetaObject *vmemo = static_cast(node); Q_ASSERT(vmemo); if (!vmemo->varPropertiesInitialized || vmemo->varProperties.IsEmpty() || !vmemo->ctxt || !vmemo->ctxt->engine) return; QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(vmemo->ctxt->engine); ep->v8engine()->addRelationshipForGC(vmemo->object, vmemo->varProperties); } bool QDeclarativeVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const { Q_ASSERT(index >= propOffset + metaData->propertyCount); *target = 0; *coreIndex = -1; *valueTypeIndex = -1; if (!ctxt) return false; QDeclarativeVMEMetaData::AliasData *d = metaData->aliasData() + (index - propOffset - metaData->propertyCount); QDeclarativeContext *context = ctxt->asQDeclarativeContext(); QDeclarativeContextPrivate *ctxtPriv = QDeclarativeContextPrivate::get(context); *target = ctxtPriv->data->idValues[d->contextIdx].data(); if (!*target) return false; if (d->isObjectAlias()) { } else if (d->isValueTypeAlias()) { *coreIndex = d->propertyIndex(); *valueTypeIndex = d->valueTypeIndex(); } else { *coreIndex = d->propertyIndex(); } return true; } void QDeclarativeVMEMetaObject::connectAlias(int aliasId) { if (!aConnected.testBit(aliasId)) { if (!aliasEndpoints) aliasEndpoints = new QDeclarativeVMEMetaObjectEndpoint[metaData->aliasCount]; aConnected.setBit(aliasId); QDeclarativeVMEMetaData::AliasData *d = metaData->aliasData() + aliasId; QDeclarativeVMEMetaObjectEndpoint *endpoint = aliasEndpoints + aliasId; endpoint->metaObject = this; endpoint->connect(&ctxt->idValues[d->contextIdx].bindings); endpoint->tryConnect(); } } void QDeclarativeVMEMetaObject::connectAliasSignal(int index) { int aliasId = (index - methodOffset) - metaData->propertyCount; if (aliasId < 0 || aliasId >= metaData->aliasCount) return; connectAlias(aliasId); } QT_END_NAMESPACE