aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp103
-rw-r--r--src/qml/qml/qqml.cpp207
-rw-r--r--src/qml/qml/qqmlprivate.h5
-rw-r--r--src/qmlcompiler/qqmljscodegenerator.cpp34
-rw-r--r--src/qmlcompiler/qqmljscodegenerator_p.h1
-rw-r--r--src/qmlcompiler/qqmljscompilepass_p.h6
-rw-r--r--src/qmlcompiler/qqmljsmetatypes_p.h12
-rw-r--r--src/qmlcompiler/qqmljsshadowcheck.cpp2
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader.cpp7
-rw-r--r--src/qmlcompiler/qqmljstypedescriptionreader_p.h1
-rw-r--r--tests/auto/qml/qmlcppcodegen/data/qmlUsing.qml6
-rw-r--r--tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp21
12 files changed, 214 insertions, 191 deletions
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index abe7a96512..b771a938e4 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -1711,8 +1711,19 @@ int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
return 1;
}
- if (QMetaType::canConvert(type, conversionMetaType))
+ if (QMetaType::canConvert(type, conversionMetaType)) {
+ if (conversionMetaType == QMetaType::fromType<QJSValue>()
+ || conversionMetaType == QMetaType::fromType<double>()
+ || conversionMetaType == QMetaType::fromType<QString>()) {
+ // Unspecific conversions receive lower score. You can convert anything
+ // to QString or double via toString() and valueOf(), respectively.
+ // And anything can be wrapped into QJSValue, but that's inefficient.
+ return 6;
+ }
+
+ // We have an explicitly defined conversion method to a non-boring type.
return 5;
+ }
return 10;
};
@@ -1726,6 +1737,33 @@ int MatchVariant(QMetaType conversionMetaType, Retrieve &&retrieve) {
static int MatchScore(const Value &actual, QMetaType conversionMetaType)
{
const int conversionType = conversionMetaType.id();
+ const auto convertibleScore = [&](QMetaType actualType) {
+ // There are a number of things we can do in JavaScript to subvert this, but
+ // if the conversion is not explicitly defined in C++, we don't want to prioritize it.
+ if (!QMetaType::canConvert(actualType, conversionMetaType))
+ return 10;
+
+ // You can convert anything to QJSValue, but that's inefficient.
+ // If we have a better option, we should use it.
+ if (conversionMetaType == QMetaType::fromType<QJSValue>())
+ return 9;
+
+ // You can also convert anything to QVariant, but that's also suboptimal.
+ // You can convert anything to string or double via toString() and valueOf().
+ // Those are also rather unspecific.
+ switch (conversionType) {
+ case QMetaType::QVariant:
+ case QMetaType::Double:
+ case QMetaType::QString:
+ return 9;
+ default:
+ break;
+ }
+
+ // We have an explicitly defined conversion method to a non-boring type.
+ return 8;
+ };
+
if (actual.isNumber()) {
switch (conversionType) {
case QMetaType::Double:
@@ -1751,7 +1789,9 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
case QMetaType::QJsonValue:
return 5;
default:
- return 10;
+ return convertibleScore(actual.isInteger()
+ ? QMetaType::fromType<int>()
+ : QMetaType::fromType<double>());
}
} else if (actual.isString()) {
switch (conversionType) {
@@ -1761,8 +1801,21 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
return 5;
case QMetaType::QUrl:
return 6; // we like to convert strings to URLs in QML
- default:
+ case QMetaType::Double:
+ case QMetaType::Float:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ case QMetaType::Char:
+ case QMetaType::UChar:
+ // QMetaType can natively convert strings to numbers.
+ // However, in the general case it's of course extremely lossy.
return 10;
+ default:
+ return convertibleScore(QMetaType::fromType<QString>());
}
} else if (actual.isBoolean()) {
switch (conversionType) {
@@ -1771,7 +1824,7 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
case QMetaType::QJsonValue:
return 5;
default:
- return 10;
+ return convertibleScore(QMetaType::fromType<bool>());
}
} else if (actual.as<DateObject>()) {
switch (conversionType) {
@@ -1782,23 +1835,26 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
case QMetaType::QTime:
return 2;
default:
- return 10;
+ return convertibleScore(QMetaType::fromType<QDateTime>());
}
} else if (actual.as<RegExpObject>()) {
switch (conversionType) {
#if QT_CONFIG(regularexpression)
case QMetaType::QRegularExpression:
return 0;
-#endif
default:
- return 10;
+ return convertibleScore(QMetaType::fromType<QRegularExpression>());
+#else
+ default:
+ return convertibleScore(QMetaType());
+#endif
}
} else if (actual.as<ArrayBuffer>()) {
switch (conversionType) {
case QMetaType::QByteArray:
return 0;
default:
- return 10;
+ return convertibleScore(QMetaType::fromType<QByteArray>());
}
} else if (actual.as<ArrayObject>()) {
switch (conversionType) {
@@ -1813,7 +1869,7 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
case QMetaType::QVector3D:
return 7;
default:
- return 10;
+ return convertibleScore(QMetaType());
}
} else if (actual.isNull()) {
switch (conversionType) {
@@ -1826,7 +1882,7 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
if (conversionMetaType.flags().testFlag(QMetaType::IsPointer))
return 0;
else
- return 10;
+ return convertibleScore(QMetaType());
}
}
} else if (const Object *obj = actual.as<Object>()) {
@@ -1849,7 +1905,8 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
return 0;
}
}
- return 10;
+
+ return convertibleScore(QMetaType::fromType<QObject *>());
}
if (const QQmlTypeWrapper *wrapper = obj->as<QQmlTypeWrapper>()) {
@@ -1871,14 +1928,15 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
}
}
- return 10;
+ return convertibleScore(QMetaType());
}
if (const Sequence *sequence = obj->as<Sequence>()) {
- if (SequencePrototype::metaTypeForSequence(sequence) == conversionMetaType)
+ const QMetaType sequenceType = SequencePrototype::metaTypeForSequence(sequence);
+ if (sequenceType == conversionMetaType)
return 1;
- else
- return 10;
+
+ return convertibleScore(sequenceType);
}
if (const QQmlValueTypeWrapper *wrapper = obj->as<QQmlValueTypeWrapper>()) {
@@ -1889,15 +1947,20 @@ static int MatchScore(const Value &actual, QMetaType conversionMetaType)
});
}
- if (conversionType == QMetaType::QJsonObject)
- return 5;
- if (conversionType == qMetaTypeId<QJSValue>())
+ if (conversionMetaType == QMetaType::fromType<QJSValue>())
return 0;
- if (conversionType == QMetaType::QVariantMap)
+
+ switch (conversionType) {
+ case QMetaType::QJsonObject:
+ case QMetaType::QVariantMap:
return 5;
+ default:
+ break;
+ }
+
}
- return 10;
+ return convertibleScore(QMetaType());
}
static int numDefinedArguments(CallData *callArgs)
diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp
index 26846bb68c..51a9c0816b 100644
--- a/src/qml/qml/qqml.cpp
+++ b/src/qml/qml/qqml.cpp
@@ -1915,169 +1915,38 @@ bool AOTCompiledContext::callQmlContextPropertyLookup(uint index, void **args, i
return false;
}
-enum MatchScore {
- NoMatch = 0x0,
- VariantMatch = 0x1,
- VariantExactMatch = 0x2,
- ExactMatch = 0x4,
-
- // VariantMatch and ExactMatch for different arguments are incompatible because the ExactMatch
- // tells us that the variant was not meant as a generic argument but rather as a concrete one.
- IncompatibleMatch = VariantMatch | ExactMatch,
-
- // If we're calling a scope method we know that it cannot be shadowed. Therefore an all-variant
- // method matched by an all-variant call is fine.
- ScopeAccepted = ExactMatch | VariantExactMatch,
-
- // If we're calling an object method it may be shadowed. We cannot nail down an all-variant
- // call to an all-variant method.
- ObjectAccepted = ExactMatch,
-
-};
-
-Q_DECLARE_FLAGS(MatchScores, MatchScore);
-
-static MatchScore overloadTypeMatch(QMetaType passed, QMetaType expected)
-{
- const bool isVariant = (passed == QMetaType::fromType<QVariant>());
- if (isTypeCompatible(passed, expected))
- return isVariant ? VariantExactMatch : ExactMatch;
- if (isVariant)
- return VariantMatch;
- return NoMatch;
-}
+enum MatchScore { NoMatch, VariantMatch, ExactMatch, };
static MatchScore resolveQObjectMethodOverload(
- QV4::QObjectMethod *method, QV4::Lookup *lookup, const QMetaType *types, int argc,
- MatchScore acceptedScores)
+ QV4::QObjectMethod *method, QV4::Lookup *lookup, int relativeMethodIndex)
{
Q_ASSERT(lookup->qobjectMethodLookup.method.get() == method->d());
const auto *d = method->d();
- for (int i = 0, end = d->methodCount; i != end; ++i) {
- const QMetaMethod metaMethod = d->methods[i].metaMethod();
- if (metaMethod.parameterCount() != argc)
- continue;
-
- MatchScores finalScore = NoMatch;
-
- if (!types[0].isValid()) {
- if (argc == 0) {
- // No arguments given and we're not interested in the return value:
- // The overload with 0 arguments matches (but it may still be shadowable).
- finalScore = VariantExactMatch;
- }
- } else {
- const MatchScore score = overloadTypeMatch(types[0], metaMethod.returnMetaType());
- if (score == NoMatch)
- continue;
- finalScore = score;
- }
-
- for (int j = 0; j < argc; ++j) {
- const MatchScore score
- = overloadTypeMatch(types[j + 1], metaMethod.parameterMetaType(j));
-
- if (score == NoMatch) {
- finalScore = NoMatch;
- break;
- }
-
- finalScore.setFlag(score);
- if (finalScore.testFlags(IncompatibleMatch)) {
- finalScore = NoMatch;
- break;
- }
- }
+ const int methodCount = d->methodCount;
- if (finalScore == NoMatch)
- continue;
-
- if (finalScore.testAnyFlags(acceptedScores)) {
- lookup->qobjectMethodLookup.propertyData = d->methods + i;
- return ExactMatch;
- }
+ if (relativeMethodIndex == -1 && methodCount == 1) {
+ // QML-declared signals do not have a meaningful method index and cannot be overloaded.
+ // They still show up as QObjectMethod rather than ArrowFunction. If they didn't, we
+ // wouldn't have to care.
+ Q_ASSERT(d->methods[0].metaMethod().methodType() == QMetaMethod::Signal);
+ lookup->qobjectMethodLookup.propertyData = d->methods;
+ return ExactMatch;
}
- // No adjusting of the lookup's propertyData here. We re-fetch the method on every call.
- // Furthermore, the first propertyData of the collection of possible overloads has the
- // isOverridden flag we use to determine whether to invalidate a lookup. Therefore, we
- // have to store that one if the method can be overridden (or shadowed).
- return VariantMatch;
-}
-
-static inline bool allTypesAreVariant(const QMetaType *types, int argc)
-{
- for (int i = 0; i <= argc; ++i) { // Yes, i <= argc, because of return type
- if (types[i] != QMetaType::fromType<QVariant>())
- return false;
- }
- return true;
-}
-
-static bool isArrowFunctionVariantCall(
- QV4::ArrowFunction *function, const QMetaType *types, int argc)
-{
- QV4::Function *v4Function = function->function();
- Q_ASSERT(v4Function);
-
- switch (v4Function->kind) {
- case QV4::Function::AotCompiled: {
- Q_ASSERT(argc + 1 == v4Function->aotCompiledFunction.types.size());
- const QMetaType *parameterTypes = v4Function->aotCompiledFunction.types.data();
-
- if (types[0].isValid() && !isTypeCompatible(types[0], parameterTypes[0])) {
- Q_ASSERT(allTypesAreVariant(types, argc));
- return true;
- }
-
- for (int i = 1; i <= argc; ++i) { // Yes, i <= argc, because of return type
- if (!isTypeCompatible(types[i], parameterTypes[i])) {
- Q_ASSERT(allTypesAreVariant(types, argc));
- return true;
- }
- }
-
- return false;
- }
- case QV4::Function::JsTyped: {
- const auto *compiledFunction = v4Function->compiledFunction;
- const QV4::CompiledData::Parameter *formals
- = v4Function->compiledFunction->formalsTable();
-
- if (types[0].isValid()
- && !isTypeCompatible(types[0], jsTypedFunctionArgument(
- v4Function->jsTypedFunction.types[0], compiledFunction->returnType))) {
- Q_ASSERT(allTypesAreVariant(types, argc));
- return true;
- }
-
- for (int i = 1; i <= argc; ++i) { // Yes, i <= argc, because of return type
- if (!isTypeCompatible(types[i], jsTypedFunctionArgument(
- v4Function->jsTypedFunction.types[i], formals[i - 1].type))) {
- Q_ASSERT(allTypesAreVariant(types, argc));
- return true;
- }
- }
+ for (int i = 0, end = d->methodCount; i != end; ++i) {
+ const QMetaMethod metaMethod = d->methods[i].metaMethod();
+ if (metaMethod.relativeMethodIndex() != relativeMethodIndex)
+ continue;
- return false;
- }
- case QV4::Function::JsUntyped: {
- // We can call untyped functions if we're not expecting a specific return value and don't
- // have to pass any arguments. The compiler verifies this.
- Q_ASSERT(v4Function->nFormals == 0);
- Q_ASSERT(!types[0].isValid() || types[0] == QMetaType::fromType<QVariant>());
- return types[0] == QMetaType::fromType<QVariant>();
- }
- case QV4::Function::Eval:
- break;
+ lookup->qobjectMethodLookup.propertyData = d->methods + i;
+ return ExactMatch;
}
- Q_UNREACHABLE_RETURN(false);
+ return NoMatch;
}
-void AOTCompiledContext::initCallQmlContextPropertyLookup(
- uint index, const QMetaType *types, int argc) const
+void AOTCompiledContext::initCallQmlContextPropertyLookup(uint index, int relativeMethodIndex) const
{
if (engine->hasError()) {
engine->handle()->amendException();
@@ -2092,7 +1961,7 @@ void AOTCompiledContext::initCallQmlContextPropertyLookup(
if (auto *method = function->as<QV4::QObjectMethod>()) {
Q_ASSERT(lookup->call == QV4::Lookup::Call::ContextGetterScopeObjectMethod);
method->d()->ensureMethodsCache(qmlScopeObject->metaObject());
- const auto match = resolveQObjectMethodOverload(method, lookup, types, argc, ScopeAccepted);
+ const auto match = resolveQObjectMethodOverload(method, lookup, relativeMethodIndex);
Q_ASSERT(match == ExactMatch);
return;
}
@@ -2199,8 +2068,36 @@ bool AOTCompiledContext::callObjectPropertyLookup(
return false;
}
+void AOTCompiledContext::initCallObjectPropertyLookupAsVariant(uint index, QObject *object) const
+{
+ if (engine->hasError()) {
+ engine->handle()->amendException();
+ return;
+ }
+
+ QV4::Lookup *lookup = compilationUnit->runtimeLookups + index;
+ QV4::Scope scope(engine->handle());
+ QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(scope.engine, object));
+ QV4::ScopedFunctionObject function(scope, lookup->getter(scope.engine, thisObject));
+ if (auto *method = function->as<QV4::QObjectMethod>()) {
+ method->d()->ensureMethodsCache(object->metaObject());
+ lookup->asVariant = true;
+ return;
+ }
+
+ if (function->as<QV4::ArrowFunction>()) {
+ // Can't have overloads of JavaScript functions.
+ lookup->asVariant = true;
+ return;
+ }
+
+ scope.engine->throwTypeError(
+ QStringLiteral("Property '%1' of object [object Object] is not a function")
+ .arg(compilationUnit->runtimeStrings[lookup->nameIndex]->toQString()));
+}
+
void AOTCompiledContext::initCallObjectPropertyLookup(
- uint index, QObject *object, const QMetaType *types, int argc) const
+ uint index, QObject *object, int relativeMethodIndex) const
{
if (engine->hasError()) {
engine->handle()->amendException();
@@ -2213,15 +2110,13 @@ void AOTCompiledContext::initCallObjectPropertyLookup(
QV4::ScopedFunctionObject function(scope, lookup->getter(scope.engine, thisObject));
if (auto *method = function->as<QV4::QObjectMethod>()) {
method->d()->ensureMethodsCache(object->metaObject());
- if (resolveQObjectMethodOverload(method, lookup, types, argc, ObjectAccepted) == VariantMatch)
- lookup->asVariant = true;
+ const auto match = resolveQObjectMethodOverload(method, lookup, relativeMethodIndex);
+ Q_ASSERT(match == ExactMatch);
return;
}
- if (QV4::ArrowFunction *arrowFunction = function->as<QV4::ArrowFunction>()) {
+ if (function->as<QV4::ArrowFunction>()) {
// Can't have overloads of JavaScript functions.
- if (isArrowFunctionVariantCall(arrowFunction, types, argc))
- lookup->asVariant = true;
return;
}
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index 8da2cb298c..8f512f7a93 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -693,7 +693,7 @@ namespace QQmlPrivate
// the exception should be propagated. If not, the original lookup can be tried again.
bool callQmlContextPropertyLookup(uint index, void **args, int argc) const;
- void initCallQmlContextPropertyLookup(uint index, const QMetaType *types, int argc) const;
+ void initCallQmlContextPropertyLookup(uint index, int relativeMethodIndex) const;
#if QT_QML_REMOVED_SINCE(6, 9)
bool callQmlContextPropertyLookup(
@@ -706,7 +706,8 @@ namespace QQmlPrivate
bool callObjectPropertyLookup(uint index, QObject *object, void **args, int argc) const;
void initCallObjectPropertyLookup(
- uint index, QObject *object, const QMetaType *types, int argc) const;
+ uint index, QObject *object, int relativeMethodIndex) const;
+ void initCallObjectPropertyLookupAsVariant(uint index, QObject *object) const;
#if QT_QML_REMOVED_SINCE(6, 9)
bool callObjectPropertyLookup(
diff --git a/src/qmlcompiler/qqmljscodegenerator.cpp b/src/qmlcompiler/qqmljscodegenerator.cpp
index 063140f92d..4d9c5811db 100644
--- a/src/qmlcompiler/qqmljscodegenerator.cpp
+++ b/src/qmlcompiler/qqmljscodegenerator.cpp
@@ -1738,13 +1738,11 @@ QString QQmlJSCodeGenerator::initAndCall(
int argc, int argv, const QString &callMethodTemplate, const QString &initMethodTemplate,
QString *outVar)
{
- QString types;
QString args;
if (m_state.changedRegisterIndex() == InvalidRegister ||
m_typeResolver->registerContains(
m_state.accumulatorOut(), m_typeResolver->voidType())) {
- types = u"QMetaType()"_s;
args = u"nullptr"_s;
} else {
*outVar = u"callResult"_s;
@@ -1753,14 +1751,25 @@ QString QQmlJSCodeGenerator::initAndCall(
m_body += u";\n";
args = contentPointer(m_state.accumulatorOut(), *outVar);
- types = contentType(m_state.accumulatorOut(), *outVar);
}
for (int i = 0; i < argc; ++i) {
const QQmlJSRegisterContent content = registerType(argv + i);
const QString var = registerVariable(argv + i);
args += u", "_s + contentPointer(content, var);
- types += u", "_s + contentType(content, var);
+ }
+
+ QString initMethod;
+
+ if (m_state.isShadowable()) {
+ initMethod = initMethodTemplate;
+ } else {
+ const QQmlJSMetaMethod method = m_state.accumulatorOut().methodCall();
+ Q_ASSERT(!method.isConstructor());
+
+ const QQmlJSMetaMethod::RelativeFunctionIndex relativeMethodIndex =
+ method.isJavaScriptFunction() ? method.jsFunctionIndex() : method.methodIndex();
+ initMethod = initMethodTemplate.arg(int(relativeMethodIndex));
}
return u"const auto doCall = [&]() {\n"_s
@@ -1768,8 +1777,7 @@ QString QQmlJSCodeGenerator::initAndCall(
+ u" return aotContext->"_s + callMethodTemplate.arg(u"args"_s).arg(argc) + u";\n"
+ u"};\n"_s
+ u"const auto doInit = [&]() {\n"_s
- + u" QMetaType types[] = {" + types + u"};\n"_s
- + u" aotContext->"_s + initMethodTemplate.arg(u"types"_s).arg(argc) + u";\n"
+ + u" aotContext->"_s + initMethod + u";\n"
+ u"};\n"_s;
}
@@ -2282,10 +2290,14 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
m_body += u"{\n"_s;
QString outVar;
+
+ const QString initMethodTemplate = m_state.isShadowable()
+ ? u"initCallObjectPropertyLookupAsVariant(%1, %2)"_s
+ : u"initCallObjectPropertyLookup(%1, %2, %3)"_s;
+
m_body += initAndCall(
argc, argv, u"callObjectPropertyLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
- u"initCallObjectPropertyLookup(%1, %2, %3, %4)"_s.arg(index).arg(inputPointer),
- &outVar);
+ initMethodTemplate.arg(index).arg(inputPointer), &outVar);
const QString lookup = u"doCall()"_s;
const QString initialization = u"doInit()"_s;
@@ -2331,8 +2343,10 @@ void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int a
return;
}
- if (m_state.accumulatorOut().isJavaScriptReturnValue())
+ if (m_state.accumulatorOut().isJavaScriptReturnValue()) {
reject(u"call to untyped JavaScript function"_s);
+ return;
+ }
AccumulatorConverter registers(this);
@@ -2340,7 +2354,7 @@ void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int a
QString outVar;
m_body += initAndCall(
argc, argv, u"callQmlContextPropertyLookup(%1, %2, %3)"_s.arg(index),
- u"initCallQmlContextPropertyLookup(%1, %2, %3)"_s.arg(index), &outVar);
+ u"initCallQmlContextPropertyLookup(%1, %2)"_s.arg(index), &outVar);
const QString lookup = u"doCall()"_s;
const QString initialization = u"doInit()"_s;
diff --git a/src/qmlcompiler/qqmljscodegenerator_p.h b/src/qmlcompiler/qqmljscodegenerator_p.h
index 0e8925c95b..5ee7e5c834 100644
--- a/src/qmlcompiler/qqmljscodegenerator_p.h
+++ b/src/qmlcompiler/qqmljscodegenerator_p.h
@@ -318,6 +318,7 @@ private:
QString initAndCall(
int argc, int argv, const QString &callMethodTemplate,
const QString &initMethodTemplate, QString *outVar);
+
QString castTargetName(const QQmlJSScope::ConstPtr &type) const;
bool inlineStringMethod(const QString &name, int base, int argc, int argv);
diff --git a/src/qmlcompiler/qqmljscompilepass_p.h b/src/qmlcompiler/qqmljscompilepass_p.h
index a7447e2c23..3244251075 100644
--- a/src/qmlcompiler/qqmljscompilepass_p.h
+++ b/src/qmlcompiler/qqmljscompilepass_p.h
@@ -79,6 +79,7 @@ public:
int changedRegisterIndex = InvalidRegister;
bool hasSideEffects = false;
bool isRename = false;
+ bool isShadowable = false;
};
using InstructionAnnotations = QFlatMap<int, InstructionAnnotation>;
@@ -247,6 +248,9 @@ public:
bool isRename() const { return m_isRename; }
void setIsRename(bool isRename) { m_isRename = isRename; }
+ bool isShadowable() const { return m_isShadowable; }
+ void setIsShadowable(bool isShadowable) { m_isShadowable = isShadowable; }
+
int renameSourceRegisterIndex() const
{
Q_ASSERT(m_isRename);
@@ -260,6 +264,7 @@ public:
int m_changedRegisterIndex = InvalidRegister;
bool m_hasSideEffects = false;
bool m_isRename = false;
+ bool m_isShadowable = false;
};
QQmlJSCompilePass(const QV4::Compiler::JSUnitGenerator *jsUnitGenerator,
@@ -363,6 +368,7 @@ protected:
newState.markSideEffects(instruction->second.hasSideEffects);
newState.setReadRegisters(instruction->second.readRegisters);
newState.setIsRename(instruction->second.isRename);
+ newState.setIsShadowable(instruction->second.isShadowable);
for (auto it = instruction->second.typeConversions.begin(),
end = instruction->second.typeConversions.end(); it != end; ++it) {
diff --git a/src/qmlcompiler/qqmljsmetatypes_p.h b/src/qmlcompiler/qqmljsmetatypes_p.h
index b58aff9847..9aa4081d35 100644
--- a/src/qmlcompiler/qqmljsmetatypes_p.h
+++ b/src/qmlcompiler/qqmljsmetatypes_p.h
@@ -306,6 +306,18 @@ public:
return m_relativeFunctionIndex;
}
+ void setMethodIndex(RelativeFunctionIndex index)
+ {
+ Q_ASSERT(!m_isConstructor);
+ m_relativeFunctionIndex = index;
+ }
+
+ RelativeFunctionIndex methodIndex() const
+ {
+ Q_ASSERT(!m_isConstructor);
+ return m_relativeFunctionIndex;
+ }
+
friend bool operator==(const QQmlJSMetaMethod &a, const QQmlJSMetaMethod &b)
{
return a.m_name == b.m_name && a.m_sourceLocation == b.m_sourceLocation
diff --git a/src/qmlcompiler/qqmljsshadowcheck.cpp b/src/qmlcompiler/qqmljsshadowcheck.cpp
index 469e4efa4a..0ceac934e2 100644
--- a/src/qmlcompiler/qqmljsshadowcheck.cpp
+++ b/src/qmlcompiler/qqmljsshadowcheck.cpp
@@ -190,6 +190,7 @@ QQmlJSShadowCheck::Shadowability QQmlJSShadowCheck::checkShadowing(
const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
const QQmlJSRegisterContent varContent = m_typeResolver->conversionType(varType);
InstructionAnnotation &currentAnnotation = m_annotations[currentInstructionOffset()];
+ currentAnnotation.isShadowable = true;
if (currentAnnotation.changedRegisterIndex != InvalidRegister) {
m_typeResolver->adjustOriginalType(
@@ -203,6 +204,7 @@ QQmlJSShadowCheck::Shadowability QQmlJSShadowCheck::checkShadowing(
if (it.key() != baseRegister)
it->second.content = m_typeResolver->convert(it->second.content, varContent);
}
+
return Shadowable;
}
default:
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader.cpp b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
index 6a0a207043..980a1448e4 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader.cpp
+++ b/src/qmlcompiler/qqmljstypedescriptionreader.cpp
@@ -167,6 +167,7 @@ void QQmlJSTypeDescriptionReader::readDependencies(UiScriptBinding *ast)
void QQmlJSTypeDescriptionReader::readComponent(UiObjectDefinition *ast)
{
m_currentCtorIndex = 0;
+ m_currentMethodIndex = 0;
QQmlJSScope::Ptr scope = QQmlJSScope::create();
QList<QQmlJSScope::Export> exports;
@@ -354,6 +355,12 @@ void QQmlJSTypeDescriptionReader::readSignalOrMethod(
return;
}
+ // Signals, slots and method share one index space. Constructors are separate.
+ // We also assume that the order and therefore the indexing of all methods is retained from
+ // moc's JSON output.
+ if (!metaMethod.isConstructor())
+ metaMethod.setMethodIndex(QQmlJSMetaMethod::RelativeFunctionIndex(m_currentMethodIndex++));
+
if (metaMethod.returnTypeName().isEmpty())
metaMethod.setReturnTypeName(QLatin1String("void"));
diff --git a/src/qmlcompiler/qqmljstypedescriptionreader_p.h b/src/qmlcompiler/qqmljstypedescriptionreader_p.h
index 2bbae61fd6..592e9ad4c0 100644
--- a/src/qmlcompiler/qqmljstypedescriptionreader_p.h
+++ b/src/qmlcompiler/qqmljstypedescriptionreader_p.h
@@ -77,6 +77,7 @@ private:
QList<QQmlJSExportedScope> *m_objects = nullptr;
QStringList *m_dependencies = nullptr;
int m_currentCtorIndex = 0;
+ int m_currentMethodIndex = 0;
};
QT_END_NAMESPACE
diff --git a/tests/auto/qml/qmlcppcodegen/data/qmlUsing.qml b/tests/auto/qml/qmlcppcodegen/data/qmlUsing.qml
index 8c7aca421e..29e66539f7 100644
--- a/tests/auto/qml/qmlcppcodegen/data/qmlUsing.qml
+++ b/tests/auto/qml/qmlcppcodegen/data/qmlUsing.qml
@@ -9,12 +9,18 @@ import TestTypes as T
T.UsingUserObject {
id: self
property int valA: val.a
+ // property int valB: val.getB()
property int myA: a
+ property int myB: getB()
property int myA2: self.a
+ property int myB2: self.getB()
function twiddle() {
val.a = 55
+ // val.setB(56)
a = 57
+ setB(58)
self.a = 59
+ self.setB(60)
}
}
diff --git a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
index 0ea7346746..6b6283b854 100644
--- a/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
+++ b/tests/auto/qml/qmlcppcodegen/tst_qmlcppcodegen.cpp
@@ -4235,24 +4235,39 @@ void tst_QmlCppCodegen::qmlUsing()
QVERIFY(u);
QCOMPARE(u->a(), 7);
+ QCOMPARE(u->getB(), 5);
QCOMPARE(u->val().a(), 24);
+ QCOMPARE(u->val().getB(), 25);
QCOMPARE(u->property("valA").toInt(), 24);
QCOMPARE(u->property("myA").toInt(), 7);
+ QCOMPARE(u->property("myB").toInt(), 5);
QCOMPARE(u->property("myA2").toInt(), 7);
+ QCOMPARE(u->property("myB2").toInt(), 5);
QList<int> as;
- QObject::connect(u, &UsingUserObject::aChanged, this, [&]() { as.append(u->a()); });
+ QList<int> bs;
+ QObject::connect(u, &UsingUserObject::aChanged, this, [&]() {
+ as.append(u->a());
+ bs.append(u->getB());
+ });
QMetaObject::invokeMethod(object.data(), "twiddle");
- const QList<int> expected = { 57, 59 };
- QCOMPARE(as, expected);
+ const QList<int> expectedA = { 57, 59 };
+ QCOMPARE(as, expectedA);
+
+ const QList<int> expectedB = { 5, 58 };
+ QCOMPARE(bs, expectedB);
QCOMPARE(u->a(), 59);
+ QCOMPARE(u->getB(), 60);
QCOMPARE(u->val().a(), 55);
+ QCOMPARE(u->val().getB(), 25);
QCOMPARE(u->property("valA").toInt(), 55);
QCOMPARE(u->property("myA").toInt(), 59);
+ QCOMPARE(u->property("myB").toInt(), 5); // Remains 5, due to lack of signaling
QCOMPARE(u->property("myA2").toInt(), 59);
+ QCOMPARE(u->property("myB2").toInt(), 5); // Remains 5, due to lack of signaling
}
void tst_QmlCppCodegen::qtfont()