QML: Unify treatment of wrappers when dealing with QObjectMethod

A method can belong to a QObjectWrapper, a QQmlValueTypeWrapper, or a
QQmlTypeWrapper. store only one wrapper pointer and allow for all
variants. Furthermore, keep a reference to the wrapper so that it
doesn't get garbage collected. We still need it to retrieve the
metaobject, even if we're calling the method on a different instance.

Pick-to: 6.2 6.5 6.6
Fixes: QTBUG-115115
Change-Id: I1759fb687918ff79829fef776e0a93d29373b30f
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-07-07 13:50:07 +02:00
parent 1ab49e0d6f
commit 63b622d590
5 changed files with 212 additions and 106 deletions

View File

@ -263,7 +263,7 @@ ReturnedValue QObjectWrapper::getProperty(
if (!global) if (!global)
global = engine->rootContext(); global = engine->rootContext();
return QObjectMethod::create( return QObjectMethod::create(
global, (flags & AttachMethods) ? object : nullptr, property->coreIndex()); global, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
} else if (property->isSignalHandler()) { } else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine); QmlSignalHandler::initProto(engine);
return engine->memoryManager->allocate<QmlSignalHandler>( return engine->memoryManager->allocate<QmlSignalHandler>(
@ -271,7 +271,7 @@ ReturnedValue QObjectWrapper::getProperty(
} else { } else {
ExecutionContext *global = engine->rootContext(); ExecutionContext *global = engine->rootContext();
return QObjectMethod::create( return QObjectMethod::create(
global, (flags & AttachMethods) ? object : nullptr, property->coreIndex()); global, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex());
} }
} }
@ -290,7 +290,8 @@ ReturnedValue QObjectWrapper::getProperty(
} }
} }
static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr) static OptionalReturnedValue getDestroyOrToStringMethod(
ExecutionEngine *v4, String *name, Heap::Object *qobj, bool *hasProperty = nullptr)
{ {
int index = 0; int index = 0;
if (name->equals(v4->id_destroy())) if (name->equals(v4->id_destroy()))
@ -347,7 +348,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
ExecutionEngine *v4 = engine(); ExecutionEngine *v4 = engine();
if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty)) if (auto methodValue = getDestroyOrToStringMethod(v4, name, d(), hasProperty))
return *methodValue; return *methodValue;
QQmlPropertyData local; QQmlPropertyData local;
@ -390,7 +391,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(
return Encode::null(); return Encode::null();
} }
if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty)) if (auto methodValue = getDestroyOrToStringMethod(engine, name, wrapper, hasProperty))
return *methodValue; return *methodValue;
QQmlData *ddata = QQmlData::get(object, false); QQmlData *ddata = QQmlData::get(object, false);
@ -736,15 +737,6 @@ ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObjec
return constWrapper.asReturnedValue(); return constWrapper.asReturnedValue();
} }
Heap::QObjectMethod *QObjectWrapper::cloneMethod(
ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
Heap::Object *wrapper, QObject *object)
{
Scope scope(engine);
Scoped<QObjectMethod> method(scope, QObjectMethod::create(engine, cloneFrom, wrapper, object));
return method ? method->d() : nullptr;
}
void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack)
{ {
if (QQmlData::wasDeleted(object)) if (QQmlData::wasDeleted(object))
@ -984,7 +976,7 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E
return Encode::undefined(); return Encode::undefined();
QQmlData *ddata = QQmlData::get(qobj, false); QQmlData *ddata = QQmlData::get(qobj, false);
if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) { if (auto methodValue = getDestroyOrToStringMethod(engine, name, This->d())) {
Scoped<QObjectMethod> method(scope, *methodValue); Scoped<QObjectMethod> method(scope, *methodValue);
setupQObjectMethodLookup( setupQObjectMethodLookup(
lookup, ddata ? ddata : QQmlData::get(qobj, true), nullptr, This, method->d()); lookup, ddata ? ddata : QQmlData::get(qobj, true), nullptr, This, method->d());
@ -2312,34 +2304,33 @@ ReturnedValue CallArgument::toValue(ExecutionEngine *engine)
return Encode::undefined(); return Encode::undefined();
} }
ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index) ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::Object *wrapper, int index)
{ {
Scope valueScope(scope); Scope valueScope(scope);
Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope)); Scoped<QObjectMethod> method(
method->d()->setObject(object); valueScope,
valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, wrapper, index));
method->d()->index = index;
return method.asReturnedValue(); return method.asReturnedValue();
} }
ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index) ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index)
{ {
Scope valueScope(scope); Scope valueScope(scope);
Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope)); Scoped<QObjectMethod> method(
method->d()->index = index; valueScope,
method->d()->valueTypeWrapper.set(valueScope.engine, valueType); valueScope.engine->memoryManager->allocate<QObjectMethod>(scope, valueType, index));
return method.asReturnedValue(); return method.asReturnedValue();
} }
ReturnedValue QObjectMethod::create( ReturnedValue QObjectMethod::create(
ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
Heap::Object *wrapper, QObject *object) Heap::Object *wrapper, Heap::Object *object)
{ {
Scope valueScope(engine); Scope valueScope(engine);
Scoped<QQmlValueTypeWrapper> valueTypeWrapper(valueScope); Scoped<QQmlValueTypeWrapper> valueTypeWrapper(valueScope);
if (cloneFrom->valueTypeWrapper) { if (cloneFrom->wrapper) {
Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->valueTypeWrapper); Scoped<QQmlValueTypeWrapper> ref(valueScope, cloneFrom->wrapper);
if (ref) { if (ref) {
valueTypeWrapper = QQmlValueTypeWrapper::create(engine, ref->d(), wrapper); valueTypeWrapper = QQmlValueTypeWrapper::create(engine, ref->d(), wrapper);
} else { } else {
@ -2352,16 +2343,12 @@ ReturnedValue QObjectMethod::create(
Scoped<ExecutionContext> context(valueScope, cloneFrom->scope.get()); Scoped<ExecutionContext> context(valueScope, cloneFrom->scope.get());
Scoped<QObjectMethod> method( Scoped<QObjectMethod> method(
valueScope, engine->memoryManager->allocate<QV4::QObjectMethod>(context)); valueScope,
engine->memoryManager->allocate<QV4::QObjectMethod>(
context, valueTypeWrapper ? valueTypeWrapper->d() : object, cloneFrom->index));
method->d()->index = cloneFrom->index;
method->d()->methodCount = cloneFrom->methodCount; method->d()->methodCount = cloneFrom->methodCount;
if (valueTypeWrapper)
method->d()->valueTypeWrapper.set(engine, valueTypeWrapper->d());
else
method->d()->setObject(object);
Q_ASSERT(method->d()->methods == nullptr); Q_ASSERT(method->d()->methods == nullptr);
switch (cloneFrom->methodCount) { switch (cloneFrom->methodCount) {
case 0: case 0:
@ -2384,50 +2371,70 @@ ReturnedValue QObjectMethod::create(
return method.asReturnedValue(); return method.asReturnedValue();
} }
void Heap::QObjectMethod::init(QV4::ExecutionContext *scope) void Heap::QObjectMethod::init(QV4::ExecutionContext *scope, Object *object, int methodIndex)
{ {
Heap::FunctionObject::init(scope); Heap::FunctionObject::init(scope);
wrapper.set(internalClass->engine, object);
index = methodIndex;
} }
const QMetaObject *Heap::QObjectMethod::metaObject() const const QMetaObject *Heap::QObjectMethod::metaObject() const
{ {
if (valueTypeWrapper) Scope scope(internalClass->engine);
return valueTypeWrapper->metaObject();
if (QObject *self = object()) if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
return self->metaObject(); return objectWrapper->metaObject();
if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
return typeWrapper->metaObject();
if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
return valueWrapper->metaObject();
return nullptr;
}
QObject *Heap::QObjectMethod::object() const
{
Scope scope(internalClass->engine);
if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
return objectWrapper->object();
if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
return typeWrapper->object();
return nullptr; return nullptr;
} }
bool Heap::QObjectMethod::isDetached() const bool Heap::QObjectMethod::isDetached() const
{ {
if (qObj.isValid()) if (!wrapper)
return false; return true;
if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) QV4::Scope scope(internalClass->engine);
return wrapper->object() == nullptr; if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper)
return valueWrapper->d()->object() == nullptr;
return true; return false;
} }
bool Heap::QObjectMethod::isAttachedTo(QObject *o) const bool Heap::QObjectMethod::isAttachedTo(QObject *o) const
{ {
if (qObj.isValid() && qObj != o) QV4::Scope scope(internalClass->engine);
return false; if (Scoped<QV4::QObjectWrapper> objectWrapper(scope, wrapper); objectWrapper)
return objectWrapper->object() == o;
if (Scoped<QV4::QQmlTypeWrapper> typeWrapper(scope, wrapper); typeWrapper)
return typeWrapper->object() == o;
if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) { if (Scoped<QV4::QQmlValueTypeWrapper> valueWrapper(scope, wrapper); valueWrapper) {
QV4::Scope scope(wrapper->internalClass->engine); QV4::Scope scope(wrapper->internalClass->engine);
QV4::Scoped<QV4::QObjectWrapper> qobject(scope, wrapper->object()); if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, valueWrapper->d()->object()); qobject)
if (qobject && qobject->object() == o) return qobject->object() == o;
return true; if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, valueWrapper->d()->object()); type)
QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, wrapper->object()); return type->object() == o;
if (type && type->object() == o)
return true;
// Attached to some nested value type or sequence object // Attached to some nested value type or sequence object
return false; return false;
} }
return true; return false;
} }
Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject( Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
@ -2437,7 +2444,7 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
if (!thisMeta) { if (!thisMeta) {
// You can only get a detached method via a lookup, and then you have a thisObject. // You can only get a detached method via a lookup, and then you have a thisObject.
Q_ASSERT(valueTypeWrapper || qObj); Q_ASSERT(wrapper);
return Included; return Included;
} }
@ -2476,11 +2483,8 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject(
Q_UNREACHABLE_RETURN(Invalid); Q_UNREACHABLE_RETURN(Invalid);
}; };
if (QObject *o = object()) if (const QMetaObject *meta = metaObject())
return check(o->metaObject()); return check(meta);
if (valueTypeWrapper)
return check(valueTypeWrapper->metaObject());
// If the QObjectMethod is detached, we can only have gotten here via a lookup. // If the QObjectMethod is detached, we can only have gotten here via a lookup.
// The lookup checks that the QQmlPropertyCache matches. // The lookup checks that the QQmlPropertyCache matches.
@ -2554,15 +2558,6 @@ void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta)
Q_ASSERT(methodCount > 0); Q_ASSERT(methodCount > 0);
} }
static QObject *qObject(const Value *v)
{
if (const QObjectWrapper *o = v->as<QObjectWrapper>())
return o->object();
if (const QQmlTypeWrapper *t = v->as<QQmlTypeWrapper>())
return t->object();
return nullptr;
}
ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, QObject *o) const ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, QObject *o) const
{ {
return engine->newString( return engine->newString(
@ -2603,20 +2598,24 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
const QMetaObject *thisMeta = nullptr; const QMetaObject *thisMeta = nullptr;
QObject *o = qObject(thisObject); QObject *o = nullptr;
Heap::QQmlValueTypeWrapper *wrapper = nullptr; Heap::QQmlValueTypeWrapper *valueWrapper = nullptr;
if (o) { if (const QObjectWrapper *w = thisObject->as<QObjectWrapper>()) {
thisMeta = o->metaObject(); thisMeta = w->metaObject();
} else if (const QQmlValueTypeWrapper *value = thisObject->as<QQmlValueTypeWrapper>()) { o = w->object();
wrapper = value->d(); } else if (const QQmlTypeWrapper *w = thisObject->as<QQmlTypeWrapper>()) {
thisMeta = wrapper->metaObject(); thisMeta = w->metaObject();
o = w->object();
} else if (const QQmlValueTypeWrapper *w = thisObject->as<QQmlValueTypeWrapper>()) {
thisMeta = w->metaObject();
valueWrapper = w->d();
} }
Heap::QObjectMethod::ThisObjectMode mode = Heap::QObjectMethod::Invalid; Heap::QObjectMethod::ThisObjectMode mode = Heap::QObjectMethod::Invalid;
if (o && o == d()->object()) { if (o && o == d()->object()) {
mode = Heap::QObjectMethod::Explicit; mode = Heap::QObjectMethod::Explicit;
// Nothing to do; objects are the same. This should be common // Nothing to do; objects are the same. This should be common
} else if (wrapper && wrapper == d()->valueTypeWrapper) { } else if (valueWrapper && valueWrapper == d()->wrapper) {
mode = Heap::QObjectMethod::Explicit; mode = Heap::QObjectMethod::Explicit;
// Nothing to do; gadgets are the same. This should be somewhat common // Nothing to do; gadgets are the same. This should be somewhat common
} else { } else {
@ -2630,20 +2629,24 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
QQmlObjectOrGadget object = [&](){ QQmlObjectOrGadget object = [&](){
if (mode == Heap::QObjectMethod::Included) { if (mode == Heap::QObjectMethod::Included) {
if (QObject *qObject = d()->qObj) QV4::Scope scope(v4);
return QQmlObjectOrGadget(qObject); if (QV4::Scoped<QV4::QObjectWrapper> qobject(scope, d()->wrapper); qobject)
return QQmlObjectOrGadget(qobject->object());
wrapper = d()->valueTypeWrapper; if (QV4::Scoped<QV4::QQmlTypeWrapper> type(scope, d()->wrapper); type)
Q_ASSERT(wrapper); return QQmlObjectOrGadget(type->object());
return QQmlObjectOrGadget(wrapper->metaObject(), wrapper->gadgetPtr()); if (QV4::Scoped<QV4::QQmlValueTypeWrapper> value(scope, d()->wrapper); value) {
valueWrapper = value->d();
return QQmlObjectOrGadget(valueWrapper->metaObject(), valueWrapper->gadgetPtr());
}
Q_UNREACHABLE();
} else { } else {
if (o) if (o)
return QQmlObjectOrGadget(o); return QQmlObjectOrGadget(o);
Q_ASSERT(wrapper); Q_ASSERT(valueWrapper);
if (!wrapper->enforcesLocation()) if (!valueWrapper->enforcesLocation())
QV4::ReferenceObject::readReference(wrapper); QV4::ReferenceObject::readReference(valueWrapper);
return QQmlObjectOrGadget(thisMeta, wrapper->gadgetPtr()); return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr());
} }
}(); }();
@ -2667,9 +2670,9 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
// The method might change the value. // The method might change the value.
const auto doCall = [&](const auto &call) { const auto doCall = [&](const auto &call) {
if (!method->isConstant()) { if (!method->isConstant()) {
if (wrapper && wrapper->isReference()) { if (valueWrapper && valueWrapper->isReference()) {
ScopedValue rv(scope, call()); ScopedValue rv(scope, call());
d()->valueTypeWrapper->writeBack(); valueWrapper->writeBack();
return rv->asReturnedValue(); return rv->asReturnedValue();
} }
} }

View File

@ -61,23 +61,21 @@ private:
}; };
#define QObjectMethodMembers(class, Member) \ #define QObjectMethodMembers(class, Member) \
Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) Member(class, Pointer, Object *, wrapper) \
DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
DECLARE_MARKOBJECTS(QObjectMethod) DECLARE_MARKOBJECTS(QObjectMethod)
QV4QPointer<QObject> qObj;
QQmlPropertyData *methods; QQmlPropertyData *methods;
alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)]; alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)];
int methodCount; int methodCount;
int index; int index;
void init(QV4::ExecutionContext *scope); void init(QV4::ExecutionContext *scope, Object *wrapper, int index);
void destroy() void destroy()
{ {
if (methods != reinterpret_cast<const QQmlPropertyData *>(&_singleMethod)) if (methods != reinterpret_cast<const QQmlPropertyData *>(&_singleMethod))
delete[] methods; delete[] methods;
qObj.destroy();
FunctionObject::destroy(); FunctionObject::destroy();
} }
@ -85,8 +83,7 @@ DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) {
QString name() const; QString name() const;
const QMetaObject *metaObject() const; const QMetaObject *metaObject() const;
QObject *object() const { return qObj.data(); } QObject *object() const;
void setObject(QObject *o) { qObj = o; }
bool isDetached() const; bool isDetached() const;
bool isAttachedTo(QObject *o) const; bool isAttachedTo(QObject *o) const;
@ -144,6 +141,13 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
static void initializeBindings(ExecutionEngine *engine); static void initializeBindings(ExecutionEngine *engine);
const QMetaObject *metaObject() const
{
if (QObject *o = object())
return o->metaObject();
return nullptr;
}
QObject *object() const { return d()->object(); } QObject *object() const { return d()->object(); }
ReturnedValue getQmlProperty( ReturnedValue getQmlProperty(
@ -220,10 +224,6 @@ protected:
private: private:
Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object); Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object);
Q_NEVER_INLINE static ReturnedValue wrapConst_slowPath(ExecutionEngine *engine, QObject *object); Q_NEVER_INLINE static ReturnedValue wrapConst_slowPath(ExecutionEngine *engine, QObject *object);
static Heap::QObjectMethod *cloneMethod(
ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
Heap::Object *wrapper, QObject *object);
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(QObjectWrapper::Flags) Q_DECLARE_OPERATORS_FOR_FLAGS(QObjectWrapper::Flags)
@ -354,13 +354,11 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
enum { DestroyMethod = -1, ToStringMethod = -2 }; enum { DestroyMethod = -1, ToStringMethod = -2 };
static ReturnedValue create( static ReturnedValue create(QV4::ExecutionContext *scope, Heap::Object *wrapper, int index);
QV4::ExecutionContext *scope, QObject *object, int index);
static ReturnedValue create( static ReturnedValue create(
QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index); QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index);
static ReturnedValue create( static ReturnedValue create(QV4::ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom,
QV4::ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, Heap::Object *wrapper, Heap::Object *object);
Heap::Object *wrapper, QObject *object);
int methodIndex() const { return d()->index; } int methodIndex() const { return d()->index; }
QObject *object() const { return d()->object(); } QObject *object() const { return d()->object(); }

View File

@ -50,19 +50,31 @@ bool QQmlTypeWrapper::isSingleton() const
return d()->type().isSingleton(); return d()->type().isSingleton();
} }
const QMetaObject *QQmlTypeWrapper::metaObject() const
{
const QQmlType type = d()->type();
if (!type.isValid())
return nullptr;
if (type.isSingleton())
return type.metaObject();
return type.attachedPropertiesType(QQmlEnginePrivate::get(engine()->qmlEngine()));
}
QObject *QQmlTypeWrapper::object() const QObject *QQmlTypeWrapper::object() const
{ {
const QQmlType type = d()->type(); const QQmlType type = d()->type();
if (!type.isValid()) if (!type.isValid())
return nullptr; return nullptr;
QQmlEngine *qmlEngine = engine()->qmlEngine(); QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine());
if (type.isSingleton()) if (type.isSingleton())
return QQmlEnginePrivate::get(qmlEngine)->singletonInstance<QObject *>(type); return qmlEngine->singletonInstance<QObject *>(type);
return qmlAttachedPropertiesObject( return qmlAttachedPropertiesObject(
d()->object, d()->object,
type.attachedPropertiesFunction(QQmlEnginePrivate::get(qmlEngine))); type.attachedPropertiesFunction(qmlEngine));
} }
QObject* QQmlTypeWrapper::singletonObject() const QObject* QQmlTypeWrapper::singletonObject() const

