QmlCompiler: Relax shadowing check

If we detect a property or method as potentially shadowed, we don't have
to abandon all hope. We can still retrieve it as untyped var. Since
there are a number of things we can do with untyped var, this may still
be useful.

In the same sense, we need to treat function calls as untyped when the
function in question can be shadowed. Calling functions with var
arguments and return types leads to some more interesting situations in
the call frame setup, so we fix that, too.

Task-number: QTBUG-112480
Change-Id: I238d1cf04951f390c73e14ed9e299f2aa72b68cb
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2023-04-03 10:15:07 +02:00
parent 961d11e55f
commit 46cc70e2aa
13 changed files with 372 additions and 46 deletions

View File

@ -201,6 +201,21 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V
return o->get(name); return o->get(name);
} }
ReturnedValue Lookup::getterFallbackAsVariant(
Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (&Lookup::getterFallback == &Lookup::getterFallbackAsVariant) {
// Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
// equal. As a result, the pointers are the same, which wreaks havoc on the logic that
// decides how to retrieve the property.
qFatal("Your C++ compiler is broken.");
}
// This getter just marks the presence of a fallback lookup with variant conversion.
// It only does anything with it when running AOT-compiled code.
return getterFallback(l, engine, object);
}
ReturnedValue Lookup::getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object) ReturnedValue Lookup::getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object)
{ {
// we can safely cast to a QV4::Object here. If object is actually a string, // we can safely cast to a QV4::Object here. If object is actually a string,
@ -401,6 +416,21 @@ ReturnedValue Lookup::getterQObject(Lookup *lookup, ExecutionEngine *engine, con
return QObjectWrapper::lookupPropertyGetterImpl(lookup, engine, object, flags, revertLookup); return QObjectWrapper::lookupPropertyGetterImpl(lookup, engine, object, flags, revertLookup);
} }
ReturnedValue Lookup::getterQObjectAsVariant(
Lookup *lookup, ExecutionEngine *engine, const Value &object)
{
if (&Lookup::getterQObject == &Lookup::getterQObjectAsVariant) {
// Certain compilers, e.g. MSVC, will "helpfully" deduplicate methods that are completely
// equal. As a result, the pointers are the same, which wreaks havoc on the logic that
// decides how to retrieve the property.
qFatal("Your C++ compiler is broken.");
}
// This getter marks the presence of a qobjectlookup with variant conversion.
// It only does anything with it when running AOT-compiled code.
return getterQObject(lookup, engine, object);
}
ReturnedValue Lookup::getterQObjectMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object) ReturnedValue Lookup::getterQObjectMethod(Lookup *lookup, ExecutionEngine *engine, const Value &object)
{ {
const auto revertLookup = [lookup, engine, &object]() { const auto revertLookup = [lookup, engine, &object]() {
@ -559,6 +589,14 @@ bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, c
return o->put(name, value); return o->put(name, value);
} }
bool Lookup::setterFallbackAsVariant(
Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
// This setter just marks the presence of a fallback lookup with QVariant conversion.
// It only does anything with it when running AOT-compiled code.
return setterFallback(l, engine, object, value);
}
bool Lookup::setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) bool Lookup::setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{ {
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject()); Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
@ -622,6 +660,15 @@ bool Lookup::setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, co
return QV4::Lookup::setterFallback(l, engine, object, v); return QV4::Lookup::setterFallback(l, engine, object, v);
} }
bool Lookup::setterQObjectAsVariant(
Lookup *l, ExecutionEngine *engine, Value &object, const Value &v)
{
// This setter marks the presence of a qobjectlookup with QVariant conversion.
// It only does anything with it when running AOT-compiled code.
return QV4::Lookup::setterFallback(l, engine, object, v);
}
bool Lookup::arrayLengthSetter(Lookup *, ExecutionEngine *engine, Value &object, const Value &value) bool Lookup::arrayLengthSetter(Lookup *, ExecutionEngine *engine, Value &object, const Value &value)
{ {
Q_ASSERT(object.isObject() && static_cast<Object &>(object).isArrayObject()); Q_ASSERT(object.isObject() && static_cast<Object &>(object).isArrayObject());

View File

@ -165,6 +165,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterFallbackAsVariant(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object);
@ -178,6 +179,7 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterQObject(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterQObject(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterQObjectAsVariant(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterQObjectMethod(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterQObjectMethod(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object);
@ -192,11 +194,13 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
Q_NEVER_INLINE static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); Q_NEVER_INLINE static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterFallbackAsVariant(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0MemberData(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool setterQObject(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterQObjectAsVariant(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
void markObjects(MarkStack *stack) { void markObjects(MarkStack *stack) {
@ -216,7 +220,9 @@ struct Q_QML_PRIVATE_EXPORT Lookup {
|| getter == QQmlTypeWrapper::lookupSingletonProperty || getter == QQmlTypeWrapper::lookupSingletonProperty
|| setter == setterQObject || setter == setterQObject
|| qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty || qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty
|| qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty) { || qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectProperty
|| getter == getterQObjectAsVariant
|| setter == setterQObjectAsVariant) {
if (const QQmlPropertyCache *pc = qobjectLookup.propertyCache) if (const QQmlPropertyCache *pc = qobjectLookup.propertyCache)
pc->release(); pc->release();
} else if (getter == getterQObjectMethod } else if (getter == getterQObjectMethod

View File

@ -416,8 +416,11 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
Q_ALLOCA_DECLARE(void *, transformedArguments); Q_ALLOCA_DECLARE(void *, transformedArguments);
for (qsizetype i = 0; i < numFunctionArguments; ++i) { for (qsizetype i = 0; i < numFunctionArguments; ++i) {
const bool isValid = frame->argc() > i;
const QMetaType frameType = isValid ? frame->argTypes()[i] : QMetaType();
const QMetaType argumentType = function->typedFunction->argumentTypes[i]; const QMetaType argumentType = function->typedFunction->argumentTypes[i];
if (frame->argc() > i && argumentType == frame->argTypes()[i]) if (isValid && argumentType == frameType)
continue; continue;
if (transformedArguments == nullptr) { if (transformedArguments == nullptr) {
@ -430,22 +433,41 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
continue; continue;
} }
void *frameVal = isValid ? frame->argv()[i] : nullptr;
if (isValid && frameType == QMetaType::fromType<QVariant>()) {
QVariant *variant = static_cast<QVariant *>(frameVal);
const QMetaType variantType = variant->metaType();
if (variantType == argumentType) {
// Slightly nasty, but we're allowed to do this.
// We don't want to destruct() the QVariant's data() below.
transformedArguments[i] = frame->argv()[i] = variant->data();
} else {
Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
argumentType.construct(arg);
QMetaType::convert(variantType, variant->data(), argumentType, arg);
transformedArguments[i] = arg;
}
continue;
}
Q_ALLOCA_VAR(void, arg, argumentType.sizeOf()); Q_ALLOCA_VAR(void, arg, argumentType.sizeOf());
if (argumentType == QMetaType::fromType<QVariant>()) { if (argumentType == QMetaType::fromType<QVariant>()) {
if (frame->argc() > i) if (isValid)
new (arg) QVariant(frame->argTypes()[i], frame->argv()[i]); new (arg) QVariant(frameType, frameVal);
else else
new (arg) QVariant(); new (arg) QVariant();
} else if (argumentType == QMetaType::fromType<QJSPrimitiveValue>()) { } else if (argumentType == QMetaType::fromType<QJSPrimitiveValue>()) {
if (frame->argc() > i) if (isValid)
new (arg) QJSPrimitiveValue(frame->argTypes()[i], frame->argv()[i]); new (arg) QJSPrimitiveValue(frameType, frameVal);
else else
new (arg) QJSPrimitiveValue(); new (arg) QJSPrimitiveValue();
} else { } else {
argumentType.construct(arg); argumentType.construct(arg);
if (frame->argc() > i) if (isValid)
QMetaType::convert(frame->argTypes()[i], frame->argv()[i], argumentType, arg); QMetaType::convert(frameType, frameVal, argumentType, arg);
} }
transformedArguments[i] = arg; transformedArguments[i] = arg;
@ -453,12 +475,19 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
const QMetaType returnType = function->typedFunction->returnType; const QMetaType returnType = function->typedFunction->returnType;
const QMetaType frameReturn = frame->returnType(); const QMetaType frameReturn = frame->returnType();
bool returnsQVariantWrapper = false;
Q_ALLOCA_DECLARE(void, transformedResult); Q_ALLOCA_DECLARE(void, transformedResult);
if (frame->returnValue() && returnType != frameReturn) { if (frame->returnValue() && returnType != frameReturn) {
if (returnType.sizeOf() > 0) if (frameReturn == QMetaType::fromType<QVariant>()) {
void *returnValue = frame->returnValue();
new (returnValue) QVariant(returnType);
transformedResult = static_cast<QVariant *>(returnValue)->data();
returnsQVariantWrapper = true;
} else if (returnType.sizeOf() > 0) {
Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf()); Q_ALLOCA_ASSIGN(void, transformedResult, returnType.sizeOf());
else } else {
transformedResult = frame; // Some non-null marker value transformedResult = frame; // Some non-null marker value
}
} }
QQmlPrivate::AOTCompiledContext aotContext; QQmlPrivate::AOTCompiledContext aotContext;
@ -475,7 +504,7 @@ void VME::exec(MetaTypesStackFrame *frame, ExecutionEngine *engine)
&aotContext, transformedResult ? transformedResult : frame->returnValue(), &aotContext, transformedResult ? transformedResult : frame->returnValue(),
transformedArguments ? transformedArguments : frame->argv()); transformedArguments ? transformedArguments : frame->argv());
if (transformedResult) { if (transformedResult && !returnsQVariantWrapper) {
// Shortcut the common case of the AOT function returning a more generic QObject pointer // Shortcut the common case of the AOT function returning a more generic QObject pointer
// that we need to QObject-cast. No need to construct or destruct anything in that case. // that we need to QObject-cast. No need to construct or destruct anything in that case.
if ((frameReturn.flags() & QMetaType::PointerToQObject) if ((frameReturn.flags() & QMetaType::PointerToQObject)

View File

@ -880,7 +880,8 @@ static bool inherits(const QQmlPropertyCache *descendent, const QQmlPropertyCach
enum class ObjectPropertyResult { OK, NeedsInit, Deleted }; enum class ObjectPropertyResult { OK, NeedsInit, Deleted };
static ObjectPropertyResult loadObjectProperty( template<bool StrictType = false>
ObjectPropertyResult loadObjectProperty(
QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext) QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
{ {
QQmlData *qmlData = QQmlData::get(object); QQmlData *qmlData = QQmlData::get(object);
@ -890,8 +891,12 @@ static ObjectPropertyResult loadObjectProperty(
return ObjectPropertyResult::Deleted; return ObjectPropertyResult::Deleted;
Q_ASSERT(!QQmlData::wasDeleted(object)); Q_ASSERT(!QQmlData::wasDeleted(object));
const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache; const QQmlPropertyCache *propertyCache = l->qobjectLookup.propertyCache;
if (!inherits(qmlData->propertyCache.data(), propertyCache)) if (StrictType) {
if (qmlData->propertyCache.data() != propertyCache)
return ObjectPropertyResult::NeedsInit;
} else if (!inherits(qmlData->propertyCache.data(), propertyCache)) {
return ObjectPropertyResult::NeedsInit; return ObjectPropertyResult::NeedsInit;
}
const QQmlPropertyData *property = l->qobjectLookup.propertyData; const QQmlPropertyData *property = l->qobjectLookup.propertyData;
const int coreIndex = property->coreIndex(); const int coreIndex = property->coreIndex();
@ -930,7 +935,35 @@ static ObjectPropertyResult loadFallbackProperty(
return ObjectPropertyResult::OK; return ObjectPropertyResult::OK;
} }
template<typename Op> ObjectPropertyResult loadObjectAsVariant(
QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
{
QVariant *variant = static_cast<QVariant *>(target);
const QMetaType propType = l->qobjectLookup.propertyData->propType();
if (propType == QMetaType::fromType<QVariant>())
return loadObjectProperty<true>(l, object, variant, qmlContext);
*variant = QVariant(propType);
return loadObjectProperty<true>(l, object, variant->data(), qmlContext);
}
ObjectPropertyResult loadFallbackAsVariant(
QV4::Lookup *l, QObject *object, void *target, QQmlContextData *qmlContext)
{
const QMetaObject *metaObject
= reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
Q_ASSERT(metaObject);
QVariant *variant = static_cast<QVariant *>(target);
const QMetaType propType = metaObject->property(l->qobjectFallbackLookup.coreIndex).metaType();
if (propType == QMetaType::fromType<QVariant>())
return loadFallbackProperty(l, object, variant, qmlContext);
*variant = QVariant(propType);
return loadFallbackProperty(l, object, variant->data(), qmlContext);
}
template<bool StrictType, typename Op>
static ObjectPropertyResult changeObjectProperty(QV4::Lookup *l, QObject *object, Op op) static ObjectPropertyResult changeObjectProperty(QV4::Lookup *l, QObject *object, Op op)
{ {
const QQmlData *qmlData = QQmlData::get(object); const QQmlData *qmlData = QQmlData::get(object);
@ -939,24 +972,30 @@ static ObjectPropertyResult changeObjectProperty(QV4::Lookup *l, QObject *object
if (qmlData->isQueuedForDeletion) if (qmlData->isQueuedForDeletion)
return ObjectPropertyResult::Deleted; return ObjectPropertyResult::Deleted;
Q_ASSERT(!QQmlData::wasDeleted(object)); Q_ASSERT(!QQmlData::wasDeleted(object));
if (!inherits(qmlData->propertyCache.data(), l->qobjectLookup.propertyCache)) if (StrictType) {
if (qmlData->propertyCache.data() != l->qobjectLookup.propertyCache)
return ObjectPropertyResult::NeedsInit;
} else if (!inherits(qmlData->propertyCache.data(), l->qobjectLookup.propertyCache)) {
return ObjectPropertyResult::NeedsInit; return ObjectPropertyResult::NeedsInit;
}
const QQmlPropertyData *property = l->qobjectLookup.propertyData; const QQmlPropertyData *property = l->qobjectLookup.propertyData;
QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex())); QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
op(property); op(property);
return ObjectPropertyResult::OK; return ObjectPropertyResult::OK;
} }
template<bool StrictType = false>
static ObjectPropertyResult resetObjectProperty(QV4::Lookup *l, QObject *object) static ObjectPropertyResult resetObjectProperty(QV4::Lookup *l, QObject *object)
{ {
return changeObjectProperty(l, object, [&](const QQmlPropertyData *property) { return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
property->resetProperty(object, {}); property->resetProperty(object, {});
}); });
} }
template<bool StrictType = false>
static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value) static ObjectPropertyResult storeObjectProperty(QV4::Lookup *l, QObject *object, void *value)
{ {
return changeObjectProperty(l, object, [&](const QQmlPropertyData *property) { return changeObjectProperty<StrictType>(l, object, [&](const QQmlPropertyData *property) {
property->writeProperty(object, value, {}); property->writeProperty(object, value, {});
}); });
} }
@ -1061,10 +1100,55 @@ static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
return true; return true;
} }
static ObjectPropertyResult storeObjectAsVariant(
QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
{
QVariant *variant = static_cast<QVariant *>(value);
const QMetaType propType = l->qobjectLookup.propertyData->propType();
if (propType == QMetaType::fromType<QVariant>())
return storeObjectProperty<true>(l, object, variant);
if (!variant->isValid())
return resetObjectProperty<true>(l, object);
if (isTypeCompatible(variant->metaType(), propType))
return storeObjectProperty<true>(l, object, variant->data());
QVariant converted(propType);
v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data());
return storeObjectProperty<true>(l, object, converted.data());
}
static ObjectPropertyResult storeFallbackAsVariant(
QV4::ExecutionEngine *v4, QV4::Lookup *l, QObject *object, void *value)
{
QVariant *variant = static_cast<QVariant *>(value);
const QMetaObject *metaObject
= reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
Q_ASSERT(metaObject);
const QMetaType propType = metaObject->property(l->qobjectFallbackLookup.coreIndex).metaType();
if (propType == QMetaType::fromType<QVariant>())
return storeFallbackProperty(l, object, variant);
if (!propType.isValid())
return resetFallbackProperty(l, object);
if (isTypeCompatible(variant->metaType(), propType))
return storeFallbackProperty(l, object, variant->data());
QVariant converted(propType);
v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data());
return storeFallbackProperty(l, object, converted.data());
}
enum class ObjectLookupResult { enum class ObjectLookupResult {
Failure, Failure,
Object, Object,
Fallback Fallback,
ObjectAsVariant,
FallbackAsVariant,
}; };
static ObjectLookupResult initObjectLookup( static ObjectLookupResult initObjectLookup(
@ -1094,6 +1178,7 @@ static ObjectLookupResult initObjectLookup(
name.getPointer(), object, aotContext->qmlContext); name.getPointer(), object, aotContext->qmlContext);
} }
const bool doVariantLookup = type == QMetaType::fromType<QVariant>();
if (!property) { if (!property) {
const QMetaObject *metaObject = object->metaObject(); const QMetaObject *metaObject = object->metaObject();
if (!metaObject) if (!metaObject)
@ -1105,7 +1190,7 @@ static ObjectLookupResult initObjectLookup(
return ObjectLookupResult::Failure; return ObjectLookupResult::Failure;
const QMetaProperty property = metaObject->property(coreIndex); const QMetaProperty property = metaObject->property(coreIndex);
if (!isTypeCompatible(type, property.metaType())) if (!doVariantLookup && !isTypeCompatible(type, property.metaType()))
return ObjectLookupResult::Failure; return ObjectLookupResult::Failure;
l->releasePropertyCache(); l->releasePropertyCache();
@ -1115,16 +1200,21 @@ static ObjectLookupResult initObjectLookup(
l->qobjectFallbackLookup.notifyIndex = l->qobjectFallbackLookup.notifyIndex =
QMetaObjectPrivate::signalIndex(property.notifySignal()); QMetaObjectPrivate::signalIndex(property.notifySignal());
l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0; l->qobjectFallbackLookup.isConstant = property.isConstant() ? 1 : 0;
return ObjectLookupResult::Fallback; return doVariantLookup
? ObjectLookupResult::FallbackAsVariant
: ObjectLookupResult::Fallback;
} }
if (!isTypeCompatible(type, property->propType())) if (!doVariantLookup && !isTypeCompatible(type, property->propType()))
return ObjectLookupResult::Failure; return ObjectLookupResult::Failure;
Q_ASSERT(ddata->propertyCache); Q_ASSERT(ddata->propertyCache);
QV4::setupQObjectLookup(l, ddata, property); QV4::setupQObjectLookup(l, ddata, property);
return ObjectLookupResult::Object;
return doVariantLookup
? ObjectLookupResult::ObjectAsVariant
: ObjectLookupResult::Object;
} }
static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit, static bool initValueLookup(QV4::Lookup *l, QV4::ExecutableCompilationUnit *compilationUnit,
@ -1161,14 +1251,16 @@ bool AOTCompiledContext::captureLookup(uint index, QObject *object) const
QV4::Lookup *l = compilationUnit->runtimeLookups + index; QV4::Lookup *l = compilationUnit->runtimeLookups + index;
if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty if (l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
|| l->getter == QV4::Lookup::getterQObject) { || l->getter == QV4::Lookup::getterQObject
|| l->getter == QV4::Lookup::getterQObjectAsVariant) {
const QQmlPropertyData *property = l->qobjectLookup.propertyData; const QQmlPropertyData *property = l->qobjectLookup.propertyData;
QQmlData::flushPendingBinding(object, property->coreIndex()); QQmlData::flushPendingBinding(object, property->coreIndex());
captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext); captureObjectProperty(object, l->qobjectLookup.propertyCache, property, qmlContext);
return true; return true;
} }
if (l->getter == QV4::Lookup::getterFallback) { if (l->getter == QV4::Lookup::getterFallback
|| l->getter == QV4::Lookup::getterFallbackAsVariant) {
const int coreIndex = l->qobjectFallbackLookup.coreIndex; const int coreIndex = l->qobjectFallbackLookup.coreIndex;
QQmlData::flushPendingBinding(object, coreIndex); QQmlData::flushPendingBinding(object, coreIndex);
captureFallbackProperty( captureFallbackProperty(
@ -1224,7 +1316,9 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty || l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupContextObjectProperty
|| l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty || l->getter == QV4::QQmlTypeWrapper::lookupSingletonProperty
|| l->getter == QV4::Lookup::getterQObject || l->getter == QV4::Lookup::getterQObject
|| l->setter == QV4::Lookup::setterQObject) { || l->setter == QV4::Lookup::setterQObject
|| l->getter == QV4::Lookup::getterQObjectAsVariant
|| l->setter == QV4::Lookup::setterQObjectAsVariant) {
return l->qobjectLookup.propertyData->propType(); return l->qobjectLookup.propertyData->propType();
} else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) { } else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) {
return QMetaType(l->qgadgetLookup.metaType); return QMetaType(l->qgadgetLookup.metaType);
@ -1237,6 +1331,8 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
return QMetaType::fromType<QObject *>(); return QMetaType::fromType<QObject *>();
} else if (l->getter == QV4::Lookup::getterFallback } else if (l->getter == QV4::Lookup::getterFallback
|| l->setter == QV4::Lookup::setterFallback || l->setter == QV4::Lookup::setterFallback
|| l->getter == QV4::Lookup::getterFallbackAsVariant
|| l->setter == QV4::Lookup::setterFallbackAsVariant
|| l->qmlContextPropertyGetter || l->qmlContextPropertyGetter
== QV4::QQmlContextWrapper::lookupScopeFallbackProperty) { == QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
const QMetaObject *metaObject const QMetaObject *metaObject
@ -1272,33 +1368,37 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
l.forCall = false; l.forCall = false;
ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit; ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit;
switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) { switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
case ObjectLookupResult::ObjectAsVariant:
case ObjectLookupResult::Object: { case ObjectLookupResult::Object: {
const QMetaType propType = l.qobjectLookup.propertyData->propType(); const QMetaType propType = l.qobjectLookup.propertyData->propType();
if (type == propType) { if (isTypeCompatible(type, propType)) {
storeResult = storeObjectProperty(&l, qmlScopeObject, value); storeResult = storeObjectProperty(&l, qmlScopeObject, value);
} else if (isUndefined(value, type)) { } else if (isUndefined(value, type)) {
storeResult = resetObjectProperty(&l, qmlScopeObject); storeResult = resetObjectProperty(&l, qmlScopeObject);
} else { } else {
QVariant var(propType); QVariant var(propType);
propType.convert(type, value, propType, var.data()); QV4::ExecutionEngine *v4 = engine->handle();
v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
storeResult = storeObjectProperty(&l, qmlScopeObject, var.data()); storeResult = storeObjectProperty(&l, qmlScopeObject, var.data());
} }
l.qobjectLookup.propertyCache->release(); l.qobjectLookup.propertyCache->release();
break; break;
} }
case ObjectLookupResult::FallbackAsVariant:
case ObjectLookupResult::Fallback: { case ObjectLookupResult::Fallback: {
const QMetaObject *metaObject const QMetaObject *metaObject
= reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1); = reinterpret_cast<const QMetaObject *>(l.qobjectFallbackLookup.metaObject - 1);
const QMetaType propType const QMetaType propType
= metaObject->property(l.qobjectFallbackLookup.coreIndex).metaType(); = metaObject->property(l.qobjectFallbackLookup.coreIndex).metaType();
if (type == propType) { if (isTypeCompatible(type, propType)) {
storeResult = storeFallbackProperty(&l, qmlScopeObject, value); storeResult = storeFallbackProperty(&l, qmlScopeObject, value);
} else if (isUndefined(value, type)) { } else if (isUndefined(value, type)) {
storeResult = resetFallbackProperty(&l, qmlScopeObject); storeResult = resetFallbackProperty(&l, qmlScopeObject);
} else { } else {
QVariant var(propType); QVariant var(propType);
propType.convert(type, value, propType, var.data()); QV4::ExecutionEngine *v4 = engine->handle();
v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data());
storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data()); storeResult = storeFallbackProperty(&l, qmlScopeObject, var.data());
} }
break; break;
@ -1574,9 +1674,11 @@ void AOTCompiledContext::initLoadScopeObjectPropertyLookup(uint index, QMetaType
} }
switch (initObjectLookup(this, l, qmlScopeObject, type)) { switch (initObjectLookup(this, l, qmlScopeObject, type)) {
case ObjectLookupResult::ObjectAsVariant:
case ObjectLookupResult::Object: case ObjectLookupResult::Object:
l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty; l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeObjectProperty;
break; break;
case ObjectLookupResult::FallbackAsVariant:
case ObjectLookupResult::Fallback: case ObjectLookupResult::Fallback:
l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty; l->qmlContextPropertyGetter = QV4::QQmlContextWrapper::lookupScopeFallbackProperty;
break; break;
@ -1742,6 +1844,10 @@ bool AOTCompiledContext::getObjectLookup(uint index, QObject *object, void *targ
result = loadObjectProperty(l, object, target, qmlContext); result = loadObjectProperty(l, object, target, qmlContext);
else if (l->getter == QV4::Lookup::getterFallback) else if (l->getter == QV4::Lookup::getterFallback)
result = loadFallbackProperty(l, object, target, qmlContext); result = loadFallbackProperty(l, object, target, qmlContext);
else if (l->getter == QV4::Lookup::getterQObjectAsVariant)
result = loadObjectAsVariant(l, object, target, qmlContext);
else if (l->getter == QV4::Lookup::getterFallbackAsVariant)
result = loadFallbackAsVariant(l, object, target, qmlContext);
else else
return false; return false;
@ -1768,9 +1874,15 @@ void AOTCompiledContext::initGetObjectLookup(uint index, QObject *object, QMetaT
case ObjectLookupResult::Object: case ObjectLookupResult::Object:
l->getter = QV4::Lookup::getterQObject; l->getter = QV4::Lookup::getterQObject;
break; break;
case ObjectLookupResult::ObjectAsVariant:
l->getter = QV4::Lookup::getterQObjectAsVariant;
break;
case ObjectLookupResult::Fallback: case ObjectLookupResult::Fallback:
l->getter = QV4::Lookup::getterFallback; l->getter = QV4::Lookup::getterFallback;
break; break;
case ObjectLookupResult::FallbackAsVariant:
l->getter = QV4::Lookup::getterFallbackAsVariant;
break;
case ObjectLookupResult::Failure: case ObjectLookupResult::Failure:
engine->handle()->throwTypeError(); engine->handle()->throwTypeError();
break; break;
@ -1884,6 +1996,10 @@ bool AOTCompiledContext::setObjectLookup(uint index, QObject *object, void *valu
result = storeObjectProperty(l, object, value); result = storeObjectProperty(l, object, value);
else if (l->setter == QV4::Lookup::setterFallback) else if (l->setter == QV4::Lookup::setterFallback)
result = storeFallbackProperty(l, object, value); result = storeFallbackProperty(l, object, value);
else if (l->setter == QV4::Lookup::setterQObjectAsVariant)
result = storeObjectAsVariant(engine->handle(), l, object, value);
else if (l->setter == QV4::Lookup::setterFallbackAsVariant)
result = storeFallbackAsVariant(engine->handle(), l, object, value);
else else
return false; return false;
@ -1910,9 +2026,15 @@ void AOTCompiledContext::initSetObjectLookup(uint index, QObject *object, QMetaT
case ObjectLookupResult::Object: case ObjectLookupResult::Object:
l->setter = QV4::Lookup::setterQObject; l->setter = QV4::Lookup::setterQObject;
break; break;
case ObjectLookupResult::ObjectAsVariant:
l->setter = QV4::Lookup::setterQObjectAsVariant;
break;
case ObjectLookupResult::Fallback: case ObjectLookupResult::Fallback:
l->setter = QV4::Lookup::setterFallback; l->setter = QV4::Lookup::setterFallback;
break; break;
case ObjectLookupResult::FallbackAsVariant:
l->setter = QV4::Lookup::setterFallbackAsVariant;
break;
case ObjectLookupResult::Failure: case ObjectLookupResult::Failure:
engine->handle()->throwTypeError(); engine->handle()->throwTypeError();
break; break;

View File

@ -1047,6 +1047,13 @@ void QQmlJSCodeGenerator::generate_GetLookup(int index)
// that would be expensive. // that would be expensive.
reject(u"attached object for non-QObject type"_s); reject(u"attached object for non-QObject type"_s);
} }
if (!m_state.accumulatorIn().storedType()->isReferenceType()) {
// This can happen if we retroactively determine that the property might not be
// what we think it is (ie, it can be shadowed).
reject(u"attached object of potentially non-QObject base"_s);
}
rejectIfNonQObjectOut(u"non-QObject attached type"_s); rejectIfNonQObjectOut(u"non-QObject attached type"_s);
const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString const QString lookup = u"aotContext->loadAttachedLookup("_s + indexString

View File

@ -779,13 +779,13 @@ QQmlJSAotFunction QQmlJSAotCompiler::doCompile(
if (error->isValid()) if (error->isValid())
return compileError(); return compileError();
QQmlJSBasicBlocks basicBlocks(m_unitGenerator, &m_typeResolver, m_logger); QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger);
typePropagationResult = basicBlocks.run(function, typePropagationResult, error); shadowCheck.run(&typePropagationResult, function, error);
if (error->isValid()) if (error->isValid())
return compileError(); return compileError();
QQmlJSShadowCheck shadowCheck(m_unitGenerator, &m_typeResolver, m_logger); QQmlJSBasicBlocks basicBlocks(m_unitGenerator, &m_typeResolver, m_logger);
shadowCheck.run(&typePropagationResult, function, error); typePropagationResult = basicBlocks.run(function, typePropagationResult, error);
if (error->isValid()) if (error->isValid())
return compileError(); return compileError();

View File

@ -26,10 +26,16 @@ using namespace Qt::StringLiterals;
* 3. The property is declared final. * 3. The property is declared final.
* 4. The object we are retrieving the property from is a value type. Value * 4. The object we are retrieving the property from is a value type. Value
* types cannot be used polymorphically. * types cannot be used polymorphically.
*
* If the property is potentially shadowed, we can still retrieve it, but we
* don't know its type. We should assume "var" then.
*
* All of the above also holds for methods. There we have to transform the
* arguments and return types into "var".
*/ */
void QQmlJSShadowCheck::run( void QQmlJSShadowCheck::run(
const InstructionAnnotations *annotations, const Function *function, InstructionAnnotations *annotations, const Function *function,
QQmlJS::DiagnosticMessage *error) QQmlJS::DiagnosticMessage *error)
{ {
m_annotations = annotations; m_annotations = annotations;
@ -45,8 +51,11 @@ void QQmlJSShadowCheck::generate_LoadProperty(int nameIndex)
return; // enum lookup cannot be shadowed. return; // enum lookup cannot be shadowed.
auto accumulatorIn = m_state.registers.find(Accumulator); auto accumulatorIn = m_state.registers.find(Accumulator);
if (accumulatorIn != m_state.registers.end()) if (accumulatorIn != m_state.registers.end()) {
checkShadowing(accumulatorIn.value().content, m_jsUnitGenerator->stringForIndex(nameIndex)); checkShadowing(
accumulatorIn.value().content, m_jsUnitGenerator->stringForIndex(nameIndex),
Accumulator);
}
} }
void QQmlJSShadowCheck::generate_GetLookup(int index) void QQmlJSShadowCheck::generate_GetLookup(int index)
@ -55,18 +64,35 @@ void QQmlJSShadowCheck::generate_GetLookup(int index)
return; // enum lookup cannot be shadowed. return; // enum lookup cannot be shadowed.
auto accumulatorIn = m_state.registers.find(Accumulator); auto accumulatorIn = m_state.registers.find(Accumulator);
if (accumulatorIn != m_state.registers.end()) if (accumulatorIn != m_state.registers.end()) {
checkShadowing(accumulatorIn.value().content, m_jsUnitGenerator->lookupName(index)); checkShadowing(
accumulatorIn.value().content, m_jsUnitGenerator->lookupName(index), Accumulator);
}
} }
void QQmlJSShadowCheck::generate_StoreProperty(int nameIndex, int base) void QQmlJSShadowCheck::generate_StoreProperty(int nameIndex, int base)
{ {
checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->stringForIndex(nameIndex)); checkShadowing(
m_state.registers[base].content, m_jsUnitGenerator->stringForIndex(nameIndex), base);
} }
void QQmlJSShadowCheck::generate_SetLookup(int index, int base) void QQmlJSShadowCheck::generate_SetLookup(int index, int base)
{ {
checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(index)); checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(index), base);
}
void QQmlJSShadowCheck::generate_CallProperty(int nameIndex, int base, int argc, int argv)
{
Q_UNUSED(argc);
Q_UNUSED(argv);
checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(nameIndex), base);
}
void QQmlJSShadowCheck::generate_CallPropertyLookup(int nameIndex, int base, int argc, int argv)
{
Q_UNUSED(argc);
Q_UNUSED(argv);
checkShadowing(m_state.registers[base].content, m_jsUnitGenerator->lookupName(nameIndex), base);
} }
QV4::Moth::ByteCodeHandler::Verdict QQmlJSShadowCheck::startInstruction(QV4::Moth::Instr::Type) QV4::Moth::ByteCodeHandler::Verdict QQmlJSShadowCheck::startInstruction(QV4::Moth::Instr::Type)
@ -82,7 +108,7 @@ void QQmlJSShadowCheck::endInstruction(QV4::Moth::Instr::Type)
} }
void QQmlJSShadowCheck::checkShadowing( void QQmlJSShadowCheck::checkShadowing(
const QQmlJSRegisterContent &baseType, const QString &memberName) const QQmlJSRegisterContent &baseType, const QString &memberName, int baseRegister)
{ {
if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference) if (baseType.storedType()->accessSemantics() != QQmlJSScope::AccessSemantics::Reference)
return; return;
@ -110,8 +136,30 @@ void QQmlJSShadowCheck::checkShadowing(
return; // Only properties and methods can be shadowed return; // Only properties and methods can be shadowed
} }
setError(u"Member %1 of %2 can be shadowed"_s m_logger->log(
.arg(memberName, m_state.accumulatorIn().descriptiveName())); u"Member %1 of %2 can be shadowed"_s.arg(
memberName, m_state.accumulatorIn().descriptiveName()),
qmlCompiler, currentSourceLocation());
// Make it "var". We don't know what it is.
const QQmlJSScope::ConstPtr varType = m_typeResolver->varType();
const QQmlJSRegisterContent varContent = m_typeResolver->globalType(varType);
InstructionAnnotation &currentAnnotation = (*m_annotations)[currentInstructionOffset()];
if (currentAnnotation.changedRegisterIndex != InvalidRegister) {
m_typeResolver->adjustOriginalType(
currentAnnotation.changedRegister.storedType(), varType);
m_typeResolver->adjustOriginalType(
m_typeResolver->containedType(currentAnnotation.changedRegister), varType);
}
for (auto it = currentAnnotation.readRegisters.begin(),
end = currentAnnotation.readRegisters.end();
it != end; ++it) {
if (it.key() != baseRegister)
it->second.content = m_typeResolver->convert(it->second.content, varContent);
}
return; return;
} }
default: default:

View File

@ -28,7 +28,7 @@ public:
~QQmlJSShadowCheck() = default; ~QQmlJSShadowCheck() = default;
void run(const InstructionAnnotations *annotations, const Function *function, void run(InstructionAnnotations *annotations, const Function *function,
QQmlJS::DiagnosticMessage *error); QQmlJS::DiagnosticMessage *error);
private: private:
@ -36,13 +36,16 @@ private:
void generate_GetLookup(int index) override; void generate_GetLookup(int index) override;
void generate_StoreProperty(int nameIndex, int base) override; void generate_StoreProperty(int nameIndex, int base) override;
void generate_SetLookup(int index, int base) override; void generate_SetLookup(int index, int base) override;
void generate_CallProperty(int nameIndex, int base, int argc, int argv) override;
void generate_CallPropertyLookup(int nameIndex, int base, int argc, int argv) override;
QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type) override; QV4::Moth::ByteCodeHandler::Verdict startInstruction(QV4::Moth::Instr::Type) override;
void endInstruction(QV4::Moth::Instr::Type) override; void endInstruction(QV4::Moth::Instr::Type) override;
void checkShadowing(const QQmlJSRegisterContent &baseType, const QString &propertyName); void checkShadowing(
const QQmlJSRegisterContent &baseType, const QString &propertyName, int baseRegister);
const InstructionAnnotations *m_annotations = nullptr; InstructionAnnotations *m_annotations = nullptr;
State m_state; State m_state;
}; };

View File

@ -515,6 +515,19 @@ bool QQmlJSTypeResolver::adjustTrackedType(
return true; return true;
} }
void QQmlJSTypeResolver::adjustOriginalType(
const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const
{
if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)
return;
const auto it = m_trackedTypes->find(tracked);
Q_ASSERT(it != m_trackedTypes->end());
it->original = conversion;
*it->clone = std::move(*QQmlJSScope::clone(conversion));
}
void QQmlJSTypeResolver::generalizeType(const QQmlJSScope::ConstPtr &type) const void QQmlJSTypeResolver::generalizeType(const QQmlJSScope::ConstPtr &type) const
{ {
if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes) if (m_cloneMode == QQmlJSTypeResolver::DoNotCloneTypes)

View File

@ -140,6 +140,8 @@ public:
[[nodiscard]] bool adjustTrackedType( [[nodiscard]] bool adjustTrackedType(
const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &tracked,
const QList<QQmlJSScope::ConstPtr> &conversions) const; const QList<QQmlJSScope::ConstPtr> &conversions) const;
void adjustOriginalType(
const QQmlJSScope::ConstPtr &tracked, const QQmlJSScope::ConstPtr &conversion) const;
void generalizeType(const QQmlJSScope::ConstPtr &type) const; void generalizeType(const QQmlJSScope::ConstPtr &type) const;
void setParentMode(ParentMode mode) { m_parentMode = mode; } void setParentMode(ParentMode mode) { m_parentMode = mode; }

View File

@ -181,6 +181,7 @@ set(qml_files
scopeVsObject.qml scopeVsObject.qml
script.js script.js
script.mjs script.mjs
shadowedMethod.qml
shared/Slider.qml shared/Slider.qml
shifts.qml shifts.qml
signal.qml signal.qml

View File

@ -0,0 +1,35 @@
pragma Strict
import QtQuick
Item {
component B: Item {
function contains(point: point) : string {
return "b"
}
}
component C: Item {
function contains(point: point) : string {
return "c"
}
}
property Item a: Item {}
property B b: B {}
property C c: C {}
function doThing() : var { return a.contains(Qt.point(0, 0)) }
property var athing;
property var bthing;
property var cthing;
Component.onCompleted: {
athing = doThing();
a = b;
bthing = doThing();
a = c;
cthing = doThing();
}
}

View File

@ -183,6 +183,7 @@ private slots:
void jsArrayMethods(); void jsArrayMethods();
void jsArrayMethodsWithParams_data(); void jsArrayMethodsWithParams_data();
void jsArrayMethodsWithParams(); void jsArrayMethodsWithParams();
void shadowedMethod();
}; };
void tst_QmlCppCodegen::initTestCase() void tst_QmlCppCodegen::initTestCase()
@ -3754,6 +3755,18 @@ void tst_QmlCppCodegen::jsArrayMethodsWithParams()
QCOMPARE(object->property("listPropertyLastIndexOf"), check->property("jsArrayLastIndexOf")); QCOMPARE(object->property("listPropertyLastIndexOf"), check->property("jsArrayLastIndexOf"));
} }
void tst_QmlCppCodegen::shadowedMethod()
{
QQmlEngine e;
QQmlComponent c(&e, QUrl(u"qrc:/qt/qml/TestTypes/shadowedMethod.qml"_s));
QVERIFY2(c.isReady(), qPrintable(c.errorString()));
QScopedPointer<QObject> o(c.create());
QVERIFY(!o.isNull());
QCOMPARE(o->property("athing"), QVariant::fromValue<bool>(false));
QCOMPARE(o->property("bthing"), QVariant::fromValue(u"b"_s));
QCOMPARE(o->property("cthing"), QVariant::fromValue(u"c"_s));
}
QTEST_MAIN(tst_QmlCppCodegen) QTEST_MAIN(tst_QmlCppCodegen)
#include "tst_qmlcppcodegen.moc" #include "tst_qmlcppcodegen.moc"