diff options
19 files changed, 202 insertions, 24 deletions
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 883b21ab07..9021fd0284 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1947,11 +1947,7 @@ QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache { QQmlPropertyData *pd = cache->property(name, /*object*/nullptr, /*context*/nullptr); - // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time - if (!pd || pd->isFunction()) - return nullptr; - - if (!cache->isAllowedInRevision(pd)) + if (pd && !cache->isAllowedInRevision(pd)) return nullptr; return pd; @@ -2280,6 +2276,10 @@ QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &n if (_scopeObject) { QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name); if (data) { + // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time + if (data->isFunction()) + return Reference::fromName(this, name); + Reference base = Reference::fromStackSlot(this, _qmlContextSlot); Reference::PropertyCapturePolicy capturePolicy; if (!data->isConstant() && !data->isQmlBinding()) @@ -2293,6 +2293,10 @@ QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &n if (_contextObject) { QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name); if (data) { + // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time + if (data->isFunction()) + return Reference::fromName(this, name); + Reference base = Reference::fromStackSlot(this, _qmlContextSlot); Reference::PropertyCapturePolicy capturePolicy; if (!data->isConstant() && !data->isQmlBinding()) diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 2e4223de7d..8cdec2f6ee 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -764,11 +764,11 @@ struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator { int propertyIndex = 0; ~QObjectWrapperOwnPropertyKeyIterator() override = default; - PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; + PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; }; -PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) +PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs) { // Used to block access to QObject::destroyed() and QObject::deleteLater() from QML static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)"); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 634fbcbd97..4ef4fa2c9e 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -238,20 +238,20 @@ void Heap::RegExpCtor::clearLastMatch() lastMatchEnd = 0; } -static bool isRegExp(ExecutionEngine *e, const Value *arg) +static bool isRegExp(ExecutionEngine *e, const QV4::Value *arg) { - const Object *o = arg->objectValue(); + const QV4::Object *o = arg->objectValue(); if (!o) return false; - Value isRegExp = Value::fromReturnedValue(o->get(e->symbol_match())); + QV4::Value isRegExp = QV4::Value::fromReturnedValue(o->get(e->symbol_match())); if (!isRegExp.isUndefined()) return isRegExp.toBoolean(); const RegExpObject *re = o->as<RegExpObject>(); return re ? true : false; } -uint parseFlags(Scope &scope, const Value *f) +uint parseFlags(Scope &scope, const QV4::Value *f) { uint flags = CompiledData::RegExp::RegExp_NoFlags; if (!f->isUndefined()) { @@ -546,13 +546,13 @@ static int advanceStringIndex(int index, const QString &str, bool unicode) return index; } -static void advanceLastIndexOnEmptyMatch(ExecutionEngine *e, bool unicode, Object *rx, const String *matchString, const QString &str) +static void advanceLastIndexOnEmptyMatch(ExecutionEngine *e, bool unicode, QV4::Object *rx, const String *matchString, const QString &str) { Scope scope(e); if (matchString->d()->length() == 0) { - ScopedValue v(scope, rx->get(scope.engine->id_lastIndex())); + QV4::ScopedValue v(scope, rx->get(scope.engine->id_lastIndex())); int lastIndex = advanceStringIndex(v->toLength(), str, unicode); - if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(lastIndex))) + if (!rx->put(scope.engine->id_lastIndex(), QV4::Value::fromInt32(lastIndex))) scope.engine->throwTypeError(); } } diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index f6b9c5ba94..03f351b9e4 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -112,11 +112,11 @@ bool StringObject::virtualDeleteProperty(Managed *m, PropertyKey id) struct StringObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator { ~StringObjectOwnPropertyKeyIterator() override = default; - PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; + PropertyKey next(const QV4::Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; }; -PropertyKey StringObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) +PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Property *pd, PropertyAttributes *attrs) { const StringObject *s = static_cast<const StringObject *>(o); uint slen = s->d()->string->toQString().length(); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 29814246db..1cca50f6c1 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -377,7 +377,7 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs) if (lhs.m()->internalClass->vtable->isString) return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs; accumulator = lhs; - lhs = Value::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT)); + lhs = QV4::Value::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT)); goto redo; case QV4::Value::QT_Empty: Q_UNREACHABLE(); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index ea4a69f05d..ba8d5831ad 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -847,21 +847,40 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const } } + QSet<QString> localEnums; + const QMetaObject *localMetaObject = nullptr; + // Add any enum values defined by this class, overwriting any inherited values for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) { QMetaEnum e = metaObject->enumerator(ii); const bool isScoped = e.isScoped(); QStringHash<int> *scoped = isScoped ? new QStringHash<int>() : nullptr; + // We allow enums in sub-classes to overwrite enums from base-classes, such as + // ListView.Center (from enum PositionMode) overwriting Item.Center (from enum TransformOrigin). + // This is acceptable because the _use_ of the enum from the QML side requires qualification + // anyway, i.e. ListView.Center vs. Item.Center. + // However if a class defines two enums with the same value, then that must produce a warning + // because it represents a valid conflict. + if (e.enclosingMetaObject() != localMetaObject) { + localEnums.clear(); + localMetaObject = e.enclosingMetaObject(); + } + for (int jj = 0; jj < e.keyCount(); ++jj) { const QString key = QString::fromUtf8(e.key(jj)); const int value = e.value(jj); if (!isScoped || (regType == QQmlType::CppType && extraData.cd->registerEnumClassesUnscoped)) { - if (enums.contains(key)) { - qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData()); - createEnumConflictReport(metaObject, key); - } - enums.insert(key, value); + if (localEnums.contains(key)) { + auto existingEntry = enums.find(key); + if (existingEntry != enums.end() && existingEntry.value() != value) { + qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData()); + createEnumConflictReport(metaObject, key); + } + } else { + localEnums.insert(key); + } + enums.insert(key, value); } if (isScoped) scoped->insert(key, value); diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp index c852c16509..3d8c5e0507 100644 --- a/src/quick/util/qquickstategroup.cpp +++ b/src/quick/util/qquickstategroup.cpp @@ -302,10 +302,18 @@ void QQuickStateGroup::componentComplete() Q_D(QQuickStateGroup); d->componentComplete = true; + QSet<QString> names; for (int ii = 0; ii < d->states.count(); ++ii) { QQuickState *state = d->states.at(ii); if (!state->isNamed()) state->setName(QLatin1String("anonymousState") + QString::number(++d->unnamedCount)); + + const QString stateName = state->name(); + if (names.contains(stateName)) { + qmlWarning(state->parent()) << "Found duplicate state name: " << stateName; + } else { + names << stateName; + } } if (d->updateAutoState()) { diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables.mjs b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables.mjs new file mode 100644 index 0000000000..19c012d19b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables.mjs @@ -0,0 +1,31 @@ +export function runTest(libraryUnderTest) { + let state1 = state(libraryUnderTest); + try { modifyFromOutside(libraryUnderTest); } catch (e) {} + let state2 = state(libraryUnderTest); + try { modifyFromInside(libraryUnderTest); } catch (e) {} + let state3 = state(libraryUnderTest); + return state1 + " " + state2 + " " + state3; +} + +function stringify(value) { + let s = "?"; + if (value !== undefined) + s = value.toString(); + return s; +} + +function state(libraryUnderTest) { + return (stringify(libraryUnderTest.varValue) + + stringify(libraryUnderTest.letValue) + + stringify(libraryUnderTest.constValue)); +} + +function modifyFromOutside(libraryUnderTest) { + ++libraryUnderTest.varValue; + ++libraryUnderTest.letValue; + ++libraryUnderTest.constValue; +} + +function modifyFromInside(libraryUnderTest) { + libraryUnderTest.incrementAll(); +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.mjs b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.mjs new file mode 100644 index 0000000000..b6eb1a2623 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.mjs @@ -0,0 +1,8 @@ +export var varValue = 0; +export let letValue = 0; +export const constValue = 0; +export function incrementAll() { + ++varValue; + ++letValue; + ++constValue; +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.qml b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.qml new file mode 100644 index 0000000000..bb4e759cbf --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_module.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import "importLexicalVariables.mjs" as TestRunner +import "importLexicalVariables_module.mjs" as LibraryUnderTest + +QtObject { + id: root + function runTest() { + return TestRunner.runTest(LibraryUnderTest); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.js b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.js new file mode 100644 index 0000000000..f8c215a5e7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.js @@ -0,0 +1,9 @@ +.pragma library +var varValue = 0; +let letValue = 0; +const constValue = 0; +function incrementAll() { + ++varValue; + ++letValue; + ++constValue; +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.qml b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.qml new file mode 100644 index 0000000000..1b3fe7fa2e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_pragmaLibrary.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import "importLexicalVariables.mjs" as TestRunner +import "importLexicalVariables_pragmaLibrary.js" as LibraryUnderTest + +QtObject { + id: root + function runTest() { + return TestRunner.runTest(LibraryUnderTest); + } +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.js b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.js new file mode 100644 index 0000000000..4fb68abc87 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.js @@ -0,0 +1,8 @@ +var varValue = 0; +let letValue = 0; +const constValue = 0; +function incrementAll() { + ++varValue; + ++letValue; + ++constValue; +} diff --git a/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.qml b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.qml new file mode 100644 index 0000000000..263511e802 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/importLexicalVariables_script.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import "importLexicalVariables.mjs" as TestRunner +import "importLexicalVariables_script.js" as LibraryUnderTest + +QtObject { + id: root + function runTest() { + return TestRunner.runTest(LibraryUnderTest); + } +} diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index cf3eecff6d..8f388fcac6 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -357,6 +357,8 @@ private slots: void jumpStrictNotEqualUndefined(); void removeBindingsWithNoDependencies(); void temporaryDeadZone(); + void importLexicalVariables_data(); + void importLexicalVariables(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -8812,6 +8814,38 @@ void tst_qqmlecmascript::temporaryDeadZone() QVERIFY(v.isError()); } +void tst_qqmlecmascript::importLexicalVariables_data() +{ + QTest::addColumn<QUrl>("testFile"); + QTest::addColumn<QString>("expected"); + + QTest::newRow("script") + << testFileUrl("importLexicalVariables_script.qml") + << QStringLiteral("0?? 1?? 2??"); + QTest::newRow("pragmaLibrary") + << testFileUrl("importLexicalVariables_pragmaLibrary.qml") + << QStringLiteral("0?? 1?? 2??"); + QTest::newRow("module") + << testFileUrl("importLexicalVariables_module.qml") + << QStringLiteral("000 000 110"); +} + +void tst_qqmlecmascript::importLexicalVariables() +{ + QFETCH(QUrl, testFile); + QFETCH(QString, expected); + + QQmlEngine engine; + QQmlComponent component(&engine, testFile); + QScopedPointer<QObject> object(component.create()); + QVERIFY(object != nullptr); + QVERIFY(!component.isError()); + + QVariant result; + QMetaObject::invokeMethod(object.data(), "runTest", Qt::DirectConnection, Q_RETURN_ARG(QVariant, result)); + QCOMPARE(result, QVariant(expected)); +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" diff --git a/tests/auto/qml/qqmllanguage/data/polymorphicFunctionLookup.qml b/tests/auto/qml/qqmllanguage/data/polymorphicFunctionLookup.qml new file mode 100644 index 0000000000..4a3cc52793 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/polymorphicFunctionLookup.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 +QtObject { + id: root + property bool testFunc; + property bool ok: false + property QtObject subObject: QtObject { + function testFunc() + { + root.ok = true + } + + Component.onCompleted: testFunc() + } +} diff --git a/tests/auto/qml/qqmllanguage/testtypes.h b/tests/auto/qml/qqmllanguage/testtypes.h index d890668655..bb6e9582c2 100644 --- a/tests/auto/qml/qqmllanguage/testtypes.h +++ b/tests/auto/qml/qqmllanguage/testtypes.h @@ -1392,7 +1392,7 @@ class ScopedEnumsWithNameClash public: enum class ScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3, OtherScopedEnum }; - enum class OtherScopedEnum : int { ScopedVal1, ScopedVal2, ScopedVal3 }; + enum class OtherScopedEnum : int { ScopedVal1 = 10, ScopedVal2 = 11, ScopedVal3 = 12 }; }; class ScopedEnumsWithResolvedNameClash diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 157fd500ad..7a8de739f4 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -298,6 +298,8 @@ private slots: void retrieveQmlTypeId(); + void polymorphicFunctionLookup(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -5049,6 +5051,17 @@ void tst_qqmllanguage::retrieveQmlTypeId() QVERIFY(qmlTypeId("Test", 1, 0, "MyTypeObjectSingleton") >= 0); } +void tst_qqmllanguage::polymorphicFunctionLookup() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("polymorphicFunctionLookup.qml")); + VERIFY_ERRORS(0); + QScopedPointer<QObject> o(component.create()); + QVERIFY(!o.isNull()); + + QVERIFY(o->property("ok").toBool()); +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index ae02352293..ff796a5bd8 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -2676,7 +2676,7 @@ void tst_QQuickListView::sectionsSnap_data() QTest::addColumn<int>("duration"); QTest::newRow("drag") << QQuickListView::NoSnap << QPoint(100, 45) << 500; - QTest::newRow("flick") << QQuickListView::SnapOneItem << QPoint(100, 75) << 50; + QTest::newRow("flick") << QQuickListView::SnapOneItem << QPoint(100, 60) << 100; } void tst_QQuickListView::sectionsSnap() |