View File

@ -66,6 +66,7 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
V4_NEEDS_DESTROY V4_NEEDS_DESTROY
bool isSingleton() const; bool isSingleton() const;
const QMetaObject *metaObject() const;
QObject *object() const; QObject *object() const;
QObject *singletonObject() const; QObject *singletonObject() const;

View File

@ -307,6 +307,7 @@ private slots:
void symbolToVariant(); void symbolToVariant();
void garbageCollectedObjectMethodBase();
public: public:
Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE QJSValue throwingCppMethod1();
Q_INVOKABLE void throwingCppMethod2(); Q_INVOKABLE void throwingCppMethod2();
@ -6163,6 +6164,97 @@ void tst_QJSEngine::symbolToVariant()
QCOMPARE(val.toVariant(QJSValue::ConvertJSObjects), QStringLiteral("Symbol(asymbol)")); QCOMPARE(val.toVariant(QJSValue::ConvertJSObjects), QStringLiteral("Symbol(asymbol)"));
} }
class PACHelper : public QObject {
Q_OBJECT
public:
Q_INVOKABLE bool shExpMatch(const QString &, const QString &) { return false; }
Q_INVOKABLE QString dnsResolve(const QString &) { return QString{}; }
};
class ProxyAutoConf {
public:
void exposeQObjectMethodsAsGlobal(QJSEngine *engine, QObject *object)
{
QJSValue helper = engine->newQObject(object);
QJSValue g = engine->globalObject();
QJSValueIterator it(helper);
while (it.hasNext()) {
it.next();
if (!it.value().isCallable())
continue;
g.setProperty(it.name(), it.value());
}
}
bool parse(const QString & pacBytes)
{
jsFindProxyForURL = QJSValue();
engine = std::make_unique<QJSEngine>();
exposeQObjectMethodsAsGlobal(engine.get(), new PACHelper);
engine->evaluate(pacBytes);
jsFindProxyForURL = engine->globalObject().property(QStringLiteral("FindProxyForURL"));
return true;
}
QString findProxyForUrl(const QString &url, const QString &host)
{
QJSValueList args;
args << url << host;
engine->collectGarbage();
QJSValue callResult = jsFindProxyForURL.call(args);
return callResult.toString().trimmed();
}
private:
std::unique_ptr<QJSEngine> engine;
QJSValue jsFindProxyForURL;
};
QString const pacstring = R"js(
function FindProxyForURL(host) {
list_split_all = Array(
"oneoneoneoneo.oneo.oneo.oneoneo.one",
"twotwotwotwotw.otwo.twot.wotwotw.otw",
"threethreethr.eeth.reet.hreethr.eet",
"fourfourfourfo.urfo.urfo.urfourf.our",
"fivefivefivef.ivef.ivef.ivefive.fiv",
"sixsixsixsixsi.xsix.sixs.ixsixsi.xsi",
"sevensevenseve.nsev.ense.venseve.nse",
"eight.eighteigh.tei",
"*.nin.eninen.ine"
)
list_myip_direct =
"10.254.0.0/255.255.0.0"
for (i = 0; i < list_split_all.length; ++i)
for (j = 0; j < list_myip_direct.length; ++j)
shExpMatch(host, list_split_all)
shExpMatch()
dnsResolve()}
)js";
void tst_QJSEngine::garbageCollectedObjectMethodBase()
{
ProxyAutoConf proxyConf;
bool pac_read = false;
const auto processUrl = [&](QString const &url, QString const &host)
{
if (!pac_read) {
proxyConf.parse(pacstring);
pac_read = true;
}
return proxyConf.findProxyForUrl(url, host);
};
const QString url = QStringLiteral("https://servername.domain.test");
const QString host = QStringLiteral("servername.domain.test");
for (size_t i = 0; i < 5; ++i) {
auto future = std::async(processUrl, url, host);
QCOMPARE(future.get(), QLatin1String("Error: Insufficient arguments"));
}
}
QTEST_MAIN(tst_QJSEngine) QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc" #include "tst_qjsengine.moc"