diff options
Diffstat (limited to 'src/qml')
48 files changed, 968 insertions, 471 deletions
diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h index 63fd4b0dac..0be6ca96ea 100644 --- a/src/qml/animations/qabstractanimationjob_p.h +++ b/src/qml/animations/qabstractanimationjob_p.h @@ -56,6 +56,8 @@ #include <QtCore/private/qabstractanimation_p.h> #include <vector> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class QAnimationGroupJob; diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h index fb567dc019..b01b2f3b36 100644 --- a/src/qml/animations/qanimationgroupjob_p.h +++ b/src/qml/animations/qanimationgroupjob_p.h @@ -54,6 +54,8 @@ #include "private/qabstractanimationjob_p.h" #include <QtCore/qdebug.h> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QAnimationGroupJob : public QAbstractAnimationJob diff --git a/src/qml/animations/qanimationjobutil_p.h b/src/qml/animations/qanimationjobutil_p.h index 0bb9e83b2d..e3d6fe9178 100644 --- a/src/qml/animations/qanimationjobutil_p.h +++ b/src/qml/animations/qanimationjobutil_p.h @@ -51,6 +51,8 @@ // We mean it. // +QT_REQUIRE_CONFIG(qml_animation); + #define RETURN_IF_DELETED(func) \ { \ bool *prevWasDeleted = m_wasDeleted; \ diff --git a/src/qml/animations/qcontinuinganimationgroupjob_p.h b/src/qml/animations/qcontinuinganimationgroupjob_p.h index baf4ff1ae5..c67b8d39ad 100644 --- a/src/qml/animations/qcontinuinganimationgroupjob_p.h +++ b/src/qml/animations/qcontinuinganimationgroupjob_p.h @@ -53,6 +53,8 @@ #include "private/qanimationgroupjob_p.h" +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QContinuingAnimationGroupJob : public QAnimationGroupJob diff --git a/src/qml/animations/qparallelanimationgroupjob_p.h b/src/qml/animations/qparallelanimationgroupjob_p.h index 67ba626247..0265fe3274 100644 --- a/src/qml/animations/qparallelanimationgroupjob_p.h +++ b/src/qml/animations/qparallelanimationgroupjob_p.h @@ -53,6 +53,8 @@ #include "private/qanimationgroupjob_p.h" +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QParallelAnimationGroupJob : public QAnimationGroupJob diff --git a/src/qml/animations/qpauseanimationjob_p.h b/src/qml/animations/qpauseanimationjob_p.h index d0e8d57fc7..6c9bbf0dab 100644 --- a/src/qml/animations/qpauseanimationjob_p.h +++ b/src/qml/animations/qpauseanimationjob_p.h @@ -53,6 +53,8 @@ #include <private/qanimationgroupjob_p.h> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QPauseAnimationJob : public QAbstractAnimationJob diff --git a/src/qml/animations/qsequentialanimationgroupjob_p.h b/src/qml/animations/qsequentialanimationgroupjob_p.h index 800f0c3b90..13f9806be1 100644 --- a/src/qml/animations/qsequentialanimationgroupjob_p.h +++ b/src/qml/animations/qsequentialanimationgroupjob_p.h @@ -53,6 +53,8 @@ #include <private/qanimationgroupjob_p.h> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class QPauseAnimationJob; diff --git a/src/qml/configure.json b/src/qml/configure.json index b744ea6948..47b897ce27 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -39,6 +39,47 @@ "features.xmlstreamwriter" ], "output": [ "privateFeature" ] + }, + "qml-devtools": { + "label": "QML Development Tools", + "purpose": "Provides the QmlDevtools library and various utilities.", + "section": "QML", + "output": [ "privateFeature" ] + }, + "qml-sequence-object": { + "label": "QML sequence object", + "purpose": "Supports mapping sequence types into QML.", + "section": "QML", + "output": [ "privateFeature" ] + }, + "qml-list-model": { + "label": "QML list model", + "purpose": "Provides the ListModel QML type.", + "section": "QML", + "output": [ "privateFeature" ] + }, + "qml-xml-http-request": { + "label": "QML XML http request", + "purpose": "Provides support for sending XML http requests.", + "section": "QML", + "condition": [ + "features.xmlstreamreader", + "features.qml-network" + ], + "output": [ "privateFeature" ] + }, + "qml-locale": { + "label": "QML Locale", + "purpose": "Provides support for locales in QML.", + "section": "QML", + "output": [ "privateFeature" ] + }, + "qml-animation": { + "label": "QML Animations", + "purpose": "Provides support for animations and timers in QML.", + "section": "QML", + "condition": "features.animation", + "output": [ "privateFeature" ] } }, @@ -47,7 +88,11 @@ "section": "Qt QML", "entries": [ "qml-network", - "qml-debug" + "qml-debug", + "qml-sequence-object", + "qml-list-model", + "qml-xml-http-request", + "qml-locale" ] } ] diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h index c63e694c7e..f39f8fccd2 100644 --- a/src/qml/debugger/qqmlabstractprofileradapter_p.h +++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h @@ -73,13 +73,13 @@ public: ~QQmlAbstractProfilerAdapter() override {} void setService(QQmlProfilerService *new_service) { service = new_service; } - virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) = 0; + virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) = 0; void startProfiling(quint64 features); void stopProfiling(); - void reportData(bool trackLocations) { emit dataRequested(trackLocations); } + void reportData() { emit dataRequested(); } void stopWaiting() { waiting = false; } void startWaiting() { waiting = true; } @@ -96,7 +96,7 @@ signals: void profilingDisabled(); void profilingDisabledWhileWaiting(); - void dataRequested(bool trackLocations); + void dataRequested(); void referenceTimeKnown(const QElapsedTimer &timer); protected: diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp index 8c0bd73822..da0b14dd85 100644 --- a/src/qml/debugger/qqmlprofiler.cpp +++ b/src/qml/debugger/qqmlprofiler.cpp @@ -59,19 +59,18 @@ void QQmlProfiler::startProfiling(quint64 features) void QQmlProfiler::stopProfiling() { featuresEnabled = false; - reportData(true); + reportData(); m_locations.clear(); } -void QQmlProfiler::reportData(bool trackLocations) +void QQmlProfiler::reportData() { LocationHash resolved; resolved.reserve(m_locations.size()); for (auto it = m_locations.begin(), end = m_locations.end(); it != end; ++it) { - if (!trackLocations || !it->sent) { + if (!it->sent) { resolved.insert(it.key(), it.value()); - if (trackLocations) - it->sent = true; + it->sent = true; } } diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index deb4d107d6..44d208db41 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -383,7 +383,7 @@ public: void startProfiling(quint64 features); void stopProfiling(); - void reportData(bool trackLocations); + void reportData(); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index c483af638b..924de14bc7 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -304,9 +304,9 @@ QJSEngine::QJSEngine() QJSEngine::QJSEngine(QObject *parent) : QObject(*new QJSEnginePrivate, parent) - , m_v4Engine(new QV4::ExecutionEngine) + , m_v4Engine(new QV4::ExecutionEngine(this)) { - m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine); + m_v4Engine->v8Engine = new QV8Engine(m_v4Engine); checkForApplicationInstance(); QJSEnginePrivate::addToDebugServer(this); @@ -317,9 +317,9 @@ QJSEngine::QJSEngine(QObject *parent) */ QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent) : QObject(dd, parent) - , m_v4Engine(new QV4::ExecutionEngine) + , m_v4Engine(new QV4::ExecutionEngine(this)) { - m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine); + m_v4Engine->v8Engine = new QV8Engine(m_v4Engine); checkForApplicationInstance(); } diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 4bc877bd9d..1ec00cc744 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -36,7 +36,6 @@ SOURCES += \ $$PWD/qv4runtimecodegen.cpp \ $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ - $$PWD/qv4sequenceobject.cpp \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ $$PWD/qv4arraybuffer.cpp \ @@ -89,7 +88,6 @@ HEADERS += \ $$PWD/qv4script_p.h \ $$PWD/qv4scopedvalue_p.h \ $$PWD/qv4executableallocator_p.h \ - $$PWD/qv4sequenceobject_p.h \ $$PWD/qv4include_p.h \ $$PWD/qv4qobjectwrapper_p.h \ $$PWD/qv4profiling_p.h \ @@ -98,6 +96,14 @@ HEADERS += \ $$PWD/qv4dataview_p.h \ $$PWD/qv4vme_moth_p.h +qtConfig(qml-sequence-object) { + HEADERS += \ + $$PWD/qv4sequenceobject_p.h + + SOURCES += \ + $$PWD/qv4sequenceobject.cpp +} + } diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 5f59e1e809..b697a2081b 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -63,7 +63,11 @@ #include "qv4debugging_p.h" #include "qv4profiling_p.h" #include "qv4executableallocator_p.h" + +#if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" +#endif + #include "qv4qobjectwrapper_p.h" #include "qv4memberdata_p.h" #include "qv4arraybuffer_p.h" @@ -76,7 +80,9 @@ #include <private/qqmlvaluetype_p.h> #include <private/qqmllistwrapper_p.h> #include <private/qqmllist_p.h> +#if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> +#endif #include <QtCore/QTextStream> #include <QDateTime> @@ -110,7 +116,7 @@ ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const #ifdef V4_BOOTSTRAP QJSEngine *ExecutionEngine::jsEngine() const { - return v8Engine->publicEngine(); + return publicEngine; } QQmlEngine *ExecutionEngine::qmlEngine() const @@ -121,7 +127,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const qint32 ExecutionEngine::maxCallDepth = -1; -ExecutionEngine::ExecutionEngine() +ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) : executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) , bumperPointerAllocator(new WTF::BumpPointerAllocator) @@ -129,6 +135,7 @@ ExecutionEngine::ExecutionEngine() , gcStack(new WTF::PageAllocation) , globalCode(nullptr) , v8Engine(nullptr) + , publicEngine(jsEngine) , argumentsAccessors(nullptr) , nArgumentsAccessors(0) , m_engineId(engineSerial.fetchAndAddOrdered(1)) @@ -346,8 +353,10 @@ ExecutionEngine::ExecutionEngine() jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>(); Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d()); +#if QT_CONFIG(qml_sequence_object) ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this)); jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic, SequencePrototype::defaultPrototype(this))); +#endif ExecutionContext *global = rootContext(); jsObjects[Object_Ctor] = memoryManager->allocObject<ObjectCtor>(global); @@ -382,10 +391,11 @@ ExecutionEngine::ExecutionEngine() static_cast<SyntaxErrorPrototype *>(syntaxErrorPrototype())->init(this, syntaxErrorCtor()); static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor()); static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor()); - static_cast<VariantPrototype *>(variantPrototype())->init(); - sequencePrototype()->cast<SequencePrototype>()->init(); +#if QT_CONFIG(qml_sequence_object) + sequencePrototype()->cast<SequencePrototype>()->init(); +#endif // typed arrays @@ -524,7 +534,7 @@ void ExecutionEngine::initRootContext() jsObjects[IntegerNull] = Encode((int)0); } -InternalClass *ExecutionEngine::newClass(const InternalClass &other) +InternalClass *ExecutionEngine::newClass(InternalClass *other) { return new (classPool) InternalClass(other); } @@ -1141,8 +1151,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return v->toVariant(); } else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) { return l->toVariant(); - } else if (object->isListType()) +#if QT_CONFIG(qml_sequence_object) + } else if (object->isListType()) { return QV4::SequencePrototype::toVariant(object); +#endif + } } if (value.as<ArrayObject>()) { @@ -1165,10 +1178,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return QVariant::fromValue(QV4::JsonObject::toJsonArray(a)); } +#if QT_CONFIG(qml_sequence_object) bool succeeded = false; QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded); if (succeeded) return retn; +#endif } if (value.isUndefined()) @@ -1188,8 +1203,10 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return str.at(0); return str; } +#if QT_CONFIG(qml_locale) if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>()) return *ld->d()->locale; +#endif if (const QV4::DateObject *d = value.as<DateObject>()) return d->toQDateTime(); if (const ArrayBuffer *d = value.as<ArrayBuffer>()) @@ -1345,6 +1362,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr))); case QMetaType::QObjectStar: return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr)); +#if QT_CONFIG(qml_sequence_object) case QMetaType::QStringList: { bool succeeded = false; @@ -1354,6 +1372,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return retn->asReturnedValue(); return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr))); } +#endif case QMetaType::QVariantList: return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr)); case QMetaType::QVariantMap: @@ -1364,8 +1383,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr)); case QMetaType::QJsonArray: return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr)); +#if QT_CONFIG(qml_locale) case QMetaType::QLocale: return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr)); +#endif default: break; } @@ -1405,10 +1426,12 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) if (objOk) return QV4::QObjectWrapper::wrap(this, obj); +#if QT_CONFIG(qml_sequence_object) bool succeeded = false; QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded)); if (succeeded) return retn->asReturnedValue(); +#endif if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type)) return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 5edf89f720..d21b623a1e 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -88,7 +88,6 @@ struct CompilationUnit; } struct Function; -struct InternalClass; struct InternalClassPool; struct Q_QML_EXPORT CppStackFrame { @@ -153,10 +152,11 @@ public: QJSEngine *jsEngine() const; QQmlEngine *qmlEngine() const; #else // !V4_BOOTSTRAP - QJSEngine *jsEngine() const { return v8Engine->publicEngine(); } + QJSEngine *jsEngine() const { return publicEngine; } QQmlEngine *qmlEngine() const { return v8Engine ? v8Engine->engine() : nullptr; } #endif // V4_BOOTSTRAP QV8Engine *v8Engine; + QJSEngine *publicEngine; enum JSObjects { RootContext, @@ -178,7 +178,9 @@ public: TypeErrorProto, URIErrorProto, VariantProto, +#if QT_CONFIG(qml_sequence_object) SequenceProto, +#endif ArrayBufferProto, DataViewProto, ValueTypeProto, @@ -247,7 +249,9 @@ public: Object *typeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeErrorProto); } Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); } Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); } +#if QT_CONFIG(qml_sequence_object) Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); } +#endif Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); } Object *dataViewPrototype() const { return reinterpret_cast<Object *>(jsObjects + DataViewProto); } @@ -374,7 +378,7 @@ public: int internalClassIdCount = 0; - ExecutionEngine(); + ExecutionEngine(QJSEngine *jsEngine = nullptr); ~ExecutionEngine(); #if !QT_CONFIG(qml_debug) @@ -454,7 +458,7 @@ public: void initRootContext(); - InternalClass *newClass(const InternalClass &other); + InternalClass *newClass(InternalClass *other); StackTrace exceptionStackTrace; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 9da854e7d7..69ca62cb5c 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -74,24 +74,8 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) // fill up to max 50% bool grow = (d->alloc <= d->size*2); - if (classSize < d->size || grow) { - PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); - for (int i = 0; i < d->alloc; ++i) { - const Entry &e = d->entries[i]; - if (!e.identifier || e.index >= static_cast<unsigned>(classSize)) - continue; - uint idx = e.identifier->hashValue % dd->alloc; - while (dd->entries[idx].identifier) { - ++idx; - idx %= dd->alloc; - } - dd->entries[idx] = e; - } - dd->size = classSize; - Q_ASSERT(d->refCount > 1); - --d->refCount; - d = dd; - } + if (classSize < d->size || grow) + detach(grow, classSize); uint idx = entry.identifier->hashValue % d->alloc; while (d->entries[idx].identifier) { @@ -102,11 +86,52 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize) ++d->size; } +int PropertyHash::removeIdentifier(Identifier *identifier, int classSize) +{ + detach(false, classSize); + uint idx = identifier->hashValue % d->alloc; + while (1) { + if (d->entries[idx].identifier == identifier) { + int val = d->entries[idx].index; + d->entries[idx] = { nullptr, 0 }; + return val; + } + + ++idx; + idx %= d->alloc; + } + Q_UNREACHABLE(); +} + +void PropertyHash::detach(bool grow, int classSize) +{ + if (d->refCount == 1 && !grow) + return; + + PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits); + for (int i = 0; i < d->alloc; ++i) { + const Entry &e = d->entries[i]; + if (!e.identifier || e.index >= static_cast<unsigned>(classSize)) + continue; + uint idx = e.identifier->hashValue % dd->alloc; + while (dd->entries[idx].identifier) { + ++idx; + idx %= dd->alloc; + } + dd->entries[idx] = e; + } + dd->size = classSize; + if (!--d->refCount) + delete d; + d = dd; +} + InternalClass::InternalClass(ExecutionEngine *engine) : engine(engine) , vtable(nullptr) , prototype(nullptr) + , parent(nullptr) , m_sealed(nullptr) , m_frozen(nullptr) , size(0) @@ -116,19 +141,20 @@ InternalClass::InternalClass(ExecutionEngine *engine) } -InternalClass::InternalClass(const QV4::InternalClass &other) +InternalClass::InternalClass(QV4::InternalClass *other) : QQmlJS::Managed() - , engine(other.engine) - , vtable(other.vtable) - , prototype(other.prototype) - , propertyTable(other.propertyTable) - , nameMap(other.nameMap) - , propertyData(other.propertyData) + , engine(other->engine) + , vtable(other->vtable) + , prototype(other->prototype) + , parent(other) + , propertyTable(other->propertyTable) + , nameMap(other->nameMap) + , propertyData(other->propertyData) , m_sealed(nullptr) , m_frozen(nullptr) - , size(other.size) - , extensible(other.extensible) - , isUsedAsProto(other.isUsedAsProto) + , size(other->size) + , extensible(other->extensible) + , isUsedAsProto(other->isUsedAsProto) { id = engine->newInternalClassId(); } @@ -183,6 +209,15 @@ InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalC } } +static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e) +{ + // add a dummy entry, since we need two entries for accessors + newClass->propertyTable.addEntry(e, newClass->size); + newClass->nameMap.add(newClass->size, nullptr); + newClass->propertyData.add(newClass->size, PropertyAttributes()); + ++newClass->size; +} + InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttributes data, uint *index) { data.resolve(); @@ -201,14 +236,34 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri return t.lookup; // create a new class and add it to the tree - InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); - newClass = newClass->changePrototype(prototype); - for (uint i = 0; i < size; ++i) { - if (i == idx) { - newClass = newClass->addMember(nameMap.at(i), data); - } else if (!propertyData.at(i).isEmpty()) { - newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); + InternalClass *newClass = engine->newClass(this); + if (data.isAccessor() != propertyData.at(idx).isAccessor()) { + // this changes the layout of the class, so we need to rebuild the data + newClass->propertyTable = PropertyHash(); + newClass->nameMap = SharedInternalClassData<Identifier *>(); + newClass->propertyData = SharedInternalClassData<PropertyAttributes>(); + newClass->size = 0; + for (uint i = 0; i < size; ++i) { + Identifier *identifier = nameMap.at(i); + PropertyHash::Entry e = { identifier, newClass->size }; + if (!identifier) + e.identifier = nameMap.at(i - 1); + newClass->propertyTable.addEntry(e, newClass->size); + newClass->nameMap.add(newClass->size, identifier); + if (i == idx) { + newClass->propertyData.add(newClass->size, data); + ++newClass->size; + if (data.isAccessor()) + addDummyEntry(newClass, e); + else + ++i; + } else { + newClass->propertyData.add(newClass->size, propertyData.at(i)); + ++newClass->size; + } } + } else { + newClass->propertyData.set(idx, data); } t.lookup = newClass; @@ -231,18 +286,8 @@ InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto) return t.lookup; // create a new class and add it to the tree - InternalClass *newClass; - if (!size && !prototype) { - newClass = engine->newClass(*this); - newClass->prototype = proto; - } else { - newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); - newClass = newClass->changePrototype(proto); - for (uint i = 0; i < size; ++i) { - if (!propertyData.at(i).isEmpty()) - newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); - } - } + InternalClass *newClass = engine->newClass(this); + newClass->prototype = proto; t.lookup = newClass; @@ -261,18 +306,8 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt) return t.lookup; // create a new class and add it to the tree - InternalClass *newClass; - if (this == engine->internalClasses[EngineBase::Class_Empty]) { - newClass = engine->newClass(*this); - newClass->vtable = vt; - } else { - newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vt); - newClass = newClass->changePrototype(prototype); - for (uint i = 0; i < size; ++i) { - if (!propertyData.at(i).isEmpty()) - newClass = newClass->addMember(nameMap.at(i), propertyData.at(i)); - } - } + InternalClass *newClass = engine->newClass(this); + newClass->vtable = vt; t.lookup = newClass; Q_ASSERT(t.lookup); @@ -290,7 +325,7 @@ InternalClass *InternalClass::nonExtensible() if (t.lookup) return t.lookup; - InternalClass *newClass = engine->newClass(*this); + InternalClass *newClass = engine->newClass(this); newClass->extensible = false; t.lookup = newClass; @@ -343,20 +378,15 @@ InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttr return t.lookup; // create a new class and add it to the tree - InternalClass *newClass = engine->newClass(*this); + InternalClass *newClass = engine->newClass(this); PropertyHash::Entry e = { identifier, newClass->size }; newClass->propertyTable.addEntry(e, newClass->size); newClass->nameMap.add(newClass->size, identifier); newClass->propertyData.add(newClass->size, data); ++newClass->size; - if (data.isAccessor()) { - // add a dummy entry, since we need two entries for accessors - newClass->propertyTable.addEntry(e, newClass->size); - newClass->nameMap.add(newClass->size, 0); - newClass->propertyData.add(newClass->size, PropertyAttributes()); - ++newClass->size; - } + if (data.isAccessor()) + addDummyEntry(newClass, e); t.lookup = newClass; Q_ASSERT(t.lookup); @@ -366,36 +396,25 @@ InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttr void InternalClass::removeMember(Object *object, Identifier *id) { InternalClass *oldClass = object->internalClass(); - uint propIdx = oldClass->propertyTable.lookup(id); - Q_ASSERT(propIdx < oldClass->size); + Q_ASSERT(oldClass->propertyTable.lookup(id) < oldClass->size); - Transition temp = { { id }, nullptr, -1 }; + Transition temp = { { id }, nullptr, Transition::RemoveMember }; Transition &t = object->internalClass()->lookupOrInsertTransition(temp); - bool accessor = oldClass->propertyData.at(propIdx).isAccessor(); - - if (t.lookup) { - object->setInternalClass(t.lookup); - } else { + if (!t.lookup) { // create a new class and add it to the tree - InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable); - newClass = newClass->changePrototype(oldClass->prototype); - for (uint i = 0; i < oldClass->size; ++i) { - if (i == propIdx) - continue; - if (!oldClass->propertyData.at(i).isEmpty()) - newClass = newClass->addMember(oldClass->nameMap.at(i), oldClass->propertyData.at(i)); - } - object->setInternalClass(newClass); + InternalClass *newClass = oldClass->engine->newClass(oldClass); + // simply make the entry inaccessible + int idx = newClass->propertyTable.removeIdentifier(id, oldClass->size); + newClass->nameMap.set(idx, nullptr); + newClass->propertyData.set(idx, PropertyAttributes()); + t.lookup = newClass; + Q_ASSERT(t.lookup); } + object->setInternalClass(t.lookup); - Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1)); - - // remove the entry in the property data - removeFromPropertyData(object, propIdx, accessor); - - t.lookup = object->internalClass(); - Q_ASSERT(t.lookup); + // we didn't remove the data slot, just made it inaccessible + Q_ASSERT(object->internalClass()->size == oldClass->size); } uint InternalClass::find(const String *string) @@ -415,16 +434,32 @@ InternalClass *InternalClass::sealed() if (m_sealed) return m_sealed; - m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable); - m_sealed = m_sealed->changePrototype(prototype); + bool alreadySealed = !extensible; + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + if (attrs.isConfigurable()) { + alreadySealed = false; + break; + } + } + + if (alreadySealed) { + m_sealed = this; + return this; + } + + m_sealed = engine->newClass(this); + for (uint i = 0; i < size; ++i) { PropertyAttributes attrs = propertyData.at(i); if (attrs.isEmpty()) continue; attrs.setConfigurable(false); - m_sealed = m_sealed->addMember(nameMap.at(i), attrs); + m_sealed->propertyData.set(i, attrs); } - m_sealed = m_sealed->nonExtensible(); + m_sealed->extensible = false; m_sealed->m_sealed = m_sealed; return m_sealed; @@ -435,8 +470,35 @@ InternalClass *InternalClass::frozen() if (m_frozen) return m_frozen; - m_frozen = propertiesFrozen(); - m_frozen = m_frozen->nonExtensible(); + bool alreadyFrozen = !extensible; + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) { + alreadyFrozen = false; + break; + } + } + + if (alreadyFrozen) { + m_frozen = this; + m_sealed = this; + return this; + } + + m_frozen = engine->newClass(this); + + for (uint i = 0; i < size; ++i) { + PropertyAttributes attrs = propertyData.at(i); + if (attrs.isEmpty()) + continue; + if (attrs.isData()) + attrs.setWritable(false); + attrs.setConfigurable(false); + m_frozen->propertyData.set(i, attrs); + } + m_frozen->extensible = false; m_frozen->m_frozen = m_frozen; m_frozen->m_sealed = m_frozen; @@ -468,7 +530,7 @@ InternalClass *InternalClass::asProtoClass() if (t.lookup) return t.lookup; - InternalClass *newClass = engine->newClass(*this); + InternalClass *newClass = engine->newClass(this); newClass->isUsedAsProto = true; t.lookup = newClass; @@ -505,27 +567,24 @@ void InternalClass::destroy() } } +static void updateProtoUsage(Heap::Object *o, InternalClass *ic) +{ + if (ic->prototype == o) + ic->id = ic->engine->newInternalClassId(); + for (auto &t : ic->transitions) { + Q_ASSERT(t.lookup); + updateProtoUsage(o, t.lookup); + } +} + + void InternalClass::updateProtoUsage(Heap::Object *o) { Q_ASSERT(isUsedAsProto); InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty]; Q_ASSERT(!ic->prototype); - // only need to go two levels into the IC hierarchy, as prototype changes - // can only happen there - for (auto &t : ic->transitions) { - Q_ASSERT(t.lookup); - if (t.flags == InternalClassTransition::VTableChange) { - InternalClass *ic2 = t.lookup; - for (auto &t2 : ic2->transitions) { - if (t2.flags == InternalClassTransition::PrototypeChange && - t2.lookup->prototype == o) - ic2->updateInternalClassIdRecursive(); - } - } else if (t.flags == InternalClassTransition::PrototypeChange && t.lookup->prototype == o) { - ic->updateInternalClassIdRecursive(); - } - } + ::updateProtoUsage(o, ic); } void InternalClass::updateInternalClassIdRecursive() @@ -538,26 +597,23 @@ void InternalClass::updateInternalClassIdRecursive() } - -void InternalClassPool::markObjects(MarkStack *markStack) +static void markChildren(MarkStack *markStack, InternalClass *ic) { - InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty]; - Q_ASSERT(!ic->prototype); + if (ic->prototype) + ic->prototype->mark(markStack); - // only need to go two levels into the IC hierarchy, as prototype changes - // can only happen there for (auto &t : ic->transitions) { Q_ASSERT(t.lookup); - if (t.flags == InternalClassTransition::VTableChange) { - InternalClass *ic2 = t.lookup; - for (auto &t2 : ic2->transitions) { - if (t2.flags == InternalClassTransition::PrototypeChange) - t2.lookup->prototype->mark(markStack); - } - } else if (t.flags == InternalClassTransition::PrototypeChange) { - t.lookup->prototype->mark(markStack); - } + markChildren(markStack, t.lookup); } } + +void InternalClassPool::markObjects(MarkStack *markStack) +{ + InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty]; + Q_ASSERT(!ic->prototype); + ::markChildren(markStack, ic); +} + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index b689272006..53fc25e42b 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -79,12 +79,12 @@ struct PropertyHash inline PropertyHash(); inline PropertyHash(const PropertyHash &other); inline ~PropertyHash(); + PropertyHash &operator=(const PropertyHash &other); void addEntry(const Entry &entry, int classSize); uint lookup(const Identifier *identifier) const; - -private: - PropertyHash &operator=(const PropertyHash &other); + int removeIdentifier(Identifier *identifier, int classSize); + void detach(bool grow, int classSize); }; struct PropertyHashData @@ -118,6 +118,17 @@ inline PropertyHash::~PropertyHash() delete d; } +inline PropertyHash &PropertyHash::operator=(const PropertyHash &other) +{ + ++other.d->refCount; + if (!--d->refCount) + delete d; + d = other.d; + return *this; +} + + + inline uint PropertyHash::lookup(const Identifier *identifier) const { Q_ASSERT(d->entries); @@ -163,6 +174,13 @@ struct SharedInternalClassData { if (!--d->refcount) delete d; } + SharedInternalClassData &operator=(const SharedInternalClassData &other) { + ++other.d->refcount; + if (!--d->refcount) + delete d; + d = other.d; + return *this; + } void add(uint pos, T value) { if (pos < d->size) { @@ -214,9 +232,6 @@ struct SharedInternalClassData { Q_ASSERT(i < d->size); return d->data[i]; } - -private: - SharedInternalClassData &operator=(const SharedInternalClassData &other); }; struct InternalClassTransition @@ -233,7 +248,8 @@ struct InternalClassTransition NotExtensible = 0x100, VTableChange = 0x200, PrototypeChange = 0x201, - ProtoClass = 0x202 + ProtoClass = 0x202, + RemoveMember = -1 }; bool operator==(const InternalClassTransition &other) const @@ -248,6 +264,7 @@ struct InternalClass : public QQmlJS::Managed { ExecutionEngine *engine; const VTable *vtable; Heap::Object *prototype; + InternalClass *parent = nullptr; PropertyHash propertyTable; // id to valueIndex SharedInternalClassData<Identifier *> nameMap; @@ -309,7 +326,7 @@ private: void updateInternalClassIdRecursive(); friend struct ExecutionEngine; InternalClass(ExecutionEngine *engine); - InternalClass(const InternalClass &other); + InternalClass(InternalClass *other); }; struct InternalClassPool : public QQmlJS::MemoryPool diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index 5fd200efc1..b337243204 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -78,7 +78,7 @@ Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine( void Profiler::stopProfiling() { featuresEnabled = 0; - reportData(true); + reportData(); m_sentLocations.clear(); } @@ -89,7 +89,7 @@ bool operator<(const FunctionCall &call1, const FunctionCall &call2) (call1.m_end == call2.m_end && call1.m_function < call2.m_function))); } -void Profiler::reportData(bool trackLocations) +void Profiler::reportData() { std::sort(m_data.begin(), m_data.end()); QVector<FunctionCallProperties> properties; @@ -100,12 +100,11 @@ void Profiler::reportData(bool trackLocations) properties.append(call.properties()); Function *function = call.function(); SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)]; - if (!trackLocations || !marker.isValid()) { + if (!marker.isValid()) { FunctionLocation &location = locations[properties.constLast().id]; if (!location.isValid()) location = call.resolveLocation(); - if (trackLocations) - marker.setFunction(function); + marker.setFunction(function); } } diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index e8c154e4e7..8461384e9a 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -251,7 +251,7 @@ public: void stopProfiling(); void startProfiling(quint64 features); - void reportData(bool trackLocations); + void reportData(); void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 5ebd385cfb..f555740455 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -56,7 +56,11 @@ #include <private/qv4functionobject_p.h> #include <private/qv4runtime_p.h> #include <private/qv4variantobject_p.h> + +#if QT_CONFIG(qml_sequence_object) #include <private/qv4sequenceobject_p.h> +#endif + #include <private/qv4objectproto_p.h> #include <private/qv4jsonobject_p.h> #include <private/qv4regexpobject_p.h> @@ -181,11 +185,13 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType())) return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType()); } else { +#if QT_CONFIG(qml_sequence_object) // see if it's a sequence type bool succeeded = false; QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded)); if (succeeded) return retn->asReturnedValue(); +#endif } if (property.propType() == QMetaType::UnknownType) { @@ -1611,6 +1617,7 @@ void CallArgument::initAsType(int callType) } } +#if QT_CONFIG(qml_sequence_object) template <class T, class M> void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine) { @@ -1623,6 +1630,7 @@ void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M } } } +#endif void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value) { @@ -1705,6 +1713,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q type = callType; } else if (callType == QMetaType::Void) { *qvariantPtr = QVariant(); +#if QT_CONFIG(qml_sequence_object) } else if (callType == qMetaTypeId<std::vector<int>>() || callType == qMetaTypeId<std::vector<qreal>>() || callType == qMetaTypeId<std::vector<bool>>() @@ -1732,6 +1741,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q stdVectorQModelIndexPtr = nullptr; fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine); } +#endif } else { queryEngine = true; } diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index e9bef2f604..eab1ffb5d7 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -59,6 +59,8 @@ #include "qv4context_p.h" #include "qv4string_p.h" +QT_REQUIRE_CONFIG(qml_sequence_object); + QT_BEGIN_NAMESPACE namespace QV4 { diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index 14def49d0a..31b51cbfe3 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -40,13 +40,17 @@ #include "qv4serialize_p.h" #include <private/qv8engine_p.h> +#if QT_CONFIG(qml_list_model) #include <private/qqmllistmodel_p.h> #include <private/qqmllistmodelworkeragent_p.h> +#endif #include <private/qv4value_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4regexpobject_p.h> +#if QT_CONFIG(qml_sequence_object) #include <private/qv4sequenceobject_p.h> +#endif #include <private/qv4objectproto_p.h> #include <private/qv4qobjectwrapper_p.h> @@ -82,8 +86,12 @@ enum Type { WorkerNumber, WorkerDate, WorkerRegexp, +#if QT_CONFIG(qml_list_model) WorkerListModel, +#endif +#if QT_CONFIG(qml_sequence_object) WorkerSequence +#endif }; static inline quint32 valueheader(Type type, quint32 size = 0) @@ -228,6 +236,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { // XXX TODO: Generalize passing objects between the main thread and worker scripts so // that others can trivially plug in their elements. +#if QT_CONFIG(qml_list_model) QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object()); if (lm && lm->agent()) { QQmlListModelWorkerAgent *agent = lm->agent(); @@ -236,9 +245,13 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine push(data, (void *)agent); return; } +#else + Q_UNUSED(qobjectWrapper); +#endif // No other QObject's are allowed to be sent push(data, valueheader(WorkerUndefined)); } else if (const Object *o = v.as<Object>()) { +#if QT_CONFIG(qml_sequence_object) if (o->isListType()) { // valid sequence. we generate a length (sequence length + 1 for the sequence type) uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32(); @@ -256,6 +269,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine return; } +#endif // regular object QV4::ScopedValue val(scope, v); @@ -353,6 +367,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) data += ALIGN(length * sizeof(quint16)); return Encode(engine->newRegExpObject(pattern, flags)); } +#if QT_CONFIG(qml_list_model) case WorkerListModel: { void *ptr = popPtr(data); @@ -369,6 +384,8 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) agent->setEngine(engine); return rv->asReturnedValue(); } +#endif +#if QT_CONFIG(qml_sequence_object) case WorkerSequence: { ScopedValue value(scope); @@ -387,6 +404,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded); return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded); } +#endif } Q_ASSERT(!"Unreachable"); return QV4::Encode::undefined(); diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 53933cd090..403f400bee 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -65,8 +65,6 @@ QT_BEGIN_NAMESPACE namespace QV4 { -struct InternalClass; - struct VTable { const VTable * const parent; @@ -88,17 +86,42 @@ struct VTable namespace Heap { +template <typename T, size_t o> +struct Pointer { + static Q_CONSTEXPR size_t offset = o; + T operator->() const { return get(); } + operator T () const { return get(); } + + Base *base(); + + void set(EngineBase *e, T newVal) { + WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Base *>(newVal)); + } + + T get() const { return reinterpret_cast<T>(ptr); } + + template <typename Type> + Type *cast() { return static_cast<Type *>(ptr); } + + Base *heapObject() const { return ptr; } + +private: + Base *ptr; +}; +typedef Pointer<char *, 0> V4PointerCheck; +Q_STATIC_ASSERT(std::is_trivial< V4PointerCheck >::value); + struct Q_QML_EXPORT Base { void *operator new(size_t) = delete; - static void markObjects(Heap::Base *, MarkStack *) {} + static void markObjects(Base *, MarkStack *) {} InternalClass *internalClass; inline ReturnedValue asReturnedValue() const; inline void mark(QV4::MarkStack *markStack); - const VTable *vtable() const { return internalClass->vtable; } + inline const VTable *vtable() const; inline bool isMarked() const { const HeapItem *h = reinterpret_cast<const HeapItem *>(this); Chunk *c = h->chunk(); @@ -130,8 +153,8 @@ struct Q_QML_EXPORT Base { } void *operator new(size_t, Managed *m) { return m; } - void *operator new(size_t, Heap::Base *m) { return m; } - void operator delete(void *, Heap::Base *) {} + void *operator new(size_t, Base *m) { return m; } + void operator delete(void *, Base *) {} void init() { _setInitialized(); } void destroy() { _setDestroyed(); } @@ -178,10 +201,9 @@ Q_STATIC_ASSERT(std::is_standard_layout<Base>::value); Q_STATIC_ASSERT(offsetof(Base, internalClass) == 0); Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE); -} inline -void Heap::Base::mark(QV4::MarkStack *markStack) +void Base::mark(QV4::MarkStack *markStack) { Q_ASSERT(inUse()); const HeapItem *h = reinterpret_cast<const HeapItem *>(this); @@ -196,36 +218,18 @@ void Heap::Base::mark(QV4::MarkStack *markStack) } } -namespace Heap { - -template <typename T, size_t o> -struct Pointer { - static Q_CONSTEXPR size_t offset = o; - T operator->() const { return get(); } - operator T () const { return get(); } - - Heap::Base *base() { - Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); - Q_ASSERT(base->inUse()); - return base; - } - - void set(EngineBase *e, T newVal) { - WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Heap::Base *>(newVal)); - } - - T get() const { return reinterpret_cast<T>(ptr); } - - template <typename Type> - Type *cast() { return static_cast<Type *>(ptr); } - - Heap::Base *heapObject() const { return ptr; } +inline +const VTable *Base::vtable() const +{ + return internalClass->vtable; +} -private: - Heap::Base *ptr; -}; -typedef Pointer<char *, 0> V4PointerCheck; -Q_STATIC_ASSERT(std::is_trivial< V4PointerCheck >::value); +template<typename T, size_t o> +Base *Pointer<T, o>::base() { + Base *base = reinterpret_cast<Base *>(this) - (offset/sizeof(Base *)); + Q_ASSERT(base->inUse()); + return base; +} } diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp index b4f0debf85..97ce6ebea3 100644 --- a/src/qml/parser/qqmljsengine_p.cpp +++ b/src/qml/parser/qqmljsengine_p.cpp @@ -112,13 +112,6 @@ double integerFromString(const char *buf, int size, int radix) return result; } -double integerFromString(const QString &str, int radix) -{ - QByteArray ba = QStringRef(&str).trimmed().toLatin1(); - return integerFromString(ba.constData(), ba.size(), radix); -} - - Engine::Engine() : _lexer(nullptr), _directives(nullptr) { } diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index aab9025a08..2414f92e50 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -87,12 +87,11 @@ Lexer::Lexer(Engine *engine) : _engine(engine) , _codePtr(nullptr) , _endPtr(nullptr) - , _lastLinePtr(nullptr) - , _tokenLinePtr(nullptr) , _tokenStartPtr(nullptr) , _char(QLatin1Char('\n')) , _errorCode(NoError) , _currentLineNumber(0) + , _currentColumnNumber(0) , _tokenValue(0) , _parenthesesState(IgnoreParentheses) , _parenthesesCount(0) @@ -101,6 +100,7 @@ Lexer::Lexer(Engine *engine) , _tokenKind(0) , _tokenLength(0) , _tokenLine(0) + , _tokenColumn(0) , _validTokenText(false) , _prohibitAutomaticSemicolon(false) , _restrictedKeyword(false) @@ -137,14 +137,13 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) _codePtr = code.unicode(); _endPtr = _codePtr + code.length(); - _lastLinePtr = _codePtr; - _tokenLinePtr = _codePtr; _tokenStartPtr = _codePtr; _char = QLatin1Char('\n'); _errorCode = NoError; _currentLineNumber = lineno; + _currentColumnNumber = 0; _tokenValue = 0; // parentheses state @@ -156,6 +155,7 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) _patternFlags = 0; _tokenLength = 0; _tokenLine = lineno; + _tokenColumn = 0; _validTokenText = false; _prohibitAutomaticSemicolon = false; @@ -172,9 +172,10 @@ void Lexer::scanChar() if (sequenceLength == 2) _char = *_codePtr++; - if (unsigned sequenceLength = isLineTerminatorSequence()) { - _lastLinePtr = _codePtr + sequenceLength - 1; // points to the first character after the newline + ++_currentColumnNumber; + if (isLineTerminator()) { ++_currentLineNumber; + _currentColumnNumber = 0; } } @@ -222,6 +223,25 @@ inline bool isBinop(int tok) return false; } } + +int hexDigit(QChar c) +{ + if (c >= QLatin1Char('0') && c <= QLatin1Char('9')) + return c.unicode() - '0'; + if (c >= QLatin1Char('a') && c <= QLatin1Char('f')) + return c.unicode() - 'a' + 10; + if (c >= QLatin1Char('A') && c <= QLatin1Char('F')) + return c.unicode() - 'A' + 10; + return -1; +} + +int octalDigit(QChar c) +{ + if (c >= QLatin1Char('0') && c <= QLatin1Char('7')) + return c.unicode() - '0'; + return -1; +} + } // anonymous namespace int Lexer::lex() @@ -295,39 +315,57 @@ int Lexer::lex() return _tokenKind; } -bool Lexer::isUnicodeEscapeSequence(const QChar *chars) -{ - if (isHexDigit(chars[0]) && isHexDigit(chars[1]) && isHexDigit(chars[2]) && isHexDigit(chars[3])) - return true; - - return false; -} - -QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok) +uint Lexer::decodeUnicodeEscapeCharacter(bool *ok) { - if (_char == QLatin1Char('u') && isUnicodeEscapeSequence(&_codePtr[0])) { - scanChar(); // skip u + Q_ASSERT(_char == QLatin1Char('u')); + scanChar(); // skip u + if (_codePtr + 4 <= _endPtr && isHexDigit(_char)) { + uint codePoint = 0; + for (int i = 0; i < 4; ++i) { + int digit = hexDigit(_char); + if (digit < 0) + goto error; + codePoint *= 16; + codePoint += digit; + scanChar(); + } - const QChar c1 = _char; - scanChar(); + *ok = true; + return codePoint; + } else if (_codePtr < _endPtr && _char == QLatin1Char('{')) { + scanChar(); // skip '{' + uint codePoint = 0; + if (!isHexDigit(_char)) + // need at least one hex digit + goto error; - const QChar c2 = _char; - scanChar(); + while (_codePtr <= _endPtr) { + int digit = hexDigit(_char); + if (digit < 0) + break; + codePoint *= 16; + codePoint += digit; + if (codePoint > 0x10ffff) + goto error; + scanChar(); + } - const QChar c3 = _char; - scanChar(); + if (_char != QLatin1Char('}')) + goto error; - const QChar c4 = _char; - scanChar(); + scanChar(); // skip '}' - if (ok) - *ok = true; - return convertUnicode(c1, c2, c3, c4); + *ok = true; + return codePoint; } + error: + _errorCode = IllegalUnicodeEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + *ok = false; - return QChar(); + return 0; } QChar Lexer::decodeHexEscapeCharacter(bool *ok) @@ -351,15 +389,15 @@ QChar Lexer::decodeHexEscapeCharacter(bool *ok) return QChar(); } -static inline bool isIdentifierStart(QChar ch) +static inline bool isIdentifierStart(uint ch) { // fast path for ascii - if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || - (ch.unicode() >= 'A' && ch.unicode() <= 'Z') || + if ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || ch == '$' || ch == '_') return true; - switch (ch.category()) { + switch (QChar::category(ch)) { case QChar::Number_Letter: case QChar::Letter_Uppercase: case QChar::Letter_Lowercase: @@ -373,17 +411,17 @@ static inline bool isIdentifierStart(QChar ch) return false; } -static bool isIdentifierPart(QChar ch) +static bool isIdentifierPart(uint ch) { // fast path for ascii - if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || - (ch.unicode() >= 'A' && ch.unicode() <= 'Z') || - (ch.unicode() >= '0' && ch.unicode() <= '9') || + if ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || ch == '$' || ch == '_' || - ch.unicode() == 0x200c /* ZWNJ */ || ch.unicode() == 0x200d /* ZWJ */) + ch == 0x200c /* ZWNJ */ || ch == 0x200d /* ZWJ */) return true; - switch (ch.category()) { + switch (QChar::category(ch)) { case QChar::Mark_NonSpacing: case QChar::Mark_SpacingCombining: @@ -416,15 +454,13 @@ int Lexer::scanToken() again: _validTokenText = false; - _tokenLinePtr = _lastLinePtr; while (_char.isSpace()) { - if (unsigned sequenceLength = isLineTerminatorSequence()) { - _tokenLinePtr = _codePtr + sequenceLength - 1; - + if (isLineTerminator()) { if (_restrictedKeyword) { // automatic semicolon insertion _tokenLine = _currentLineNumber; + _tokenColumn = _currentColumnNumber; _tokenStartPtr = _codePtr - 1; return T_SEMICOLON; } else { @@ -438,6 +474,7 @@ again: _tokenStartPtr = _codePtr - 1; _tokenLine = _currentLineNumber; + _tokenColumn = _currentColumnNumber; if (_codePtr > _endPtr) return EOF_SYMBOL; @@ -557,51 +594,8 @@ again: return T_DIVIDE_; case '.': - if (_char.isDigit()) { - QVarLengthArray<char,32> chars; - - chars.append(ch.unicode()); // append the `.' - - while (_char.isDigit()) { - chars.append(_char.unicode()); - scanChar(); - } - - if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { - if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && - _codePtr[1].isDigit())) { - - chars.append(_char.unicode()); - scanChar(); // consume `e' - - if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { - chars.append(_char.unicode()); - scanChar(); // consume the sign - } - - while (_char.isDigit()) { - chars.append(_char.unicode()); - scanChar(); - } - } - } - - chars.append('\0'); - - const char *begin = chars.constData(); - const char *end = nullptr; - bool ok = false; - - _tokenValue = qstrtod(begin, &end, &ok); - - if (end - begin != chars.size() - 1) { - _errorCode = IllegalExponentIndicator; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number"); - return T_ERROR; - } - - return T_NUMERIC_LITERAL; - } + if (isDecimalDigit(_char.unicode())) + return scanNumber(ch); return T_DOT; case '-': @@ -737,11 +731,15 @@ again: // unicode escape sequence case 'u': { bool ok = false; - u = decodeUnicodeEscapeCharacter(&ok); - if (! ok) { - _errorCode = IllegalUnicodeEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + uint codePoint = decodeUnicodeEscapeCharacter(&ok); + if (!ok) return T_ERROR; + if (QChar::requiresSurrogates(codePoint)) { + // need to use a surrogate pair + _tokenText += QChar(QChar::highSurrogate(codePoint)); + u = QChar::lowSurrogate(codePoint); + } else { + u = codePoint; } } break; @@ -824,28 +822,36 @@ again: return scanNumber(ch); default: { - QChar c = ch; + uint c = ch.unicode(); bool identifierWithEscapeChars = false; - if (c == QLatin1Char('\\') && _char == QLatin1Char('u')) { + if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_char.unicode())) { + c = QChar::surrogateToUcs4(ushort(c), _char.unicode()); + scanChar(); + } else if (c == '\\' && _char == QLatin1Char('u')) { identifierWithEscapeChars = true; bool ok = false; c = decodeUnicodeEscapeCharacter(&ok); - if (! ok) { - _errorCode = IllegalUnicodeEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + if (!ok) return T_ERROR; - } } if (isIdentifierStart(c)) { if (identifierWithEscapeChars) { _tokenText.resize(0); - _tokenText += c; + if (QChar::requiresSurrogates(c)) { + _tokenText += QChar(QChar::highSurrogate(c)); + _tokenText += QChar(QChar::lowSurrogate(c)); + } else { + _tokenText += QChar(c); + } _validTokenText = true; } - while (true) { - c = _char; - if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) { - if (! identifierWithEscapeChars) { + while (_codePtr <= _endPtr) { + c = _char.unicode(); + if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_codePtr->unicode())) { + scanChar(); + c = QChar::surrogateToUcs4(ushort(c), _char.unicode()); + } else if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) { + if (!identifierWithEscapeChars) { identifierWithEscapeChars = true; _tokenText.resize(0); _tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1); @@ -855,38 +861,52 @@ again: scanChar(); // skip '\\' bool ok = false; c = decodeUnicodeEscapeCharacter(&ok); - if (! ok) { - _errorCode = IllegalUnicodeEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + if (!ok) return T_ERROR; - } - if (isIdentifierPart(c)) - _tokenText += c; - continue; - } else if (isIdentifierPart(c)) { - if (identifierWithEscapeChars) - _tokenText += c; - scanChar(); + if (!isIdentifierPart(c)) + break; + + if (identifierWithEscapeChars) { + if (QChar::requiresSurrogates(c)) { + _tokenText += QChar(QChar::highSurrogate(c)); + _tokenText += QChar(QChar::lowSurrogate(c)); + } else { + _tokenText += QChar(c); + } + } continue; } - _tokenLength = _codePtr - _tokenStartPtr - 1; + if (!isIdentifierPart(c)) + break; + + if (identifierWithEscapeChars) { + if (QChar::requiresSurrogates(c)) { + _tokenText += QChar(QChar::highSurrogate(c)); + _tokenText += QChar(QChar::lowSurrogate(c)); + } else { + _tokenText += QChar(c); + } + } + scanChar(); + } - int kind = T_IDENTIFIER; + _tokenLength = _codePtr - _tokenStartPtr - 1; - if (! identifierWithEscapeChars) - kind = classify(_tokenStartPtr, _tokenLength, _qmlMode); + int kind = T_IDENTIFIER; - if (_engine) { - if (kind == T_IDENTIFIER && identifierWithEscapeChars) - _tokenSpell = _engine->newStringRef(_tokenText); - else - _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength); - } + if (!identifierWithEscapeChars) + kind = classify(_tokenStartPtr, _tokenLength, _qmlMode); - return kind; + if (_engine) { + if (kind == T_IDENTIFIER && identifierWithEscapeChars) + _tokenSpell = _engine->newStringRef(_tokenText); + else + _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength); } + + return kind; } } @@ -898,91 +918,110 @@ again: int Lexer::scanNumber(QChar ch) { - if (ch != QLatin1Char('0')) { - QVarLengthArray<char, 64> buf; - buf += ch.toLatin1(); - - QChar n = _char; - const QChar *code = _codePtr; - while (n.isDigit()) { - buf += n.toLatin1(); - n = *code++; - } + if (ch == QLatin1Char('0')) { + if (_char == QLatin1Char('x') || _char == QLatin1Char('X')) { + ch = _char; // remember the x or X to use it in the error message below. + + // parse hex integer literal + scanChar(); // consume 'x' + + if (!isHexDigit(_char)) { + _errorCode = IllegalNumber; + _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch); + return T_ERROR; + } - if (n != QLatin1Char('.') && n != QLatin1Char('e') && n != QLatin1Char('E')) { - if (code != _codePtr) { - _codePtr = code - 1; + double d = 0.; + while (1) { + int digit = ::hexDigit(_char); + if (digit < 0) + break; + d *= 16; + d += digit; scanChar(); } - buf.append('\0'); - _tokenValue = strtod(buf.constData(), nullptr); + + _tokenValue = d; return T_NUMERIC_LITERAL; - } - } else if (_char.isDigit() && !qmlMode()) { - _errorCode = IllegalCharacter; - _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'"); - return T_ERROR; - } + } else if (_char == QLatin1Char('o') || _char == QLatin1Char('O')) { + ch = _char; // remember the o or O to use it in the error message below. - QVarLengthArray<char,32> chars; - chars.append(ch.unicode()); + // parse octal integer literal + scanChar(); // consume 'o' - if (ch == QLatin1Char('0') && (_char == QLatin1Char('x') || _char == QLatin1Char('X'))) { - ch = _char; // remember the x or X to use it in the error message below. + if (!isOctalDigit(_char.unicode())) { + _errorCode = IllegalNumber; + _errorMessage = QCoreApplication::translate("QQmlParser", "At least one octal digit is required after '0%1'").arg(ch); + return T_ERROR; + } - // parse hex integer literal - chars.append(_char.unicode()); - scanChar(); // consume `x' + double d = 0.; + while (1) { + int digit = ::octalDigit(_char); + if (digit < 0) + break; + d *= 8; + d += digit; + scanChar(); + } - while (isHexDigit(_char)) { - chars.append(_char.unicode()); - scanChar(); - } + _tokenValue = d; + return T_NUMERIC_LITERAL; + } else if (_char == QLatin1Char('b') || _char == QLatin1Char('B')) { + ch = _char; // remember the b or B to use it in the error message below. + + // parse binary integer literal + scanChar(); // consume 'b' - if (chars.size() < 3) { - _errorCode = IllegalHexNumber; - _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch); + if (_char.unicode() != '0' && _char.unicode() != '1') { + _errorCode = IllegalNumber; + _errorMessage = QCoreApplication::translate("QQmlParser", "At least one binary digit is required after '0%1'").arg(ch); + return T_ERROR; + } + + double d = 0.; + while (1) { + int digit = 0; + if (_char.unicode() == '1') + digit = 1; + else if (_char.unicode() != '0') + break; + d *= 2; + d += digit; + scanChar(); + } + + _tokenValue = d; + return T_NUMERIC_LITERAL; + } else if (_char.isDigit() && !qmlMode()) { + _errorCode = IllegalCharacter; + _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'"); return T_ERROR; } - - _tokenValue = integerFromString(chars.constData(), chars.size(), 16); - return T_NUMERIC_LITERAL; } // decimal integer literal - while (_char.isDigit()) { - chars.append(_char.unicode()); - scanChar(); // consume the digit - } - - if (_char == QLatin1Char('.')) { - chars.append(_char.unicode()); - scanChar(); // consume `.' + QVarLengthArray<char,32> chars; + chars.append(ch.unicode()); + if (ch != QLatin1Char('.')) { while (_char.isDigit()) { chars.append(_char.unicode()); - scanChar(); + scanChar(); // consume the digit } - if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { - if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && - _codePtr[1].isDigit())) { - - chars.append(_char.unicode()); - scanChar(); // consume `e' + if (_char == QLatin1Char('.')) { + chars.append(_char.unicode()); + scanChar(); // consume `.' + } + } - if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) { - chars.append(_char.unicode()); - scanChar(); // consume the sign - } + while (_char.isDigit()) { + chars.append(_char.unicode()); + scanChar(); + } - while (_char.isDigit()) { - chars.append(_char.unicode()); - scanChar(); - } - } - } - } else if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { + if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) { if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) && _codePtr[1].isDigit())) { @@ -1001,12 +1040,6 @@ int Lexer::scanNumber(QChar ch) } } - if (chars.length() == 1) { - // if we ended up with a single digit, then it was a '0' - _tokenValue = 0; - return T_NUMERIC_LITERAL; - } - chars.append('\0'); const char *begin = chars.constData(); @@ -1174,16 +1207,6 @@ bool Lexer::isOctalDigit(ushort c) return (c >= '0' && c <= '7'); } -int Lexer::tokenEndLine() const -{ - return _currentLineNumber; -} - -int Lexer::tokenEndColumn() const -{ - return _codePtr - _lastLinePtr; -} - QString Lexer::tokenText() const { if (_validTokenText) diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h index 11d8081713..eca5d932b4 100644 --- a/src/qml/parser/qqmljslexer_p.h +++ b/src/qml/parser/qqmljslexer_p.h @@ -124,7 +124,7 @@ public: enum Error { NoError, IllegalCharacter, - IllegalHexNumber, + IllegalNumber, UnclosedStringLiteral, IllegalEscapeSequence, IllegalUnicodeEscapeSequence, @@ -166,10 +166,7 @@ public: int tokenLength() const { return _tokenLength; } int tokenStartLine() const { return _tokenLine; } - int tokenStartColumn() const { return _tokenStartPtr - _tokenLinePtr + 1; } - - int tokenEndLine() const; - int tokenEndColumn() const; + int tokenStartColumn() const { return _tokenColumn; } inline QStringRef tokenSpell() const { return _tokenSpell; } double tokenValue() const { return _tokenValue; } @@ -202,10 +199,9 @@ private: static bool isDecimalDigit(ushort c); static bool isHexDigit(QChar c); static bool isOctalDigit(ushort c); - static bool isUnicodeEscapeSequence(const QChar *chars); void syncProhibitAutomaticSemicolon(); - QChar decodeUnicodeEscapeCharacter(bool *ok); + uint decodeUnicodeEscapeCharacter(bool *ok); QChar decodeHexEscapeCharacter(bool *ok); private: @@ -218,14 +214,13 @@ private: const QChar *_codePtr; const QChar *_endPtr; - const QChar *_lastLinePtr; - const QChar *_tokenLinePtr; const QChar *_tokenStartPtr; QChar _char; Error _errorCode; int _currentLineNumber; + int _currentColumnNumber; double _tokenValue; // parentheses state @@ -238,6 +233,7 @@ private: int _tokenKind; int _tokenLength; int _tokenLine; + int _tokenColumn; bool _validTokenText; bool _prohibitAutomaticSemicolon; diff --git a/src/qml/qml.pro b/src/qml/qml.pro index f75bfa0313..eabca59836 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -72,7 +72,7 @@ include(jsruntime/jsruntime.pri) include(jit/jit.pri) include(qml/qml.pri) include(debugger/debugger.pri) -qtConfig(animation) { +qtConfig(qml-animation) { include(animations/animations.pri) } include(types/types.pri) diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 412dc6cba2..6d69294c17 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -20,7 +20,6 @@ SOURCES += \ $$PWD/qqmlinfo.cpp \ $$PWD/qqmlerror.cpp \ $$PWD/qqmlvaluetype.cpp \ - $$PWD/qqmlxmlhttprequest.cpp \ $$PWD/qqmlcleanup.cpp \ $$PWD/qqmlpropertycache.cpp \ $$PWD/qqmlnotifier.cpp \ @@ -31,7 +30,6 @@ SOURCES += \ $$PWD/qqmlextensionplugin.cpp \ $$PWD/qqmlimport.cpp \ $$PWD/qqmllist.cpp \ - $$PWD/qqmllocale.cpp \ $$PWD/qqmljavascriptexpression.cpp \ $$PWD/qqmlabstractbinding.cpp \ $$PWD/qqmlvaluetypeproxybinding.cpp \ @@ -85,7 +83,6 @@ HEADERS += \ $$PWD/qqmldata_p.h \ $$PWD/qqmlerror.h \ $$PWD/qqmlvaluetype_p.h \ - $$PWD/qqmlxmlhttprequest_p.h \ $$PWD/qqmlcleanup_p.h \ $$PWD/qqmlpropertycache_p.h \ $$PWD/qqmlpropertyindex_p.h \ @@ -99,7 +96,6 @@ HEADERS += \ $$PWD/qqmlimport_p.h \ $$PWD/qqmlextensionplugin.h \ $$PWD/qqmlscriptstring_p.h \ - $$PWD/qqmllocale_p.h \ $$PWD/qqmlcomponentattached_p.h \ $$PWD/qqmljavascriptexpression_p.h \ $$PWD/qqmlabstractbinding_p.h \ @@ -120,5 +116,22 @@ HEADERS += \ $$PWD/qqmldelayedcallqueue_p.h \ $$PWD/qqmlloggingcategory_p.h +qtConfig(qml-xml-http-request) { + HEADERS += \ + $$PWD/qqmlxmlhttprequest_p.h + + SOURCES += \ + $$PWD/qqmlxmlhttprequest.cpp + +} + +qtConfig(qml-locale) { + HEADERS += \ + $$PWD/qqmllocale_p.h + + SOURCES += \ + $$PWD/qqmllocale.cpp +} + include(ftw/ftw.pri) include(v8/v8.pri) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 4054d2f0be..0e74baf684 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -48,7 +48,6 @@ #include "qqmlcomponent.h" #include "qqmlvme_p.h" #include "qqmlstringconverters_p.h" -#include "qqmlxmlhttprequest_p.h" #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" #include "qqmlcomponent_p.h" @@ -79,13 +78,17 @@ #include <private/qobject_p.h> #include <private/qmetaobject_p.h> +#if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> +#endif #include <private/qqmlbind_p.h> #include <private/qqmlconnections_p.h> -#if QT_CONFIG(animation) +#if QT_CONFIG(qml_animation) #include <private/qqmltimer_p.h> #endif +#if QT_CONFIG(qml_list_model) #include <private/qqmllistmodel_p.h> +#endif #include <private/qqmlplatform_p.h> #include <private/qquickpackage_p.h> #include <private/qqmldelegatemodel_p.h> @@ -223,7 +226,7 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8 qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3 qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections"); -#if QT_CONFIG(animation) +#if QT_CONFIG(qml_animation) qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer"); #endif qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1 @@ -236,8 +239,10 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int // These QtQuick types' implementation resides in the QtQml module void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor) { +#if QT_CONFIG(qml_list_model) qmlRegisterType<QQmlListElement>(uri, versionMajor, versionMinor, "ListElement"); // Now in QtQml.Models, here for compatibility qmlRegisterCustomType<QQmlListModel>(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility +#endif qmlRegisterType<QQuickWorkerScript>(uri, versionMajor, versionMinor, "WorkerScript"); qmlRegisterType<QQuickPackage>(uri, versionMajor, versionMinor, "Package"); qmlRegisterType<QQmlDelegateModel>(uri, versionMajor, versionMinor, "VisualDataModel"); @@ -252,7 +257,9 @@ void QQmlEnginePrivate::defineQtQuick2Module() // register the QtQuick2 types which are implemented in the QtQml module. registerQtQuick2Types("QtQuick",2,0); +#if QT_CONFIG(qml_locale) qmlRegisterUncreatableType<QQmlLocale>("QtQuick", 2, 0, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); +#endif } bool QQmlEnginePrivate::designerMode() @@ -950,7 +957,9 @@ void QQmlEnginePrivate::init() if (baseModulesUninitialized) { qmlRegisterType<QQmlComponent>("QML", 1, 0, "Component"); // required for the Compiler. registerBaseTypes("QtQml", 2, 0); // import which provides language building blocks. +#if QT_CONFIG(qml_locale) qmlRegisterUncreatableType<QQmlLocale>("QtQml", 2, 2, "Locale", QQmlEngine::tr("Locale cannot be instantiated. Use Qt.locale()")); +#endif QQmlData::init(); baseModulesUninitialized = false; diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h index 8341b1f555..859c36e11b 100644 --- a/src/qml/qml/qqmllocale_p.h +++ b/src/qml/qml/qqmllocale_p.h @@ -58,6 +58,8 @@ #include <private/qqmlglobal_p.h> #include <private/qv4object_p.h> +QT_REQUIRE_CONFIG(qml_locale); + QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlxmlhttprequest_p.h b/src/qml/qml/qqmlxmlhttprequest_p.h index f2836d8301..7515ef8dcc 100644 --- a/src/qml/qml/qqmlxmlhttprequest_p.h +++ b/src/qml/qml/qqmlxmlhttprequest_p.h @@ -55,7 +55,7 @@ #include <QtCore/qglobal.h> #include <private/qqmlglobal_p.h> -#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network) +QT_REQUIRE_CONFIG(qml_xml_http_request); QT_BEGIN_NAMESPACE @@ -64,7 +64,5 @@ void qt_rem_qmlxmlhttprequest(QV4::ExecutionEngine *engine, void *); QT_END_NAMESPACE -#endif // xmlstreamreader && qml_network - #endif // QQMLXMLHTTPREQUEST_P_H diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index c147feeb73..0e1d9e6fad 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -44,7 +44,9 @@ #include <private/qqmlcomponent_p.h> #include <private/qqmlloggingcategory_p.h> #include <private/qqmlstringconverters_p.h> +#if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> +#endif #include <private/qv8engine_p.h> #include <private/qqmldelayedcallqueue_p.h> #include <QFileInfo> @@ -138,7 +140,9 @@ void Heap::QtObject::init(QQmlEngine *qmlEngine) o->defineDefaultProperty(QStringLiteral("btoa"), QV4::QtObject::method_btoa); o->defineDefaultProperty(QStringLiteral("atob"), QV4::QtObject::method_atob); o->defineDefaultProperty(QStringLiteral("resolvedUrl"), QV4::QtObject::method_resolvedUrl); +#if QT_CONFIG(qml_locale) o->defineDefaultProperty(QStringLiteral("locale"), QV4::QtObject::method_locale); +#endif o->defineDefaultProperty(QStringLiteral("binding"), QV4::QtObject::method_binding); if (qmlEngine) { @@ -1299,6 +1303,7 @@ ReturnedValue QtObject::method_createComponent(const FunctionObject *b, const Va return QV4::QObjectWrapper::wrap(scope.engine, c); } +#if QT_CONFIG(qml_locale) /*! \qmlmethod Qt::locale(name) @@ -1333,6 +1338,7 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co return QQmlLocale::locale(scope.engine, code); } +#endif void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction) { diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h index 104dae5d79..993e323e51 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -123,7 +123,9 @@ struct QtObject : Object static ReturnedValue method_resolvedUrl(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_createQmlObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_createComponent(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +#if QT_CONFIG(qml_locale) static ReturnedValue method_locale(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); +#endif static ReturnedValue method_binding(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); static ReturnedValue method_get_platform(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 038a75d50a..92a86aa9e5 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -39,14 +39,21 @@ #include "qv8engine_p.h" +#if QT_CONFIG(qml_sequence_object) #include "qv4sequenceobject_p.h" +#endif + #include "private/qjsengine_p.h" #include <private/qqmlbuiltinfunctions_p.h> #include <private/qqmllist_p.h> #include <private/qqmlengine_p.h> +#if QT_CONFIG(qml_xml_http_request) #include <private/qqmlxmlhttprequest_p.h> +#endif +#if QT_CONFIG(qml_locale) #include <private/qqmllocale_p.h> +#endif #include <private/qqmlglobal_p.h> #include <private/qqmlmemoryprofiler_p.h> #include <private/qqmlplatform_p.h> @@ -123,11 +130,12 @@ static void restoreJSValue(QDataStream &stream, void *data) } } -QV8Engine::QV8Engine(QJSEngine *qq, QV4::ExecutionEngine *v4) - : q(qq) - , m_engine(nullptr) +QV8Engine::QV8Engine(QV4::ExecutionEngine *v4) + : m_engine(nullptr) , m_v4Engine(v4) +#if QT_CONFIG(qml_xml_http_request) , m_xmlHttpRequestData(nullptr) +#endif { #ifdef Q_PROCESSOR_X86_32 if (!qCpuHasFeature(SSE2)) { @@ -157,7 +165,7 @@ QV8Engine::~QV8Engine() qDeleteAll(m_extensionData); m_extensionData.clear(); -#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network) +#if QT_CONFIG(qml_xml_http_request) qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData); m_xmlHttpRequestData = nullptr; #endif @@ -183,11 +191,13 @@ void QV8Engine::initializeGlobal() QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocObject<QV4::QtObject>(m_engine)); m_v4Engine->globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt); +#if QT_CONFIG(qml_locale) QQmlLocale::registerStringLocaleCompare(m_v4Engine); QQmlDateExtension::registerExtension(m_v4Engine); QQmlNumberExtension::registerExtension(m_v4Engine); +#endif -#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network) +#if QT_CONFIG(qml_xml_http_request) qt_add_domexceptions(m_v4Engine); m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine); #endif diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index b6a378667e..d90f1827fe 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -154,10 +154,9 @@ class Q_QML_PRIVATE_EXPORT QV8Engine friend class QJSEngine; public: // static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } - static QV4::ExecutionEngine *getV4(QJSEngine *q) { return q->handle(); } static QV4::ExecutionEngine *getV4(QV8Engine *d) { return d->m_v4Engine; } - QV8Engine(QJSEngine* qq, QV4::ExecutionEngine *v4); + QV8Engine(QV4::ExecutionEngine *v4); virtual ~QV8Engine(); // This enum should be in sync with QQmlEngine::ObjectOwnership @@ -170,10 +169,11 @@ public: void initQmlGlobalObject(); void setEngine(QQmlEngine *engine); QQmlEngine *engine() { return m_engine; } - QJSEngine *publicEngine() { return q; } QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; } +#if QT_CONFIG(qml_xml_http_request) void *xmlHttpRequestData() const { return m_xmlHttpRequestData; } +#endif void freezeObject(const QV4::Value &value); @@ -202,13 +202,14 @@ public: int consoleCountHelper(const QString &file, quint16 line, quint16 column); protected: - QJSEngine* q; QQmlEngine *m_engine; QQmlDelayedCallQueue m_delayedCallQueue; QV4::ExecutionEngine *m_v4Engine; +#if QT_CONFIG(qml_xml_http_request) void *m_xmlHttpRequestData; +#endif QVector<Deletable *> m_extensionData; diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h index 6e92867cf5..e02dfa5ed4 100644 --- a/src/qml/qtqmlglobal.h +++ b/src/qml/qtqmlglobal.h @@ -52,6 +52,7 @@ # endif #else # define QT_FEATURE_qml_debug -1 +# define QT_FEATURE_qml_sequence_object 1 #endif QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 44166e4aa8..29e0baa3ac 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -487,6 +487,125 @@ void QQmlDelegateModel::setRootIndex(const QVariant &root) } /*! + \qmlproperty int QtQml.Models::DelegateModel::rows + + Contains the number of rows in the model. If the model + is a list of items, it will be equal to the number of items + in the list. + + \since QtQml.Models 2.12 +*/ +int QQmlDelegateModel::rows() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.rowCount(); +} + +void QQmlDelegateModelPrivate::setRows(int rows) +{ + Q_Q(QQmlDelegateModel); + const bool changed = m_adaptorModel.rowCount() != rows; + + if (changed || !m_adaptorModel.isValid()) { + const int oldCount = m_count; + + if (rows > 0) + m_adaptorModel.rows = rows; + else + m_adaptorModel.rows.invalidate(); + + // Check if the previous layout was invalidated, and if so, reconnect the model + if (!m_adaptorModel.isValid() && m_adaptorModel.aim()) + m_adaptorModel.setModel(m_adaptorModel.list.list(), q, m_context->engine()); + + if (m_adaptorModel.canFetchMore()) + m_adaptorModel.fetchMore(); + + if (m_complete) { + const int newCount = m_adaptorModel.count(); + if (oldCount) + q->_q_itemsRemoved(0, oldCount); + if (newCount) + q->_q_itemsInserted(0, newCount); + } + + if (changed) + emit q->rowsChanged(); + } +} + +void QQmlDelegateModel::setRows(int rows) +{ + Q_D(QQmlDelegateModel); + d->setRows(rows); +} + +void QQmlDelegateModel::resetRows() +{ + Q_D(QQmlDelegateModel); + d->setRows(-1); +} + +/*! + \qmlproperty int QtQml.Models::DelegateModel::columns + + Contains the number of columns in the model. If the model + is a list of items, it will be equal to \c 1. + + \since QtQml.Models 2.12 +*/ +int QQmlDelegateModel::columns() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.columnCount(); +} + +void QQmlDelegateModelPrivate::setColumns(int columns) +{ + Q_Q(QQmlDelegateModel); + const bool changed = m_adaptorModel.columnCount() != columns; + + if (changed || !m_adaptorModel.isValid()) { + const int oldCount = m_count; + + if (columns > 1) + m_adaptorModel.columns = columns; + else + m_adaptorModel.columns.invalidate(); + + // Check if the previous layout was invalidated, and if so, reconnect the model + if (!m_adaptorModel.isValid() && m_adaptorModel.aim()) + m_adaptorModel.setModel(m_adaptorModel.list.list(), q, m_context->engine()); + + if (m_adaptorModel.canFetchMore()) + m_adaptorModel.fetchMore(); + + if (m_complete) { + const int newCount = m_adaptorModel.count(); + if (oldCount) + q->_q_itemsRemoved(0, oldCount); + if (newCount) + q->_q_itemsInserted(0, newCount); + } + + if (changed) + emit q->columnsChanged(); + } +} + +void QQmlDelegateModel::setColumns(int columns) +{ + Q_D(QQmlDelegateModel); + d->setColumns(columns); +} + +void QQmlDelegateModel::resetColumns() +{ + Q_D(QQmlDelegateModel); + d->setColumns(-1); +} + +/*! \qmlmethod QModelIndex QtQml.Models::DelegateModel::modelIndex(int index) QAbstractItemModel provides a hierarchical tree of data, whereas @@ -1045,7 +1164,10 @@ QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index) if (!it->inCache()) return QQmlIncubator::Null; - return d->m_cache.at(it.cacheIndex)->incubationTask->status(); + if (auto incubationTask = d->m_cache.at(it.cacheIndex)->incubationTask) + return incubationTask->status(); + + return QQmlIncubator::Ready; } QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) @@ -3193,7 +3315,10 @@ QQmlIncubator::Status QQmlPartsModel::incubationStatus(int index) if (!it->inCache()) return QQmlIncubator::Null; - return model->m_cache.at(it.cacheIndex)->incubationTask->status(); + if (auto incubationTask = model->m_cache.at(it.cacheIndex)->incubationTask) + return incubationTask->status(); + + return QQmlIncubator::Ready; } int QQmlPartsModel::indexOf(QObject *item, QObject *) const diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h index b894df8f82..439d5c9d37 100644 --- a/src/qml/types/qqmldelegatemodel_p.h +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -86,6 +86,8 @@ class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public Q_PROPERTY(QQmlListProperty<QQmlDelegateModelGroup> groups READ groups CONSTANT) Q_PROPERTY(QObject *parts READ parts CONSTANT) Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) + Q_PROPERTY(int rows READ rows WRITE setRows RESET resetRows NOTIFY rowsChanged REVISION 12) + Q_PROPERTY(int columns READ columns WRITE setColumns RESET resetColumns NOTIFY columnsChanged REVISION 12) Q_CLASSINFO("DefaultProperty", "delegate") Q_INTERFACES(QQmlParserStatus) public: @@ -105,6 +107,14 @@ public: QVariant rootIndex() const; void setRootIndex(const QVariant &root); + int rows() const; + void setRows(int rows); + void resetRows(); + + int columns() const; + void setColumns(int columns); + void resetColumns(); + Q_INVOKABLE QVariant modelIndex(int idx) const; Q_INVOKABLE QVariant parentModelIndex() const; @@ -136,6 +146,8 @@ Q_SIGNALS: void filterGroupChanged(); void defaultGroupsChanged(); void rootIndexChanged(); + Q_REVISION(12) void rowsChanged(); + Q_REVISION(12) void columnsChanged(); private Q_SLOTS: void _q_itemsChanged(int index, int count, const QVector<int> &roles); diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h index 68b987a5fa..9f1c9d31a4 100644 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -119,7 +119,7 @@ public: int groupIndex(Compositor::Group group); int modelIndex() const { return index; } - void setModelIndex(int idx) { index = idx; Q_EMIT modelIndexChanged(); } + virtual void setModelIndex(int idx) { index = idx; Q_EMIT modelIndexChanged(); } virtual QV4::ReturnedValue get() { return QV4::QObjectWrapper::wrap(v4, this); } @@ -301,6 +301,9 @@ public: void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); void setInitialState(QQDMIncubationTask *incubationTask, QObject *o); + void setRows(int rows); + void setColumns(int columns); + QQmlAdaptorModel m_adaptorModel; QQmlListCompositor m_compositor; QQmlComponent *m_delegate; diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h index 0c0859dc80..0a9d29ac05 100644 --- a/src/qml/types/qqmllistmodel_p.h +++ b/src/qml/types/qqmllistmodel_p.h @@ -64,6 +64,8 @@ #include <private/qv4engine_p.h> #include <private/qpodvector_p.h> +QT_REQUIRE_CONFIG(qml_list_model); + QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index ad5e94c909..99e9b30aff 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -57,6 +57,8 @@ #include <private/qv4qobjectwrapper_p.h> #include <qqml.h> +QT_REQUIRE_CONFIG(qml_list_model); + QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h index 2120f25744..ae2d4b11e0 100644 --- a/src/qml/types/qqmllistmodelworkeragent_p.h +++ b/src/qml/types/qqmllistmodelworkeragent_p.h @@ -59,6 +59,8 @@ #include <private/qv8engine_p.h> +QT_REQUIRE_CONFIG(qml_list_model); + QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp index c36e26a525..872d7c059f 100644 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -39,7 +39,9 @@ #include "qqmlmodelsmodule_p.h" #include <QtCore/qitemselectionmodel.h> +#if QT_CONFIG(qml_list_model) #include <private/qqmllistmodel_p.h> +#endif #include <private/qqmldelegatemodel_p.h> #include <private/qqmlobjectmodel_p.h> @@ -49,9 +51,12 @@ void QQmlModelsModule::defineModule() { const char uri[] = "QtQml.Models"; +#if QT_CONFIG(qml_list_model) qmlRegisterType<QQmlListElement>(uri, 2, 1, "ListElement"); qmlRegisterCustomType<QQmlListModel>(uri, 2, 1, "ListModel", new QQmlListModelParser); +#endif qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel"); + qmlRegisterType<QQmlDelegateModel,12>(uri, 2, 9, "DelegateModel"); qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup"); qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel"); qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel"); diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h index d597869994..0160e97a2f 100644 --- a/src/qml/types/qqmltimer_p.h +++ b/src/qml/types/qqmltimer_p.h @@ -57,6 +57,8 @@ #include <private/qtqmlglobal_p.h> +QT_REQUIRE_CONFIG(qml_animation); + QT_BEGIN_NAMESPACE class QQmlTimerPrivate; diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index 80c2d3a4bc..dbc2769dd1 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -37,9 +37,12 @@ ** ****************************************************************************/ +#include "qtqmlglobal_p.h" #include "qquickworkerscript_p.h" +#if QT_CONFIG(qml_list_model) #include "qqmllistmodel_p.h" #include "qqmllistmodelworkeragent_p.h" +#endif #include <private/qqmlengine_p.h> #include <private/qqmlexpression_p.h> @@ -201,7 +204,7 @@ private: }; QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) - : QV8Engine(nullptr, new QV4::ExecutionEngine), p(parent) + : QV8Engine(new QV4::ExecutionEngine), p(parent) #if QT_CONFIG(qml_network) , accessManager(nullptr) #endif diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index e85ab5982b..25b231f954 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -2,8 +2,6 @@ SOURCES += \ $$PWD/qqmlbind.cpp \ $$PWD/qqmlconnections.cpp \ $$PWD/qqmldelegatemodel.cpp \ - $$PWD/qqmllistmodel.cpp \ - $$PWD/qqmllistmodelworkeragent.cpp \ $$PWD/qqmlmodelsmodule.cpp \ $$PWD/qqmlmodelindexvaluetype.cpp \ $$PWD/qqmlobjectmodel.cpp \ @@ -16,9 +14,6 @@ HEADERS += \ $$PWD/qqmlconnections_p.h \ $$PWD/qqmldelegatemodel_p.h \ $$PWD/qqmldelegatemodel_p_p.h \ - $$PWD/qqmllistmodel_p.h \ - $$PWD/qqmllistmodel_p_p.h \ - $$PWD/qqmllistmodelworkeragent_p.h \ $$PWD/qqmlmodelsmodule_p.h \ $$PWD/qqmlmodelindexvaluetype_p.h \ $$PWD/qqmlobjectmodel_p.h \ @@ -27,7 +22,18 @@ HEADERS += \ $$PWD/qqmlinstantiator_p.h \ $$PWD/qqmlinstantiator_p_p.h -qtConfig(animation) { +qtConfig(qml-list-model) { + SOURCES += \ + $$PWD/qqmllistmodel.cpp \ + $$PWD/qqmllistmodelworkeragent.cpp + + HEADERS += \ + $$PWD/qqmllistmodel_p.h \ + $$PWD/qqmllistmodel_p_p.h \ + $$PWD/qqmllistmodelworkeragent_p.h +} + +qtConfig(qml-animation) { SOURCES += \ $$PWD/qqmltimer.cpp diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index b4bebb9d5d..1cc347d6bc 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -330,9 +330,8 @@ bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &, int idx) { if (index == -1) { Q_ASSERT(idx >= 0); - index = idx; cachedData.clear(); - emit modelIndexChanged(); + setModelIndex(idx); const QMetaObject *meta = metaObject(); const int propertyCount = type->propertyRoles.count(); for (int i = 0; i < propertyCount; ++i) @@ -399,13 +398,18 @@ QV4::ReturnedValue QQmlDMCachedModelData::set_property(const QV4::FunctionObject class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData { Q_OBJECT + Q_PROPERTY(int row MEMBER row NOTIFY rowChanged) + Q_PROPERTY(int column MEMBER column NOTIFY columnChanged) Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) + public: QQmlDMAbstractItemModelData( QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index) : QQmlDMCachedModelData(metaType, dataType, index) + , row(type->model->rowAt(index)) + , column(type->model->columnAt(index)) { } @@ -413,7 +417,7 @@ public: { if (index >= 0 && *type->model) { const QAbstractItemModel * const model = type->model->aim(); - return model->hasChildren(model->index(index, 0, type->model->rootIndex)); + return model->hasChildren(model->index(row, column, type->model->rootIndex)); } else { return false; } @@ -421,13 +425,13 @@ public: QVariant value(int role) const override { - return type->model->aim()->index(index, 0, type->model->rootIndex).data(role); + return type->model->aim()->index(row, column, type->model->rootIndex).data(role); } void setValue(int role, const QVariant &value) override { type->model->aim()->setData( - type->model->aim()->index(index, 0, type->model->rootIndex), value, role); + type->model->aim()->index(row, column, type->model->rootIndex), value, role); } QV4::ReturnedValue get() override @@ -443,6 +447,16 @@ public: ++scriptRef; return o.asReturnedValue(); } + + void setModelIndex(int idx) override; + +Q_SIGNALS: + void rowChanged(); + void columnChanged(); + +private: + int row; + int column; }; class VDMAbstractItemModelDataType : public VDMModelDelegateDataType @@ -453,11 +467,16 @@ public: { } - int count(const QQmlAdaptorModel &model) const override + int rowCount(const QQmlAdaptorModel &model) const override { return model.aim()->rowCount(model.rootIndex); } + int columnCount(const QQmlAdaptorModel &model) const override + { + return model.aim()->columnCount(model.rootIndex); + } + void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const override { QAbstractItemModel * const aim = model.aim(); @@ -485,9 +504,9 @@ public: { QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8()); if (it != roleNames.end()) { - return model.aim()->index(index, 0, model.rootIndex).data(*it); + return model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex).data(*it); } else if (role == QLatin1String("hasModelChildren")) { - return QVariant(model.aim()->hasChildren(model.aim()->index(index, 0, model.rootIndex))); + return QVariant(model.aim()->hasChildren(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex))); } else { return QVariant(); } @@ -503,7 +522,7 @@ public: QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override { return model - ? QVariant::fromValue(model.aim()->index(index, 0, model.rootIndex)) + ? QVariant::fromValue(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex)) : QVariant(); } @@ -558,6 +577,22 @@ public: } }; +void QQmlDMAbstractItemModelData::setModelIndex(int idx) +{ + QQmlDMCachedModelData::setModelIndex(idx); + + int prevRow = row; + int prevColumn = column; + + row = type->model->rowAt(idx); + column = type->model->columnAt(idx); + + if (row != prevRow) + emit rowChanged(); + if (column != prevColumn) + emit columnChanged(); +} + //----------------------------------------------------------------- // QQmlListAccessor //----------------------------------------------------------------- @@ -653,11 +688,16 @@ class VDMListDelegateDataType : public QQmlAdaptorModel::Accessors public: inline VDMListDelegateDataType() {} - int count(const QQmlAdaptorModel &model) const override + int rowCount(const QQmlAdaptorModel &model) const override { return model.list.count(); } + int columnCount(const QQmlAdaptorModel &) const override + { + return 1; + } + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { return role == QLatin1String("modelData") @@ -737,11 +777,16 @@ public: free(metaObject); } - int count(const QQmlAdaptorModel &model) const override + int rowCount(const QQmlAdaptorModel &model) const override { return model.list.count(); } + int columnCount(const QQmlAdaptorModel &) const override + { + return 1; + } + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { if (QObject *object = model.list.at(index).value<QObject *>()) @@ -955,7 +1000,38 @@ void QQmlAdaptorModel::invalidateModel(QQmlDelegateModel *vdm) bool QQmlAdaptorModel::isValid() const { - return accessors != &qt_vdm_null_accessors; + return accessors != &qt_vdm_null_accessors || rows.isValid(); +} + +int QQmlAdaptorModel::count() const +{ + return rowCount() * columnCount(); +} + +int QQmlAdaptorModel::rowCount() const +{ + if (rows.isValid()) + return rows.value; + return qMax(0, accessors->rowCount(*this)); +} + +int QQmlAdaptorModel::columnCount() const +{ + if (columns.isValid()) + return columns.value; + return qMax(isValid() ? 1 : 0, accessors->columnCount(*this)); +} + +int QQmlAdaptorModel::rowAt(int index) const +{ + int count = rowCount(); + return count <= 0 ? -1 : index % count; +} + +int QQmlAdaptorModel::columnAt(int index) const +{ + int count = rowCount(); + return count <= 0 ? -1 : index / count; } void QQmlAdaptorModel::objectDestroyed(QObject *) diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h index 8f773efa20..f0d3bcd186 100644 --- a/src/qml/util/qqmladaptormodel_p.h +++ b/src/qml/util/qqmladaptormodel_p.h @@ -56,6 +56,7 @@ #include "private/qqmllistaccessor_p.h" #include <private/qqmlguard_p.h> +#include <private/qqmlnullablevalue_p.h> QT_BEGIN_NAMESPACE @@ -73,7 +74,8 @@ public: public: inline Accessors() {} virtual ~Accessors(); - virtual int count(const QQmlAdaptorModel &) const { return 0; } + virtual int rowCount(const QQmlAdaptorModel &) const { return 0; } + virtual int columnCount(const QQmlAdaptorModel &) const { return 0; } virtual void cleanup(QQmlAdaptorModel &, QQmlDelegateModel * = nullptr) const {} virtual QVariant value(const QQmlAdaptorModel &, int, const QString &) const { @@ -102,6 +104,8 @@ public: virtual void fetchMore(QQmlAdaptorModel &) const {} }; + QQmlNullableValue<int> rows; + QQmlNullableValue<int> columns; const Accessors *accessors; QPersistentModelIndex rootIndex; QQmlListAccessor list; @@ -114,11 +118,15 @@ public: void invalidateModel(QQmlDelegateModel *vdm); bool isValid() const; + int count() const; + int rowCount() const; + int columnCount() const; + int rowAt(int index) const; + int columnAt(int index) const; inline QAbstractItemModel *aim() { return static_cast<QAbstractItemModel *>(object()); } inline const QAbstractItemModel *aim() const { return static_cast<const QAbstractItemModel *>(object()); } - inline int count() const { return qMax(0, accessors->count(*this)); } inline QVariant value(int index, const QString &role) const { return accessors->value(*this, index, role); } inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, int index) { |