QmlCompiler: Resolve types for calls in init step
Now that resolving the types can be more expensive, especially for composite types, we should be careful not to do it too often. Most of the cases where we have to resolve types are for lookups. Most of the lookups only resolve types in their "init" step, which ideally is only performed once in the application life time. However, so far calls had to pass the types of arguments and return values in the actual "lookup" step, which causes the resolution to happen on every call. This change moves those type resolutions to the init step. We can do this because: 1. Regular typed method calls are calls to a specific overload with specific types. The method itself already has the list of types and we can just remember which one that is. Then we don't need to pass the types. 2. Calls to shadowable methods are all-QVariant typed. The only thing we need to know is the number of arguments. Then we can construct the list of types in the lookup itself. We can remember which one of those we're dealing with by adding further "AsVariant" lookup functions. For the case of non-property-cached regular methods we also need a new "Fallback" lookup like we already have it for properties. Change-Id: I74a3729131d6a5ea0ad79e276965a5167cd609be Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
This commit is contained in:
parent
6911a76f43
commit
a741271dd5
|
@ -40,3 +40,62 @@ bool QQmlPrivate::AOTCompiledContext::getEnumLookup(uint index, int *target) con
|
|||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if QT_QML_REMOVED_SINCE(6, 9)
|
||||
#include <QtQml/qqmlprivate.h>
|
||||
#include <QtQml/private/qv4executablecompilationunit_p.h>
|
||||
#include <QtQml/private/qv4lookup_p.h>
|
||||
#include <QtQml/private/qv4qobjectwrapper_p.h>
|
||||
|
||||
bool QQmlPrivate::AOTCompiledContext::callObjectPropertyLookup(
|
||||
uint index, QObject *object, void **args, const QMetaType *types, int argc) const
|
||||
{
|
||||
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
|
||||
QV4::Scope scope(engine->handle());
|
||||
QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(scope.engine, object));
|
||||
QV4::ScopedFunctionObject function(scope, l->getter(l, engine->handle(), thisObject));
|
||||
if (!function) {
|
||||
scope.engine->throwTypeError(
|
||||
QStringLiteral("Property '%1' of object [object Object] is not a function")
|
||||
.arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
function->call(object, args, types, argc);
|
||||
return !scope.hasException();
|
||||
}
|
||||
|
||||
void QQmlPrivate::AOTCompiledContext::initCallObjectPropertyLookup(uint index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
Q_ASSERT(engine->hasError());
|
||||
engine->handle()->amendException();
|
||||
}
|
||||
|
||||
bool QQmlPrivate::AOTCompiledContext::callQmlContextPropertyLookup(
|
||||
uint index, void **args, const QMetaType *types, int argc) const
|
||||
{
|
||||
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
|
||||
QV4::Scope scope(engine->handle());
|
||||
QV4::ScopedValue thisObject(scope);
|
||||
QV4::ScopedFunctionObject function(
|
||||
scope, l->qmlContextPropertyGetter(l, scope.engine, thisObject));
|
||||
if (!function) {
|
||||
scope.engine->throwTypeError(
|
||||
QStringLiteral("Property '%1' of object [null] is not a function").arg(
|
||||
compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
function->call(qmlScopeObject, args, types, argc);
|
||||
return !scope.hasException();
|
||||
}
|
||||
|
||||
void QQmlPrivate::AOTCompiledContext::initCallQmlContextPropertyLookup(uint index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
Q_ASSERT(engine->hasError());
|
||||
engine->handle()->amendException();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -450,6 +450,70 @@ ReturnedValue Lookup::getterQObjectMethod(Lookup *lookup, ExecutionEngine *engin
|
|||
return QObjectWrapper::lookupMethodGetterImpl(lookup, engine, object, flags, revertLookup);
|
||||
}
|
||||
|
||||
ReturnedValue Lookup::getterQObjectMethodAsVariant(
|
||||
Lookup *lookup, ExecutionEngine *engine, const Value &object)
|
||||
{
|
||||
if (&Lookup::getterQObjectMethod == &Lookup::getterQObjectMethodAsVariant) {
|
||||
// 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 qobject method lookup with variant conversion.
|
||||
// It only does anything with it when running AOT-compiled code.
|
||||
return getterQObjectMethod(lookup, engine, object);
|
||||
}
|
||||
|
||||
ReturnedValue Lookup::getterFallbackMethod(Lookup *l, ExecutionEngine *engine, const Value &object)
|
||||
{
|
||||
const auto revertLookup = [l, engine, &object]() {
|
||||
l->getter = Lookup::getterGeneric;
|
||||
return Lookup::getterGeneric(l, engine, object);
|
||||
};
|
||||
|
||||
const Object *o = object.as<Object>();
|
||||
if (!o || o->internalClass() != l->qobjectMethodLookup.ic)
|
||||
return revertLookup();
|
||||
|
||||
const QObjectWrapper *This = static_cast<const QObjectWrapper *>(o);
|
||||
QObject *qobj = This->d()->object();
|
||||
if (QQmlData::wasDeleted(qobj))
|
||||
return QV4::Encode::undefined();
|
||||
|
||||
Scope scope(engine);
|
||||
ScopedString name(
|
||||
scope,
|
||||
engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
|
||||
|
||||
QV4::ScopedValue result(
|
||||
scope, QObjectWrapper::getMethodFallback(
|
||||
engine, This->d(), qobj, name,
|
||||
l->forCall ? QObjectWrapper::NoFlag : QObjectWrapper::AttachMethods));
|
||||
|
||||
// In the general case we cannot rely on the method to exist or stay the same across calls.
|
||||
// However, the AOT compiler can prove it in certain cases. For these, we store the method.
|
||||
if (QObjectMethod *method = result->as<QV4::QObjectMethod>())
|
||||
l->qobjectMethodLookup.method.set(engine, method->d());
|
||||
|
||||
return result->asReturnedValue();
|
||||
}
|
||||
|
||||
ReturnedValue Lookup::getterFallbackMethodAsVariant(
|
||||
Lookup *l, ExecutionEngine *engine, const Value &object)
|
||||
{
|
||||
if (&Lookup::getterFallbackMethod == &Lookup::getterFallbackMethodAsVariant) {
|
||||
// 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 fallback method lookup with variant conversion.
|
||||
// It only does anything with it when running AOT-compiled code.
|
||||
return getterFallbackMethod(l, engine, object);
|
||||
}
|
||||
|
||||
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
|
||||
{
|
||||
// Otherwise we cannot trust the protoIds
|
||||
|
|
|
@ -185,6 +185,9 @@ struct Q_QML_EXPORT Lookup {
|
|||
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 getterQObjectMethodAsVariant(Lookup *l, ExecutionEngine *engine, const Value &object);
|
||||
static ReturnedValue getterFallbackMethod(Lookup *l, ExecutionEngine *engine, const Value &object);
|
||||
static ReturnedValue getterFallbackMethodAsVariant(Lookup *l, ExecutionEngine *engine, const Value &object);
|
||||
|
||||
static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object);
|
||||
static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
|
||||
|
@ -226,6 +229,7 @@ struct Q_QML_EXPORT Lookup {
|
|||
if (const QQmlPropertyCache *pc = qobjectLookup.propertyCache)
|
||||
pc->release();
|
||||
} else if (getter == getterQObjectMethod
|
||||
|| getter == getterQObjectMethodAsVariant
|
||||
|| getter == QQmlTypeWrapper::lookupSingletonMethod
|
||||
|| qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectMethod
|
||||
|| qmlContextPropertyGetter == QQmlContextWrapper::lookupContextObjectMethod) {
|
||||
|
|
|
@ -376,6 +376,18 @@ ReturnedValue QObjectWrapper::getProperty(
|
|||
}
|
||||
}
|
||||
|
||||
ReturnedValue QObjectWrapper::getMethodFallback(
|
||||
ExecutionEngine *engine, Heap::Object *wrapper, QObject *qobject,
|
||||
QV4::String *name, Flags flags)
|
||||
{
|
||||
QQmlPropertyData local;
|
||||
const QQmlPropertyData *property = QQmlPropertyCache::property(
|
||||
qobject, name, engine->callingQmlContext(), &local);
|
||||
return property
|
||||
? getProperty(engine, wrapper, qobject, property, flags)
|
||||
: Encode::undefined();
|
||||
}
|
||||
|
||||
static OptionalReturnedValue getDestroyOrToStringMethod(
|
||||
ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
|
||||
{
|
||||
|
@ -1101,13 +1113,13 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
|
|||
}
|
||||
|
||||
if (!ddata || !ddata->propertyCache) {
|
||||
QQmlPropertyData local;
|
||||
const QQmlPropertyData *property = QQmlPropertyCache::property(
|
||||
qobj, name, qmlContext, &local);
|
||||
return property
|
||||
? getProperty(engine, This->d(), qobj, property,
|
||||
lookup->forCall ? NoFlag : AttachMethods)
|
||||
: Encode::undefined();
|
||||
QV4::ScopedValue result(scope, getMethodFallback(
|
||||
engine, This->d(), qobj, name, lookup->forCall ? NoFlag : AttachMethods));
|
||||
lookup->qobjectMethodLookup.ic.set(engine, object->internalClass());
|
||||
if (QObjectMethod *method = result->as<QObjectMethod>())
|
||||
lookup->qobjectMethodLookup.method.set(engine, method->d());
|
||||
lookup->getter = Lookup::getterFallbackMethod;
|
||||
return result->asReturnedValue();
|
||||
}
|
||||
const QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext);
|
||||
|
||||
|
|
|
@ -177,6 +177,10 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
|
|||
ExecutionEngine *engine, Heap::Object *wrapper, QObject *object,
|
||||
const QQmlPropertyData *property, Flags flags);
|
||||
|
||||
static ReturnedValue getMethodFallback(
|
||||
ExecutionEngine *engine, Heap::Object *wrapper, QObject *object,
|
||||
QV4::String *name, Flags flags);
|
||||
|
||||
static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
|
||||
static ReturnedValue lookupAttached(Lookup *l, ExecutionEngine *engine, const Value &object);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <private/qqmltypemodule_p.h>
|
||||
#include <private/qqmltypewrapper_p.h>
|
||||
#include <private/qqmlvaluetypewrapper_p.h>
|
||||
#include <private/qv4alloca_p.h>
|
||||
#include <private/qv4dateobject_p.h>
|
||||
#include <private/qv4errorobject_p.h>
|
||||
#include <private/qv4identifiertable_p.h>
|
||||
|
@ -986,6 +987,8 @@ void qmlRegisterTypeAndRevisions<QQmlTypeNotAvailable, void>(
|
|||
qmlregister(TypeAndRevisionsRegistration, &type);
|
||||
}
|
||||
|
||||
struct LookupNotInitialized {};
|
||||
|
||||
QObject *AOTCompiledContext::thisObject() const
|
||||
{
|
||||
return static_cast<QV4::MetaTypesStackFrame *>(engine->handle()->currentStackFrame)
|
||||
|
@ -1295,8 +1298,8 @@ static ObjectPropertyResult resetFallbackProperty(
|
|||
|
||||
static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
|
||||
{
|
||||
if (!lookupType.isValid()) {
|
||||
// If type is invalid, then the calling code depends on the lookup
|
||||
if (lookupType == QMetaType::fromType<LookupNotInitialized>()) {
|
||||
// If lookup is not initialized, then the calling code depends on the lookup
|
||||
// to be set up in order to query the type, via lookupResultMetaType.
|
||||
// We cannot verify the type in this case.
|
||||
} else if ((lookupType.flags() & QMetaType::IsQmlList)
|
||||
|
@ -1351,6 +1354,11 @@ static bool isTypeCompatible(QMetaType lookupType, QMetaType propertyType)
|
|||
}
|
||||
|
||||
return false;
|
||||
} else if (!propertyType.isValid()) {
|
||||
// We cannot directly store void, but we can put it into QVariant or QJSPrimitiveValue
|
||||
return !lookupType.isValid()
|
||||
|| lookupType == QMetaType::fromType<QVariant>()
|
||||
|| lookupType == QMetaType::fromType<QJSPrimitiveValue>();
|
||||
} else if (propertyType != lookupType) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1564,25 +1572,40 @@ QMetaType AOTCompiledContext::lookupResultMetaType(uint index) const
|
|||
|| l->getter == QV4::Lookup::getterQObject
|
||||
|| l->getter == QV4::Lookup::getterQObjectAsVariant) {
|
||||
return l->qobjectLookup.propertyData->propType();
|
||||
} else if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter) {
|
||||
}
|
||||
|
||||
if (l->getter == QV4::QQmlValueTypeWrapper::lookupGetter)
|
||||
return QMetaType(l->qgadgetLookup.metaType);
|
||||
} else if (l->getter == QV4::QQmlTypeWrapper::lookupEnumValue) {
|
||||
|
||||
if (l->getter == QV4::QQmlTypeWrapper::lookupEnumValue)
|
||||
return QMetaType(l->qmlEnumValueLookup.metaType);
|
||||
} else if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject
|
||||
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType
|
||||
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton
|
||||
|| l->getter == QV4::QObjectWrapper::lookupAttached) {
|
||||
|
||||
if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupIdObject
|
||||
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupType
|
||||
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupSingleton
|
||||
|| l->getter == QV4::QObjectWrapper::lookupAttached) {
|
||||
return QMetaType::fromType<QObject *>();
|
||||
} else if (l->getter == QV4::Lookup::getterFallback
|
||||
|| l->getter == QV4::Lookup::getterFallbackAsVariant
|
||||
|| l->qmlContextPropertyGetter
|
||||
}
|
||||
|
||||
if (l->getter == QV4::Lookup::getterFallback
|
||||
|| l->getter == QV4::Lookup::getterFallbackAsVariant
|
||||
|| l->qmlContextPropertyGetter
|
||||
== QV4::QQmlContextWrapper::lookupScopeFallbackProperty) {
|
||||
const QMetaObject *metaObject
|
||||
= reinterpret_cast<const QMetaObject *>(l->qobjectFallbackLookup.metaObject - 1);
|
||||
const int coreIndex = l->qobjectFallbackLookup.coreIndex;
|
||||
return metaObject->property(coreIndex).metaType();
|
||||
}
|
||||
return QMetaType();
|
||||
|
||||
if (l->getter == QV4::Lookup::getterQObjectMethod
|
||||
|| l->getter == QV4::Lookup::getterQObjectMethodAsVariant
|
||||
|| l->getter == QV4::Lookup::getterFallbackMethod
|
||||
|| l->getter == QV4::Lookup::getterFallbackMethodAsVariant
|
||||
|| l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectMethod) {
|
||||
return l->qobjectMethodLookup.propertyData->propType();
|
||||
}
|
||||
|
||||
return QMetaType::fromType<LookupNotInitialized>();
|
||||
}
|
||||
|
||||
static bool isUndefined(const void *value, QMetaType type)
|
||||
|
@ -1609,7 +1632,7 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType
|
|||
l.nameIndex = nameIndex;
|
||||
l.forCall = false;
|
||||
ObjectPropertyResult storeResult = ObjectPropertyResult::NeedsInit;
|
||||
switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType())) {
|
||||
switch (initObjectLookup(this, &l, qmlScopeObject, QMetaType::fromType<LookupNotInitialized>())) {
|
||||
case ObjectLookupResult::ObjectAsVariant:
|
||||
case ObjectLookupResult::Object: {
|
||||
const QMetaType propType = l.qobjectLookup.propertyData->propType();
|
||||
|
@ -1749,30 +1772,364 @@ QDateTime AOTCompiledContext::constructDateTime(
|
|||
year, month, day, hours, minutes, seconds, msecs, engine->handle()));
|
||||
}
|
||||
|
||||
bool AOTCompiledContext::callQmlContextPropertyLookup(
|
||||
uint index, void **args, const QMetaType *types, int argc) const
|
||||
static QMetaType jsTypedFunctionArgument(
|
||||
const QQmlType &type, const QV4::CompiledData::ParameterType ¶meter)
|
||||
{
|
||||
return parameter.isList() ? type.qListTypeId() : type.typeId();
|
||||
}
|
||||
|
||||
static bool callQObjectMethodWithTypes(
|
||||
QV4::ExecutionEngine *engine, QV4::Lookup *l, QObject *thisObject, void **args,
|
||||
QMetaType *types, int argc)
|
||||
{
|
||||
QV4::Scope scope(engine);
|
||||
QV4::Scoped<QV4::QObjectMethod> function(scope, l->qobjectMethodLookup.method);
|
||||
Q_ASSERT(function);
|
||||
function->call(thisObject, args, types, argc);
|
||||
return !scope.hasException();
|
||||
}
|
||||
|
||||
static void setVariantGetter(QV4::Lookup *l)
|
||||
{
|
||||
if (l->getter == QV4::Lookup::getterQObjectMethod) {
|
||||
l->getter = QV4::Lookup::getterQObjectMethodAsVariant;
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->getter == QV4::Lookup::getterFallbackMethod) {
|
||||
l->getter = QV4::Lookup::getterFallbackMethodAsVariant;
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->getter == QV4::Lookup::getterQObject) {
|
||||
l->getter = QV4::Lookup::getterQObjectAsVariant;
|
||||
return;
|
||||
}
|
||||
|
||||
if (l->getter == QV4::Lookup::getterFallback) {
|
||||
l->getter = QV4::Lookup::getterFallbackAsVariant;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool callQObjectMethodAsVariant(
|
||||
QV4::ExecutionEngine *engine, QV4::Lookup *l,
|
||||
QObject *thisObject, void **args, int argc)
|
||||
{
|
||||
// We need to re-fetch the method on every call because it can be shadowed.
|
||||
|
||||
QV4::Scope scope(engine);
|
||||
QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, thisObject));
|
||||
QV4::ScopedFunctionObject function(scope, l->getter(l, scope.engine, wrappedObject));
|
||||
Q_ASSERT(function);
|
||||
|
||||
// The getter may have reset the lookup, but the method is still shadowable.
|
||||
setVariantGetter(l);
|
||||
|
||||
Q_ALLOCA_VAR(QMetaType, types, (argc + 1) * sizeof(QMetaType));
|
||||
std::fill(types, types + argc + 1, QMetaType::fromType<QVariant>());
|
||||
|
||||
function->call(thisObject, args, types, argc);
|
||||
return !scope.hasException();
|
||||
}
|
||||
|
||||
static bool callQObjectMethod(
|
||||
QV4::ExecutionEngine *engine, QV4::Lookup *l,
|
||||
QObject *thisObject, void **args, int argc)
|
||||
{
|
||||
Q_ALLOCA_VAR(QMetaType, types, (argc + 1) * sizeof(QMetaType));
|
||||
const QMetaMethod method = l->qobjectMethodLookup.propertyData->metaMethod();
|
||||
Q_ASSERT(argc == method.parameterCount());
|
||||
types[0] = method.returnMetaType();
|
||||
for (int i = 0; i < argc; ++i)
|
||||
types[i + 1] = method.parameterMetaType(i);
|
||||
|
||||
return callQObjectMethodWithTypes(engine, l, thisObject, args, types, argc);
|
||||
}
|
||||
|
||||
static bool callArrowFunction(
|
||||
QV4::ExecutionEngine *engine, QV4::ArrowFunction *function,
|
||||
QObject *thisObject, void **args, int argc)
|
||||
{
|
||||
QV4::Function *v4Function = function->function();
|
||||
Q_ASSERT(v4Function);
|
||||
Q_ASSERT(v4Function->nFormals == argc);
|
||||
|
||||
switch (v4Function->kind) {
|
||||
case QV4::Function::AotCompiled: {
|
||||
const auto *types = v4Function->aotCompiledFunction.types.data();
|
||||
function->call(thisObject, args, types, argc);
|
||||
return !engine->hasException;
|
||||
}
|
||||
case QV4::Function::JsTyped: {
|
||||
const auto *compiledFunction = v4Function->compiledFunction;
|
||||
const QV4::CompiledData::Parameter *formals
|
||||
= v4Function->compiledFunction->formalsTable();
|
||||
|
||||
Q_ALLOCA_VAR(QMetaType, types, (argc + 1) * sizeof(QMetaType));
|
||||
types[0] = jsTypedFunctionArgument(
|
||||
v4Function->jsTypedFunction.types[0], compiledFunction->returnType);
|
||||
for (qsizetype i = 0; i != argc; ++i) {
|
||||
types[i + 1] = jsTypedFunctionArgument(
|
||||
v4Function->jsTypedFunction.types[i + 1], formals[i].type);
|
||||
}
|
||||
|
||||
function->call(thisObject, args, types, argc);
|
||||
return !engine->hasException;
|
||||
}
|
||||
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(argc == 0);
|
||||
QMetaType variantType = QMetaType::fromType<QVariant>();
|
||||
function->call(thisObject, args, &variantType, 0);
|
||||
return !engine->hasException;
|
||||
}
|
||||
case QV4::Function::Eval:
|
||||
break;
|
||||
}
|
||||
|
||||
Q_UNREACHABLE_RETURN(false);
|
||||
}
|
||||
|
||||
static bool callArrowFunctionAsVariant(
|
||||
QV4::ExecutionEngine *engine, QV4::ArrowFunction *function,
|
||||
QObject *thisObject, void **args, int argc)
|
||||
{
|
||||
QV4::Function *v4Function = function->function();
|
||||
Q_ASSERT(v4Function);
|
||||
|
||||
switch (v4Function->kind) {
|
||||
case QV4::Function::JsUntyped:
|
||||
// We cannot assert anything here because the method can be shadowed.
|
||||
// That's why we wrap everything in QVariant.
|
||||
case QV4::Function::AotCompiled:
|
||||
case QV4::Function::JsTyped: {
|
||||
Q_ALLOCA_VAR(QMetaType, types, (argc + 1) * sizeof(QMetaType));
|
||||
std::fill(types, types + argc + 1, QMetaType::fromType<QVariant>());
|
||||
function->call(thisObject, args, types, argc);
|
||||
return !engine->hasException;
|
||||
}
|
||||
case QV4::Function::Eval:
|
||||
break;
|
||||
}
|
||||
|
||||
Q_UNREACHABLE_RETURN(false);
|
||||
}
|
||||
|
||||
bool AOTCompiledContext::callQmlContextPropertyLookup(uint index, void **args, int argc) const
|
||||
{
|
||||
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
|
||||
|
||||
if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectMethod)
|
||||
return callQObjectMethod(engine->handle(), l, qmlScopeObject, args, argc);
|
||||
|
||||
const auto doCall = [&](auto &&call) {
|
||||
QV4::Scope scope(engine->handle());
|
||||
QV4::ScopedValue undefined(scope);
|
||||
QV4::Scoped<QV4::ArrowFunction> function(
|
||||
scope, l->qmlContextPropertyGetter(l, scope.engine, undefined));
|
||||
Q_ASSERT(function);
|
||||
return call(scope.engine, function, qmlScopeObject, args, argc);
|
||||
};
|
||||
|
||||
if (l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty)
|
||||
return doCall(&callArrowFunction);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static MatchScore resolveQObjectMethodOverload(
|
||||
QV4::QObjectMethod *method, QV4::Lookup *l, const QMetaType *types, int argc,
|
||||
MatchScore acceptedScores)
|
||||
{
|
||||
Q_ASSERT(l->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;
|
||||
}
|
||||
}
|
||||
|
||||
if (finalScore == NoMatch)
|
||||
continue;
|
||||
|
||||
if (finalScore.testAnyFlags(acceptedScores)) {
|
||||
l->qobjectMethodLookup.propertyData = d->methods + i;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Q_UNREACHABLE_RETURN(false);
|
||||
}
|
||||
|
||||
void AOTCompiledContext::initCallQmlContextPropertyLookup(
|
||||
uint index, const QMetaType *types, int argc) const
|
||||
{
|
||||
if (engine->hasError()) {
|
||||
engine->handle()->amendException();
|
||||
return;
|
||||
}
|
||||
|
||||
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
|
||||
QV4::Scope scope(engine->handle());
|
||||
QV4::ScopedValue thisObject(scope);
|
||||
QV4::ScopedFunctionObject function(
|
||||
scope, l->qmlContextPropertyGetter(l, scope.engine, thisObject));
|
||||
if (!function) {
|
||||
scope.engine->throwTypeError(
|
||||
QStringLiteral("Property '%1' of object [null] is not a function").arg(
|
||||
compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
|
||||
return false;
|
||||
if (auto *method = function->as<QV4::QObjectMethod>()) {
|
||||
Q_ASSERT(l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectMethod);
|
||||
method->d()->ensureMethodsCache(qmlScopeObject->metaObject());
|
||||
const auto match = resolveQObjectMethodOverload(method, l, types, argc, ScopeAccepted);
|
||||
Q_ASSERT(match == ExactMatch);
|
||||
return;
|
||||
}
|
||||
|
||||
function->call(qmlScopeObject, args, types, argc);
|
||||
return !scope.hasException();
|
||||
}
|
||||
if (function->as<QV4::ArrowFunction>()) {
|
||||
// Can't have overloads of JavaScript functions.
|
||||
Q_ASSERT(l->qmlContextPropertyGetter == QV4::QQmlContextWrapper::lookupScopeObjectProperty);
|
||||
return;
|
||||
}
|
||||
|
||||
void AOTCompiledContext::initCallQmlContextPropertyLookup(uint index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
Q_ASSERT(engine->hasError());
|
||||
engine->handle()->amendException();
|
||||
scope.engine->throwTypeError(
|
||||
QStringLiteral("Property '%1' of object [null] is not a function").arg(
|
||||
compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
|
||||
}
|
||||
|
||||
bool AOTCompiledContext::loadContextIdLookup(uint index, void *target) const
|
||||
|
@ -1833,28 +2190,76 @@ void AOTCompiledContext::initLoadContextIdLookup(uint index) const
|
|||
}
|
||||
|
||||
bool AOTCompiledContext::callObjectPropertyLookup(
|
||||
uint index, QObject *object, void **args, const QMetaType *types, int argc) const
|
||||
uint index, QObject *object, void **args, int argc) const
|
||||
{
|
||||
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
|
||||
|
||||
if (l->getter == QV4::Lookup::getterQObjectMethod
|
||||
|| l->getter == QV4::Lookup::getterFallbackMethod) {
|
||||
return callQObjectMethod(engine->handle(), l, object, args, argc);
|
||||
}
|
||||
|
||||
if (l->getter == QV4::Lookup::getterQObjectMethodAsVariant
|
||||
|| l->getter == QV4::Lookup::getterFallbackMethodAsVariant) {
|
||||
return callQObjectMethodAsVariant(engine->handle(), l, object, args, argc);
|
||||
}
|
||||
|
||||
const auto doCall = [&](auto &&call) {
|
||||
// Here we always retrieve a fresh method via the getter. No need to re-init.
|
||||
QV4::Scope scope(engine->handle());
|
||||
QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(scope.engine, object));
|
||||
QV4::Scoped<QV4::ArrowFunction> function(scope, l->getter(l, scope.engine, thisObject));
|
||||
Q_ASSERT(function);
|
||||
return call(scope.engine, function, qmlScopeObject, args, argc);
|
||||
};
|
||||
|
||||
if (l->getter == QV4::Lookup::getterQObject
|
||||
|| l->getter == QV4::Lookup::getterFallback) {
|
||||
return doCall(&callArrowFunction);
|
||||
}
|
||||
|
||||
if (l->getter == QV4::Lookup::getterQObjectAsVariant
|
||||
|| l->getter == QV4::Lookup::getterFallbackAsVariant) {
|
||||
const bool result = doCall(&callArrowFunctionAsVariant);
|
||||
|
||||
// The getter may have reset the lookup, but the method is still shadowable.
|
||||
setVariantGetter(l);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AOTCompiledContext::initCallObjectPropertyLookup(
|
||||
uint index, QObject *object, const QMetaType *types, int argc) const
|
||||
{
|
||||
if (engine->hasError()) {
|
||||
engine->handle()->amendException();
|
||||
return;
|
||||
}
|
||||
|
||||
QV4::Lookup *l = compilationUnit->runtimeLookups + index;
|
||||
QV4::Scope scope(engine->handle());
|
||||
QV4::ScopedValue thisObject(scope, QV4::QObjectWrapper::wrap(scope.engine, object));
|
||||
QV4::ScopedFunctionObject function(scope, l->getter(l, engine->handle(), thisObject));
|
||||
if (!function) {
|
||||
scope.engine->throwTypeError(
|
||||
QStringLiteral("Property '%1' of object [object Object] is not a function")
|
||||
.arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
|
||||
return false;
|
||||
QV4::ScopedFunctionObject function(scope, l->getter(l, scope.engine, thisObject));
|
||||
if (auto *method = function->as<QV4::QObjectMethod>()) {
|
||||
method->d()->ensureMethodsCache(object->metaObject());
|
||||
if (resolveQObjectMethodOverload(method, l, types, argc, ObjectAccepted) == VariantMatch)
|
||||
setVariantGetter(l);
|
||||
return;
|
||||
}
|
||||
|
||||
function->call(object, args, types, argc);
|
||||
return !scope.hasException();
|
||||
}
|
||||
if (QV4::ArrowFunction *arrowFunction = function->as<QV4::ArrowFunction>()) {
|
||||
// Can't have overloads of JavaScript functions.
|
||||
if (isArrowFunctionVariantCall(arrowFunction, types, argc))
|
||||
setVariantGetter(l);
|
||||
return;
|
||||
}
|
||||
|
||||
void AOTCompiledContext::initCallObjectPropertyLookup(uint index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
Q_ASSERT(engine->hasError());
|
||||
engine->handle()->amendException();
|
||||
scope.engine->throwTypeError(
|
||||
QStringLiteral("Property '%1' of object [object Object] is not a function")
|
||||
.arg(compilationUnit->runtimeStrings[l->nameIndex]->toQString()));
|
||||
}
|
||||
|
||||
bool AOTCompiledContext::callGlobalLookup(
|
||||
|
|
|
@ -687,16 +687,27 @@ namespace QQmlPrivate
|
|||
// exception is present after the initialization there is no way to carry out the lookup and
|
||||
// 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;
|
||||
|
||||
#if QT_QML_REMOVED_SINCE(6, 9)
|
||||
bool callQmlContextPropertyLookup(
|
||||
uint index, void **args, const QMetaType *types, int argc) const;
|
||||
void initCallQmlContextPropertyLookup(uint index) const;
|
||||
#endif
|
||||
|
||||
bool loadContextIdLookup(uint index, void *target) const;
|
||||
void initLoadContextIdLookup(uint index) const;
|
||||
|
||||
bool callObjectPropertyLookup(uint index, QObject *object,
|
||||
void **args, const QMetaType *types, int argc) const;
|
||||
bool callObjectPropertyLookup(uint index, QObject *object, void **args, int argc) const;
|
||||
void initCallObjectPropertyLookup(
|
||||
uint index, QObject *object, const QMetaType *types, int argc) const;
|
||||
|
||||
#if QT_QML_REMOVED_SINCE(6, 9)
|
||||
bool callObjectPropertyLookup(
|
||||
uint index, QObject *object, void **args, const QMetaType *types, int argc) const;
|
||||
void initCallObjectPropertyLookup(uint index) const;
|
||||
#endif
|
||||
|
||||
bool callGlobalLookup(uint index, void **args, const QMetaType *types, int argc) const;
|
||||
void initCallGlobalLookup(uint index) const;
|
||||
|
|
|
@ -1694,7 +1694,9 @@ void QQmlJSCodeGenerator::generate_Resume(int)
|
|||
BYTECODE_UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
|
||||
QString QQmlJSCodeGenerator::initAndCall(
|
||||
int argc, int argv, const QString &callMethodTemplate, const QString &initMethodTemplate,
|
||||
QString *outVar)
|
||||
{
|
||||
QString types;
|
||||
QString args;
|
||||
|
@ -1708,12 +1710,6 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
|
|||
*outVar = u"callResult"_s;
|
||||
const QQmlJSScope::ConstPtr outType = m_state.accumulatorOut().storedType();
|
||||
m_body += outType->augmentedInternalName() + u' ' + *outVar;
|
||||
if (!m_typeResolver->registerContains(m_state.accumulatorOut(), outType)) {
|
||||
if (m_typeResolver->equals(outType, m_typeResolver->varType())
|
||||
|| m_typeResolver->equals(outType, m_typeResolver->jsPrimitiveType())) {
|
||||
m_body += u'(' + metaType(m_state.accumulatorOut().containedType()) + u')';
|
||||
}
|
||||
}
|
||||
m_body += u";\n";
|
||||
|
||||
args = contentPointer(m_state.accumulatorOut(), *outVar);
|
||||
|
@ -1727,8 +1723,14 @@ QString QQmlJSCodeGenerator::argumentsList(int argc, int argv, QString *outVar)
|
|||
types += u", "_s + contentType(content, var);
|
||||
}
|
||||
|
||||
return u"void *args[] = { "_s + args + u" };\n"_s
|
||||
+ u"const QMetaType types[] = { "_s + types + u" };\n"_s;
|
||||
return u"const auto doCall = [&]() {\n"_s
|
||||
+ u" void *args[] = {" + args + u"};\n"_s
|
||||
+ 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"};\n"_s;
|
||||
}
|
||||
|
||||
void QQmlJSCodeGenerator::generateMoveOutVar(const QString &outVar)
|
||||
|
@ -2238,18 +2240,17 @@ void QQmlJSCodeGenerator::generate_CallPropertyLookup(int index, int base, int a
|
|||
scope, baseType, registerVariable(base),
|
||||
u"Cannot call method '%1' of %2"_s.arg(name));
|
||||
|
||||
const QString indexString = QString::number(index);
|
||||
|
||||
m_body += u"{\n"_s;
|
||||
|
||||
QString outVar;
|
||||
m_body += argumentsList(argc, argv, &outVar);
|
||||
const QString lookup = u"aotContext->callObjectPropertyLookup("_s + indexString
|
||||
+ u", "_s + inputPointer
|
||||
+ u", args, types, "_s + QString::number(argc) + u')';
|
||||
const QString initialization = u"aotContext->initCallObjectPropertyLookup("_s
|
||||
+ indexString + u')';
|
||||
generateLookup(lookup, initialization);
|
||||
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);
|
||||
|
||||
const QString lookup = u"doCall()"_s;
|
||||
const QString initialization = u"doInit()"_s;
|
||||
const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
|
||||
generateLookup(lookup, initialization, preparation);
|
||||
generateMoveOutVar(outVar);
|
||||
|
||||
m_body += u"}\n"_s;
|
||||
|
@ -2295,16 +2296,16 @@ void QQmlJSCodeGenerator::generate_CallQmlContextPropertyLookup(int index, int a
|
|||
|
||||
AccumulatorConverter registers(this);
|
||||
|
||||
const QString indexString = QString::number(index);
|
||||
|
||||
m_body += u"{\n"_s;
|
||||
QString outVar;
|
||||
m_body += argumentsList(argc, argv, &outVar);
|
||||
const QString lookup = u"aotContext->callQmlContextPropertyLookup("_s + indexString
|
||||
+ u", args, types, "_s + QString::number(argc) + u')';
|
||||
const QString initialization = u"aotContext->initCallQmlContextPropertyLookup("_s
|
||||
+ indexString + u')';
|
||||
generateLookup(lookup, initialization);
|
||||
m_body += initAndCall(
|
||||
argc, argv, u"callQmlContextPropertyLookup(%1, %2, %3)"_s.arg(index),
|
||||
u"initCallQmlContextPropertyLookup(%1, %2, %3)"_s.arg(index), &outVar);
|
||||
|
||||
const QString lookup = u"doCall()"_s;
|
||||
const QString initialization = u"doInit()"_s;
|
||||
const QString preparation = getLookupPreparation(m_state.accumulatorOut(), outVar, index);
|
||||
generateLookup(lookup, initialization, preparation);
|
||||
generateMoveOutVar(outVar);
|
||||
|
||||
m_body += u"}\n"_s;
|
||||
|
@ -2899,6 +2900,12 @@ QString QQmlJSCodeGenerator::getLookupPreparation(
|
|||
return var + u" = QVariant(aotContext->lookupResultMetaType("_s
|
||||
+ QString::number(lookup) + u"))"_s;
|
||||
}
|
||||
|
||||
if (registerIsStoredIn(content, m_typeResolver->jsPrimitiveType())) {
|
||||
return var + u" = QJSPrimitiveValue(aotContext->lookupResultMetaType("_s
|
||||
+ QString::number(lookup) + u"))"_s;
|
||||
}
|
||||
|
||||
// TODO: We could make sure they're compatible, for example QObject pointers.
|
||||
return QString();
|
||||
}
|
||||
|
|
|
@ -314,7 +314,10 @@ private:
|
|||
|
||||
|
||||
QString eqIntExpression(int lhsConst);
|
||||
QString argumentsList(int argc, int argv, QString *outVar);
|
||||
|
||||
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);
|
||||
|
|
|
@ -1160,21 +1160,12 @@ void tst_QmlCppCodegen::consoleTrace()
|
|||
QQmlComponent component(&engine, QUrl(u"qrc:/qt/qml/TestTypes/consoleTrace.qml"_s));
|
||||
QVERIFY2(!component.isError(), component.errorString().toUtf8());
|
||||
|
||||
#if !defined(QT_NO_DEBUG) || defined(QT_TEST_FORCE_INTERPRETER)
|
||||
// All line numbers in debug mode or when interpreting
|
||||
// We always get line numbers for the first call since we need to do the "init" step.
|
||||
|
||||
QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6)
|
||||
b (qrc:/qt/qml/TestTypes/consoleTrace.qml:5)
|
||||
a (qrc:/qt/qml/TestTypes/consoleTrace.qml:4)
|
||||
expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml:7))");
|
||||
#else
|
||||
// Only top-most line number otherwise
|
||||
|
||||
QTest::ignoreMessage(QtDebugMsg, R"(c (qrc:/qt/qml/TestTypes/consoleTrace.qml:6)
|
||||
b (qrc:/qt/qml/TestTypes/consoleTrace.qml)
|
||||
a (qrc:/qt/qml/TestTypes/consoleTrace.qml)
|
||||
expression for onCompleted (qrc:/qt/qml/TestTypes/consoleTrace.qml))");
|
||||
#endif
|
||||
|
||||
QScopedPointer<QObject> object(component.create());
|
||||
QVERIFY(!object.isNull());
|
||||
|
|
Loading…
Reference in New Issue