diff options
Diffstat (limited to 'src/qml/qml')
-rw-r--r-- | src/qml/qml/qml.pri | 6 | ||||
-rw-r--r-- | src/qml/qml/qqmlabstractbinding_p.h | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmlcompileddata.cpp | 6 | ||||
-rw-r--r-- | src/qml/qml/qqmlcompiler_p.h | 12 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent.cpp | 26 | ||||
-rw-r--r-- | src/qml/qml/qqmlcomponent_p.h | 16 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine.cpp | 2 | ||||
-rw-r--r-- | src/qml/qml/qqmlengine_p.h | 1 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator.cpp | 686 | ||||
-rw-r--r-- | src/qml/qml/qqmlobjectcreator_p.h | 97 | ||||
-rw-r--r-- | src/qml/qml/qqmlpropertycache_p.h | 5 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader.cpp | 79 | ||||
-rw-r--r-- | src/qml/qml/qqmltypeloader_p.h | 6 |
13 files changed, 928 insertions, 19 deletions
diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index dae76ede32..77b0334c7e 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -54,7 +54,8 @@ SOURCES += \ $$PWD/qqmllistwrapper.cpp \ $$PWD/qqmlcontextwrapper.cpp \ $$PWD/qqmlvaluetypewrapper.cpp \ - $$PWD/qqmltypewrapper.cpp + $$PWD/qqmltypewrapper.cpp \ + $$PWD/qqmlobjectcreator.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ @@ -130,7 +131,8 @@ HEADERS += \ $$PWD/qqmllistwrapper_p.h \ $$PWD/qqmlcontextwrapper_p.h \ $$PWD/qqmlvaluetypewrapper_p.h \ - $$PWD/qqmltypewrapper_p.h + $$PWD/qqmltypewrapper_p.h \ + $$PWD/qqmlobjectcreator_p.h include(ftw/ftw.pri) include(v8/v8.pri) diff --git a/src/qml/qml/qqmlabstractbinding_p.h b/src/qml/qml/qqmlabstractbinding_p.h index 40d42d8284..8b04d823e2 100644 --- a/src/qml/qml/qqmlabstractbinding_p.h +++ b/src/qml/qml/qqmlabstractbinding_p.h @@ -60,6 +60,10 @@ QT_BEGIN_NAMESPACE +namespace QtQml { +class QmlObjectCreator; +} + class Q_QML_PRIVATE_EXPORT QQmlAbstractBinding { public: @@ -150,6 +154,7 @@ private: friend class QQmlVME; friend class QtSharedPointer::ExternalRefCount<QQmlAbstractBinding>; friend class QV4Bindings; + friend class QtQml::QmlObjectCreator; typedef QSharedPointer<QQmlAbstractBinding> SharedPointer; // To save memory, we also store the rarely used weakPointer() instance in here diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index abe278f570..a2e323a129 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -87,7 +87,7 @@ int QQmlCompiledData::indexForUrl(const QUrl &data) QQmlCompiledData::QQmlCompiledData(QQmlEngine *engine) : engine(engine), importCache(0), metaTypeId(-1), listMetaTypeId(-1), isRegisteredWithEngine(false), - rootPropertyCache(0) + rootPropertyCache(0), compilationUnit(0), qmlUnit(0) { Q_ASSERT(engine); @@ -127,6 +127,10 @@ QQmlCompiledData::~QQmlCompiledData() if (rootPropertyCache) rootPropertyCache->release(); + + if (compilationUnit) + compilationUnit->deref(); + free(qmlUnit); } void QQmlCompiledData::clear() diff --git a/src/qml/qml/qqmlcompiler_p.h b/src/qml/qml/qqmlcompiler_p.h index bc013358f5..ca825a923d 100644 --- a/src/qml/qml/qqmlcompiler_p.h +++ b/src/qml/qml/qqmlcompiler_p.h @@ -70,6 +70,13 @@ QT_BEGIN_NAMESPACE +namespace QV4 { +namespace CompiledData { +struct CompilationUnit; +struct QmlUnit; +} +} + class QQmlEngine; class QQmlComponent; class QQmlContext; @@ -125,6 +132,11 @@ public: QList<QQmlScriptData *> scripts; QList<QUrl> urls; + // --- new compiler + QV4::CompiledData::CompilationUnit *compilationUnit; + QV4::CompiledData::QmlUnit *qmlUnit; + // --- + struct Instruction { #define QML_INSTR_DATA_TYPEDEF(I, FMT) typedef QQmlInstructionData<QQmlInstruction::I> I; FOR_EACH_QML_INSTR(QML_INSTR_DATA_TYPEDEF) diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 5f3a2e971c..ff961c7822 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -893,8 +893,16 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context) state.completePending = true; enginePriv->referenceScarceResources(); - state.vme.init(context, cc, start, creationContext); - QObject *rv = state.vme.execute(&state.errors); + QObject *rv = 0; + if (enginePriv->useNewCompiler) { + state.creator = new QtQml::QmlObjectCreator(engine, context, cc); + rv = state.creator->create(); + if (!rv) + state.errors = state.creator->errors; + } else { + state.vme.init(context, cc, start, creationContext); + rv = state.vme.execute(&state.errors); + } enginePriv->dereferenceScarceResources(); if (rv) { @@ -934,14 +942,22 @@ void QQmlComponentPrivate::beginDeferred(QQmlEnginePrivate *enginePriv, state->errors.clear(); state->completePending = true; - state->vme.initDeferred(object); - state->vme.execute(&state->errors); + if (enginePriv->useNewCompiler) { + // ### + } else { + state->vme.initDeferred(object); + state->vme.execute(&state->errors); + } } void QQmlComponentPrivate::complete(QQmlEnginePrivate *enginePriv, ConstructionState *state) { if (state->completePending) { - state->vme.complete(); + if (enginePriv->useNewCompiler) { + // ### + } else { + state->vme.complete(); + } state->completePending = false; diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h index 439f1fcdd9..3bd0a194df 100644 --- a/src/qml/qml/qqmlcomponent_p.h +++ b/src/qml/qml/qqmlcomponent_p.h @@ -62,6 +62,7 @@ #include "qqmlerror.h" #include "qqml.h" #include <private/qqmlprofilerservice_p.h> +#include <private/qqmlobjectcreator_p.h> #include <QtCore/QString> #include <QtCore/QStringList> @@ -105,9 +106,20 @@ public: QQmlCompiledData *cc; struct ConstructionState { - ConstructionState() : completePending(false) {} - + ConstructionState() + : creator(0) + , completePending(false) + {} + ~ConstructionState() + { + delete creator; + } + + // --- new compiler + QtQml::QmlObjectCreator *creator; + // --- old compiler QQmlVME vme; + // --- QList<QQmlError> errors; bool completePending; }; diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 39c3399917..971836f783 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -510,6 +510,7 @@ the same object as is returned from the Qt.include() call. */ // Qt.include() is implemented in qv4include.cpp +DEFINE_BOOL_CONFIG_OPTION(qmlUseNewCompiler, QML_NEW_COMPILER) QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) : propertyCapture(0), rootContext(0), isDebugging(false), @@ -520,6 +521,7 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), incubatorCount(0), incubationController(0), mutex(QMutex::Recursive) { + useNewCompiler = qmlUseNewCompiler(); } QQmlEnginePrivate::~QQmlEnginePrivate() diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index c57c70fa40..b57c84ceae 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -144,6 +144,7 @@ public: QQmlContext *rootContext; bool isDebugging; + bool useNewCompiler; bool outputWarningsToStdErr; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp new file mode 100644 index 0000000000..8fdfe8e4f1 --- /dev/null +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -0,0 +1,686 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlobjectcreator_p.h" + +#include <private/qqmlengine_p.h> +#include <private/qqmlabstractbinding_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qv4function_p.h> +#include <private/qv4functionobject_p.h> +#include <private/qqmlcontextwrapper_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qqmlstringconverters_p.h> + +QT_USE_NAMESPACE + +using namespace QtQml; + +static void removeBindingOnProperty(QObject *o, int index) +{ + int coreIndex = index & 0x0000FFFF; + int valueTypeIndex = (index & 0xFFFF0000 ? index >> 16 : -1); + + QQmlAbstractBinding *binding = QQmlPropertyPrivate::setBinding(o, coreIndex, valueTypeIndex, 0); + if (binding) binding->destroy(); +} + +QmlObjectCreator::QmlObjectCreator(QQmlEngine *engine, QQmlContextData *contextData, QQmlCompiledData *runtimeData) + : engine(engine) + , unit(runtimeData->qmlUnit) + , jsUnit(runtimeData->compilationUnit) + , context(contextData) + , typeNameCache(0) + , runtimeData(runtimeData) + , imports(&QQmlEnginePrivate::get(engine)->typeLoader) + , _object(0) + , _ddata(0) + , _propertyCache(0) +{ + typeNameCache = new QQmlTypeNameCache; + + QQmlImportDatabase *idb = &QQmlEnginePrivate::get(engine)->importDatabase; + + for (uint i = 0; i < unit->nImports; ++i) { + const QV4::CompiledData::Import *import = unit->importAt(i); + if (import->type == QV4::CompiledData::Import::ImportLibrary) { + imports.addLibraryImport(idb, stringAt(import->uriIndex), stringAt(import->qualifierIndex), + import->majorVersion, import->minorVersion, /*qmldir identifier*/ QString(), + /*qmldir url*/ QString(), /*incomplete*/ false, &errors); + } else { + // ### + } + } + + imports.populateCache(typeNameCache); + context->imports = typeNameCache; + context->imports->addref(); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + if (runtimeData->compilationUnit && !runtimeData->compilationUnit->engine) + runtimeData->compilationUnit->linkToEngine(v4); +} + +QVector<QQmlAbstractBinding*> QmlObjectCreator::setupBindings(const QV4::CompiledData::Object *obj, QV4::Object *qmlGlobal) +{ + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + + QQmlPropertyPrivate::WriteFlags propertyWriteFlags = QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::RemoveBindingOnAliasWrite; + int propertyWriteStatus = -1; + QVariant fallbackVariantValue; + + QVector<QQmlAbstractBinding*> createdDynamicBindings(obj->nBindings, 0); + + const QV4::CompiledData::Binding *binding = obj->bindingTable(); + for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) { + QString name = stringAt(binding->propertyNameIndex); + + if (name.isEmpty() && binding->value.type == QV4::CompiledData::Value::Type_Object) { + create(binding->value.objectIndex, _object); + continue; + } + + QQmlPropertyData *property = _propertyCache->property(name, _object, context); + + if (_ddata->hasBindingBit(property->coreIndex)) + removeBindingOnProperty(_object, property->coreIndex); + + if (binding->value.type == QV4::CompiledData::Value::Type_Script) { + QV4::Function *runtimeFunction = jsUnit->runtimeFunctions[binding->value.compiledScriptIndex]; + QV4::FunctionObject *function = new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, runtimeFunction, qmlGlobal); + QQmlBinding *binding = new QQmlBinding(QV4::Value::fromObject(function), _object, context, + QString(), 0, 0); // ### + + binding->setTarget(_object, *property, context); + binding->addToObject(); + + createdDynamicBindings[i] = binding; + binding->m_mePtr = &createdDynamicBindings[i]; + continue; + } + + void *argv[] = { 0, 0, &propertyWriteStatus, &propertyWriteFlags }; + + // shortcuts +#if 0 + if (property->propType == QMetaType::Double && binding->value.type == QV4::CompiledData::Value::Type_Number) { + argv[0] = const_cast<double*>(&binding->value.d); + } else if (property->propType == QMetaType::Bool && binding->value.type == QV4::CompiledData::Value::Type_Boolean) { + argv[0] = const_cast<bool*>(&binding->value.b); + } else +#endif + { + // fallback + fallbackVariantValue = variantForBinding(property->propType, binding); + + if (property->propType == QMetaType::QVariant) + argv[0] = &fallbackVariantValue; + else + argv[0] = fallbackVariantValue.data(); + } + + QMetaObject::metacall(_object, QMetaObject::WriteProperty, property->coreIndex, argv); + } + + return createdDynamicBindings; +} + +void QmlObjectCreator::setupFunctions(const QV4::CompiledData::Object *obj, QV4::Object *qmlGlobal) +{ + QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(_object); + QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); + + const quint32 *functionIdx = obj->functionOffsetTable(); + for (quint32 i = 0; i < obj->nFunctions; ++i, ++functionIdx) { + QV4::Function *function = jsUnit->runtimeFunctions[*functionIdx]; + const QString name = function->name->toQString(); + + QQmlPropertyData *property = _propertyCache->property(name, _object, context); + if (!property->isVMEFunction()) + continue; + + QV4::FunctionObject *v4Function = new (v4->memoryManager) QV4::QmlBindingWrapper(v4->rootContext, function, qmlGlobal); + vme->setVmeMethod(property->coreIndex, QV4::Value::fromObject(v4Function)); + } +} + +QObject *QmlObjectCreator::create(int index, QObject *parent) +{ + const QV4::CompiledData::Object *obj = unit->objectAt(index); + + QQmlTypeNameCache::Result res = typeNameCache->query(stringAt(obj->inheritedTypeNameIndex)); + if (!res.isValid()) + return 0; + + QObject *result = res.type->create(); + // ### use no-event variant + if (parent) + result->setParent(parent); + + QQmlData *declarativeData = QQmlData::get(result, /*create*/true); + + qSwap(_object, result); + qSwap(_ddata, declarativeData); + + context->addObject(_object); + + // ### avoid _object->metaObject + QQmlPropertyCache *baseTypeCache = QQmlEnginePrivate::get(engine)->cache(_object->metaObject()); + baseTypeCache->addref(); + + QQmlPropertyCache *cache = 0; + QByteArray vmeMetaData; + bool needCustomMetaObject = createVMEMetaObjectAndPropertyCache(obj, baseTypeCache, &cache, &vmeMetaData); + cache->addref(); + + baseTypeCache->release(); + baseTypeCache = 0; + + qSwap(_propertyCache, cache); + + if (needCustomMetaObject) { + runtimeData->datas.append(vmeMetaData); + // install on _object + (void)new QQmlVMEMetaObject(_object, _propertyCache, reinterpret_cast<const QQmlVMEMetaData*>(runtimeData->datas.last().constData())); + if (_ddata->propertyCache) + _ddata->propertyCache->release(); + _ddata->propertyCache = _propertyCache; + _ddata->propertyCache->addref(); + } + + QV4::Value scopeObject = QV4::QmlContextWrapper::qmlScope(QV8Engine::get(engine), context, _object); + + QVector<QQmlAbstractBinding*> dynamicBindings = setupBindings(obj, scopeObject.asObject()); + setupFunctions(obj, scopeObject.asObject()); + + // ### do this later when requested + for (int i = 0; i < dynamicBindings.count(); ++i) { + QQmlAbstractBinding *b = dynamicBindings.at(i); + if (!b) + continue; + b->m_mePtr = 0; + QQmlData *data = QQmlData::get(b->object()); + Q_ASSERT(data); + data->clearPendingBindingBit(b->propertyIndex()); + b->setEnabled(true, QQmlPropertyPrivate::BypassInterceptor | + QQmlPropertyPrivate::DontRemoveBinding); + } + + qSwap(_propertyCache, cache); + qSwap(_ddata, declarativeData); + qSwap(_object, result); + + cache->release(); + + return result; +} + +// ### +#define COMPILE_EXCEPTION(token, desc) {} + +static QAtomicInt classIndexCounter(0); + +bool QmlObjectCreator::createVMEMetaObjectAndPropertyCache(const QV4::CompiledData::Object *obj, QQmlPropertyCache *baseTypeCache, QQmlPropertyCache **outputCache, QByteArray *vmeMetaObjectData) const +{ + if (!obj->nProperties) { + *outputCache = baseTypeCache; + return false; + } + + QQmlPropertyCache *cache = baseTypeCache->copyAndReserve(engine, obj->nProperties, /*methodCount*/0, /*signalCount*/0); + *outputCache = cache; + + vmeMetaObjectData->clear(); + + struct TypeData { + QV4::CompiledData::Property::Type dtype; + int metaType; + } builtinTypes[] = { + { QV4::CompiledData::Property::Var, QMetaType::QVariant }, + { QV4::CompiledData::Property::Variant, QMetaType::QVariant }, + { QV4::CompiledData::Property::Int, QMetaType::Int }, + { QV4::CompiledData::Property::Bool, QMetaType::Bool }, + { QV4::CompiledData::Property::Real, QMetaType::Double }, + { QV4::CompiledData::Property::String, QMetaType::QString }, + { QV4::CompiledData::Property::Url, QMetaType::QUrl }, + { QV4::CompiledData::Property::Color, QMetaType::QColor }, + { QV4::CompiledData::Property::Font, QMetaType::QFont }, + { QV4::CompiledData::Property::Time, QMetaType::QTime }, + { QV4::CompiledData::Property::Date, QMetaType::QDate }, + { QV4::CompiledData::Property::DateTime, QMetaType::QDateTime }, + { QV4::CompiledData::Property::Rect, QMetaType::QRectF }, + { QV4::CompiledData::Property::Point, QMetaType::QPointF }, + { QV4::CompiledData::Property::Size, QMetaType::QSizeF }, + { QV4::CompiledData::Property::Vector2D, QMetaType::QVector2D }, + { QV4::CompiledData::Property::Vector3D, QMetaType::QVector3D }, + { QV4::CompiledData::Property::Vector4D, QMetaType::QVector4D }, + { QV4::CompiledData::Property::Matrix4x4, QMetaType::QMatrix4x4 }, + { QV4::CompiledData::Property::Quaternion, QMetaType::QQuaternion } + }; + static const uint builtinTypeCount = sizeof(builtinTypes) / sizeof(TypeData); + + QByteArray newClassName; + + if (false /* ### compileState->root == obj && !compileState->nested*/) { +#if 0 // ### + QString path = output->url.path(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + if (lastSlash > -1) { + QString nameBase = path.mid(lastSlash + 1, path.length()-lastSlash-5); + if (!nameBase.isEmpty() && nameBase.at(0).isUpper()) + newClassName = nameBase.toUtf8() + "_QMLTYPE_" + + QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)); + } +#endif + } + if (newClassName.isEmpty()) { + newClassName = QQmlMetaObject(baseTypeCache).className(); + newClassName.append("_QML_"); + newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1))); + } + + cache->_dynamicClassName = newClassName; + + int aliasCount = 0; + int varPropCount = 0; + + const QV4::CompiledData::Property *p = obj->propertyTable(); + for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { + + if (p->type == QV4::CompiledData::Property::Alias) + aliasCount++; + else if (p->type == QV4::CompiledData::Property::Var) + varPropCount++; + +#if 0 // ### Do this elsewhere + // No point doing this for both the alias and non alias cases + QQmlPropertyData *d = property(obj, p->name); + if (d && d->isFinal()) + COMPILE_EXCEPTION(p, tr("Cannot override FINAL property")); +#endif + } + + typedef QQmlVMEMetaData VMD; + + QByteArray &dynamicData = *vmeMetaObjectData = QByteArray(sizeof(QQmlVMEMetaData) + + obj->nProperties * sizeof(VMD::PropertyData) + + obj->nFunctions * sizeof(VMD::MethodData) + + aliasCount * sizeof(VMD::AliasData), 0); + + int effectivePropertyIndex = cache->propertyIndexCacheStart; + int effectiveMethodIndex = cache->methodIndexCacheStart; + + // For property change signal override detection. + // We prepopulate a set of signal names which already exist in the object, + // and throw an error if there is a signal/method defined as an override. + QSet<QString> seenSignals; + seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged"); + QQmlPropertyCache *parentCache = cache; + while ((parentCache = parentCache->parent())) { + if (int pSigCount = parentCache->signalCount()) { + int pSigOffset = parentCache->signalOffset(); + for (int i = pSigOffset; i < pSigCount; ++i) { + QQmlPropertyData *currPSig = parentCache->signal(i); + // XXX TODO: find a better way to get signal name from the property data :-/ + for (QQmlPropertyCache::StringCache::ConstIterator iter = parentCache->stringCache.begin(); + iter != parentCache->stringCache.end(); ++iter) { + if (currPSig == (*iter).second) { + seenSignals.insert(iter.key()); + break; + } + } + } + } + } + + // First set up notify signals for properties - first normal, then var, then alias + enum { NSS_Normal = 0, NSS_Var = 1, NSS_Alias = 2 }; + for (int ii = NSS_Normal; ii <= NSS_Alias; ++ii) { // 0 == normal, 1 == var, 2 == alias + + if (ii == NSS_Var && varPropCount == 0) continue; + else if (ii == NSS_Alias && aliasCount == 0) continue; + + const QV4::CompiledData::Property *p = obj->propertyTable(); + for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { + if ((ii == NSS_Normal && (p->type == QV4::CompiledData::Property::Alias || + p->type == QV4::CompiledData::Property::Var)) || + ((ii == NSS_Var) && (p->type != QV4::CompiledData::Property::Var)) || + ((ii == NSS_Alias) && (p->type != QV4::CompiledData::Property::Alias))) + continue; + + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + + QString changedSigName = stringAt(p->nameIndex) + QLatin1String("Changed"); + seenSignals.insert(changedSigName); + + cache->appendSignal(changedSigName, flags, effectiveMethodIndex++); + } + } + + // Dynamic signals + for (uint i = 0; i < obj->nSignals; ++i) { + const QV4::CompiledData::Signal *s = obj->signalAt(i); + const int paramCount = s->nParameters; + + QList<QByteArray> names; + QVarLengthArray<int, 10> paramTypes(paramCount?(paramCount + 1):0); + + if (paramCount) { + paramTypes[0] = paramCount; + + for (int i = 0; i < paramCount; ++i) { + const QV4::CompiledData::Parameter *param = s->parameterAt(i); + names.append(stringAt(param->nameIndex).toUtf8()); + if (param->type < builtinTypeCount) { + // built-in type + paramTypes[i + 1] = builtinTypes[param->type].metaType; + } else { + // lazily resolved type + Q_ASSERT(param->type == QV4::CompiledData::Property::Custom); + QQmlType *qmltype = 0; + if (!imports.resolveType(stringAt(param->customTypeNameIndex), &qmltype, 0, 0, 0)) + COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(s->parameterTypeNames.at(i).toString())); + + if (qmltype->isComposite()) { + QQmlTypeData *tdata = QQmlEnginePrivate::get(engine)->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + QQmlCompiledData *data = tdata->compiledData(); + + paramTypes[i + 1] = data->metaTypeId; + + tdata->release(); + } else { + paramTypes[i + 1] = qmltype->typeId(); + } + } + } + } + + ((QQmlVMEMetaData *)dynamicData.data())->signalCount++; + + quint32 flags = QQmlPropertyData::IsSignal | QQmlPropertyData::IsFunction | + QQmlPropertyData::IsVMESignal; + if (paramCount) + flags |= QQmlPropertyData::HasArguments; + + QString signalName = stringAt(s->nameIndex); + if (seenSignals.contains(signalName)) { +#if 0 // ### + const QQmlScript::Object::DynamicSignal &currSig = *s; + COMPILE_EXCEPTION(&currSig, tr("Duplicate signal name: invalid override of property change signal or superclass signal")); +#endif + } + seenSignals.insert(signalName); + + cache->appendSignal(signalName, flags, effectiveMethodIndex++, + paramCount?paramTypes.constData():0, names); + } + + + // Dynamic slots + const quint32 *functionIndex = obj->functionOffsetTable(); + for (quint32 i = 0; i < obj->nFunctions; ++i, ++functionIndex) { + const QV4::CompiledData::Function *s = unit->header.functionAt(*functionIndex); + int paramCount = s->nFormals; + + quint32 flags = QQmlPropertyData::IsFunction | QQmlPropertyData::IsVMEFunction; + + if (paramCount) + flags |= QQmlPropertyData::HasArguments; + + QString slotName = stringAt(s->nameIndex); + if (seenSignals.contains(slotName)) { +#if 0 // ### + const QQmlScript::Object::DynamicSlot &currSlot = *s; + COMPILE_EXCEPTION(&currSlot, tr("Duplicate method name: invalid override of property change signal or superclass signal")); +#endif + } + // Note: we don't append slotName to the seenSignals list, since we don't + // protect against overriding change signals or methods with properties. + + const quint32 *formalsIndices = s->formalsTable(); + QList<QByteArray> parameterNames; + parameterNames.reserve(paramCount); + for (int i = 0; i < paramCount; ++i) + parameterNames << stringAt(formalsIndices[i]).toUtf8(); + + cache->appendMethod(slotName, flags, effectiveMethodIndex++, parameterNames); + } + + + // Dynamic properties (except var and aliases) + int effectiveSignalIndex = cache->signalHandlerIndexCacheStart; + /* const QV4::CompiledData::Property* */ p = obj->propertyTable(); + for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { + + if (p->type == QV4::CompiledData::Property::Alias || + p->type == QV4::CompiledData::Property::Var) + continue; + + int propertyType = 0; + int vmePropertyType = 0; + quint32 propertyFlags = 0; + + if (p->type < builtinTypeCount) { + propertyType = builtinTypes[p->type].metaType; + vmePropertyType = propertyType; + + if (p->type == QV4::CompiledData::Property::Variant) + propertyFlags |= QQmlPropertyData::IsQVariant; + } else { + Q_ASSERT(p->type == QV4::CompiledData::Property::CustomList || + p->type == QV4::CompiledData::Property::Custom); + + QQmlType *qmltype = 0; + if (!imports.resolveType(stringAt(p->customTypeNameIndex), &qmltype, 0, 0, 0)) + COMPILE_EXCEPTION(p, tr("Invalid property type")); + + Q_ASSERT(qmltype); + if (qmltype->isComposite()) { + QQmlTypeData *tdata = QQmlEnginePrivate::get(engine)->typeLoader.getType(qmltype->sourceUrl()); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + + QQmlCompiledData *data = tdata->compiledData(); + + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = data->metaTypeId; + vmePropertyType = QMetaType::QObjectStar; + } else { + propertyType = data->listMetaTypeId; + vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >(); + } + + tdata->release(); + } else { + if (p->type == QV4::CompiledData::Property::Custom) { + propertyType = qmltype->typeId(); + vmePropertyType = QMetaType::QObjectStar; + } else { + propertyType = qmltype->qListTypeId(); + vmePropertyType = qMetaTypeId<QQmlListProperty<QObject> >(); + } + } + + if (p->type == QV4::CompiledData::Property::Custom) + propertyFlags |= QQmlPropertyData::IsQObjectDerived; + else + propertyFlags |= QQmlPropertyData::IsQList; + } + + if ((!p->flags & QV4::CompiledData::Property::IsReadOnly) && p->type != QV4::CompiledData::Property::CustomList) + propertyFlags |= QQmlPropertyData::IsWritable; + + + QString propertyName = stringAt(p->nameIndex); + if (i == obj->indexOfDefaultProperty) cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + propertyType, effectiveSignalIndex); + + effectiveSignalIndex++; + + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + (vmd->propertyData() + vmd->propertyCount)->propertyType = vmePropertyType; + vmd->propertyCount++; + } + + // Now do var properties + /* const QV4::CompiledData::Property* */ p = obj->propertyTable(); + for (quint32 i = 0; i < obj->nProperties; ++i, ++p) { + + if (p->type != QV4::CompiledData::Property::Var) + continue; + + quint32 propertyFlags = QQmlPropertyData::IsVarProperty; + if (!p->flags & QV4::CompiledData::Property::IsReadOnly) + propertyFlags |= QQmlPropertyData::IsWritable; + + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + (vmd->propertyData() + vmd->propertyCount)->propertyType = QMetaType::QVariant; + vmd->propertyCount++; + ((QQmlVMEMetaData *)dynamicData.data())->varPropertyCount++; + + QString propertyName = stringAt(p->nameIndex); + if (i == obj->indexOfDefaultProperty) cache->_defaultPropertyName = propertyName; + cache->appendProperty(propertyName, propertyFlags, effectivePropertyIndex++, + QMetaType::QVariant, effectiveSignalIndex); + + effectiveSignalIndex++; + } + + // Alias property count. Actual data is setup in buildDynamicMetaAliases + ((QQmlVMEMetaData *)dynamicData.data())->aliasCount = aliasCount; + + // Dynamic slot data - comes after the property data + /*const quint32* */functionIndex = obj->functionOffsetTable(); + for (quint32 i = 0; i < obj->nFunctions; ++i, ++functionIndex) { + const QV4::CompiledData::Function *s = unit->header.functionAt(*functionIndex); + + VMD::MethodData methodData = { int(s->nFormals), + /* body offset*/0, + /* body length*/0, + /* s->location.start.line */0 }; // ### + + VMD *vmd = (QQmlVMEMetaData *)dynamicData.data(); + VMD::MethodData &md = *(vmd->methodData() + vmd->methodCount); + vmd->methodCount++; + md = methodData; + } + + return true; +} + +QVariant QmlObjectCreator::variantForBinding(int expectedMetaType, const QV4::CompiledData::Binding *binding) const +{ + QVariant result; + + switch (expectedMetaType) { + case QMetaType::QString: + result = valueAsString(&binding->value); + break; + case QMetaType::Bool: + result = valueAsBoolean(&binding->value); + break; + case QMetaType::Double: + result = valueAsNumber(&binding->value); + break; + case QMetaType::Int: + result = (int)valueAsNumber(&binding->value); + break; + case QVariant::Color: { + bool ok = false; + result = QQmlStringConverters::colorFromString(valueAsString(&binding->value), &ok); + if (!ok) { + // ### compile error + } + break; + } + default: + QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(expectedMetaType); + if (converter) { + result = converter(valueAsString(&binding->value)); + } else { + if (expectedMetaType == QMetaType::QVariant) + result = QVariant(); + else + result = QVariant(expectedMetaType, (void*)0); + } + break; + } + return result; +} + +QString QmlObjectCreator::valueAsString(const QV4::CompiledData::Value *value) const +{ + switch (value->type) { + case QV4::CompiledData::Value::Type_Script: + case QV4::CompiledData::Value::Type_String: + return stringAt(value->stringIndex); + case QV4::CompiledData::Value::Type_Boolean: + return value->b ? QStringLiteral("true") : QStringLiteral("false"); + case QV4::CompiledData::Value::Type_Number: + return QString::number(value->d); + case QV4::CompiledData::Value::Type_Invalid: + return QString(); + default: + break; + } + return QString(); +} + +double QmlObjectCreator::valueAsNumber(const QV4::CompiledData::Value *value) +{ + if (value->type == QV4::CompiledData::Value::Type_Number) + return value->d; + return 0.0; +} + +bool QmlObjectCreator::valueAsBoolean(const QV4::CompiledData::Value *value) +{ + if (value->type == QV4::CompiledData::Value::Type_Boolean) + return value->b; + return false; +} + diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h new file mode 100644 index 0000000000..6a2b28c35d --- /dev/null +++ b/src/qml/qml/qqmlobjectcreator_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, 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, Digia gives you certain additional +** rights. These rights are described in the Digia 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. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QQMLOBJECTCREATOR_P_H +#define QQMLOBJECTCREATOR_P_H + +#include <private/qqmlimport_p.h> +#include <private/qqmltypenamecache_p.h> +#include <private/qv4compileddata_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlAbstractBinding; + +namespace QtQml { + +struct Q_QML_EXPORT QmlObjectCreator +{ + QmlObjectCreator(QQmlEngine *engine, + // extra data/output stored in these two + QQmlContextData *contextData, + QQmlCompiledData *runtimeData); + + QObject *create(QObject *parent = 0) + { return create(unit->indexOfRootObject); } + QObject *create(int index, QObject *parent = 0); + + QList<QQmlError> errors; + + bool createVMEMetaObjectAndPropertyCache(const QV4::CompiledData::Object *obj, QQmlPropertyCache *baseTypeCache, QQmlPropertyCache **cache, QByteArray *vmeMetaObjectData) const; +private: + QString stringAt(int idx) const { return unit->header.stringAt(idx); } + + QVector<QQmlAbstractBinding *> setupBindings(const QV4::CompiledData::Object *obj, QV4::Object *qmlGlobal); + void setupFunctions(const QV4::CompiledData::Object *obj, QV4::Object *qmlGlobal); + + QVariant variantForBinding(int expectedMetaType, const QV4::CompiledData::Binding *binding) const; + + QString valueAsString(const QV4::CompiledData::Value *value) const; + static double valueAsNumber(const QV4::CompiledData::Value *value); + static bool valueAsBoolean(const QV4::CompiledData::Value *value); + + QQmlEngine *engine; + const QV4::CompiledData::QmlUnit *unit; + const QV4::CompiledData::CompilationUnit *jsUnit; + QQmlContextData *context; + QQmlTypeNameCache *typeNameCache; + QQmlCompiledData *runtimeData; + QQmlImports imports; + + QObject *_object; + QQmlData *_ddata; + QQmlPropertyCache *_propertyCache; +}; + +} // end namespace QtQml + +QT_END_NAMESPACE + +#endif // QQMLOBJECTCREATOR_P_H diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h index 21fee28b02..0fd9252754 100644 --- a/src/qml/qml/qqmlpropertycache_p.h +++ b/src/qml/qml/qqmlpropertycache_p.h @@ -66,6 +66,10 @@ QT_BEGIN_NAMESPACE +namespace QtQml { +struct QmlObjectCreator; +} + class QV8Engine; class QMetaProperty; class QQmlEngine; @@ -339,6 +343,7 @@ protected: private: friend class QQmlEnginePrivate; friend class QQmlCompiler; + friend struct QtQml::QmlObjectCreator; inline QQmlPropertyCache *copy(int reserve); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 3531e89779..cd57704771 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1900,6 +1900,7 @@ QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) : QQmlTypeLoader::Blob(url, QmlFile, manager), m_typesResolved(false), m_compiledData(0), m_implicitImport(0), m_implicitImportLoaded(false) { + m_useNewCompiler = QQmlEnginePrivate::get(manager->engine())->useNewCompiler; } QQmlTypeData::~QQmlTypeData() @@ -2031,9 +2032,17 @@ void QQmlTypeData::dataReceived(const Data &data) if (data.isFile()) preparseData = data.asFile()->metaData(QLatin1String("qml:preparse")); - if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) { - setError(scriptParser.errors()); - return; + if (m_useNewCompiler) { + QQmlCodeGenerator compiler; + if (!compiler.generateFromQml(code, finalUrl(), finalUrlString(), &parsedQML)) { + setError(compiler.errors); + return; + } + } else { + if (!scriptParser.parse(code, preparseData, finalUrl(), finalUrlString())) { + setError(scriptParser.errors()); + return; + } } m_imports.setBaseUrl(finalUrl(), finalUrlString()); @@ -2063,7 +2072,34 @@ void QQmlTypeData::dataReceived(const Data &data) QList<QQmlError> errors; - foreach (const QQmlScript::Import &import, scriptParser.imports()) { + // ### convert to use new data structure once old compiler is gone. + QList<QQmlScript::Import> imports; + if (m_useNewCompiler) { + imports.reserve(parsedQML.imports.size()); + foreach (QV4::CompiledData::Import *i, parsedQML.imports) { + QQmlScript::Import import; + import.uri = parsedQML.stringAt(i->uriIndex); + import.qualifier = parsedQML.stringAt(i->qualifierIndex); + import.majorVersion = i->majorVersion; + import.minorVersion = i->minorVersion; + import.location.start.line = i->location.line; + import.location.start.column = i->location.column; + + switch (i->type) { + case QV4::CompiledData::Import::ImportFile: import.type = QQmlScript::Import::File; break; + case QV4::CompiledData::Import::ImportLibrary: import.type = QQmlScript::Import::Library; break; + case QV4::CompiledData::Import::ImportScript: import.type = QQmlScript::Import::Script; break; + default: break; + } + + + imports << import; + } + } else { + imports = scriptParser.imports(); + } + + foreach (const QQmlScript::Import &import, imports) { if (!addImport(import, &errors)) { Q_ASSERT(errors.size()); QQmlError error(errors.takeFirst()); @@ -2124,11 +2160,36 @@ void QQmlTypeData::compile() QQmlCompilingProfiler prof(m_compiledData->name); - QQmlCompiler compiler(&scriptParser._pool); - if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) { - setError(compiler.errors()); - m_compiledData->release(); - m_compiledData = 0; + if (m_useNewCompiler) { + JSCodeGen jsCodeGen; + jsCodeGen.generateJSCodeForFunctionsAndBindings(finalUrlString(), &parsedQML); + + QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); + + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &parsedQML.jsModule, &parsedQML.jsGenerator)); + isel->setUseFastLookups(false); + QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/false); + + QmlUnitGenerator qmlGenerator; + QV4::CompiledData::QmlUnit *qmlUnit = qmlGenerator.generate(parsedQML); + + if (jsUnit) { + Q_ASSERT(!jsUnit->data); + jsUnit->ownsData = false; + jsUnit->data = &qmlUnit->header; + } + + m_compiledData->compilationUnit = jsUnit; + if (m_compiledData->compilationUnit) + m_compiledData->compilationUnit->ref(); + m_compiledData->qmlUnit = qmlUnit; // ownership transferred to m_compiledData + } else { + QQmlCompiler compiler(&scriptParser._pool); + if (!compiler.compile(typeLoader()->engine(), this, m_compiledData)) { + setError(compiler.errors()); + m_compiledData->release(); + m_compiledData = 0; + } } } diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index abe545fc8f..9e71bab7b0 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -68,6 +68,7 @@ #include <private/qqmlbundle_p.h> #include <private/qflagpointer_p.h> #include <private/qqmlabstracturlinterceptor_p.h> +#include <private/qqmlcodegenerator_p.h> #include <private/qv4value_p.h> #include <private/qv4script_p.h> @@ -446,7 +447,11 @@ private: virtual void scriptImported(QQmlScriptBlob *blob, const QQmlScript::Location &location, const QString &qualifier, const QString &nameSpace); + // --- old compiler QQmlScript::Parser scriptParser; + // --- new compiler + QtQml::ParsedQML parsedQML; + // --- QList<ScriptReference> m_scripts; @@ -454,6 +459,7 @@ private: QList<TypeReference> m_types; bool m_typesResolved:1; + bool m_useNewCompiler:1; QQmlCompiledData *m_compiledData; |