diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index b273cd9ef0..ad10053394 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -263,7 +263,7 @@ ReturnedValue QObjectWrapper::getProperty( if (!global) global = engine->rootContext(); return QObjectMethod::create( - global, (flags & AttachMethods) ? object : nullptr, property->coreIndex()); + global, (flags & AttachMethods) ? wrapper : nullptr, property->coreIndex()); } else if (property->isSignalHandler()) { QmlSignalHandler::initProto(engine); return engine->memoryManager->allocate( @@ -271,7 +271,7 @@ ReturnedValue QObjectWrapper::getProperty( } else { ExecutionContext *global = engine->rootContext(); 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; if (name->equals(v4->id_destroy())) @@ -347,7 +348,7 @@ ReturnedValue QObjectWrapper::getQmlProperty( ExecutionEngine *v4 = engine(); - if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty)) + if (auto methodValue = getDestroyOrToStringMethod(v4, name, d(), hasProperty)) return *methodValue; QQmlPropertyData local; @@ -390,7 +391,7 @@ ReturnedValue QObjectWrapper::getQmlProperty( return Encode::null(); } - if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty)) + if (auto methodValue = getDestroyOrToStringMethod(engine, name, wrapper, hasProperty)) return *methodValue; QQmlData *ddata = QQmlData::get(object, false); @@ -736,15 +737,6 @@ ReturnedValue QObjectWrapper::wrapConst_slowPath(ExecutionEngine *engine, QObjec return constWrapper.asReturnedValue(); } -Heap::QObjectMethod *QObjectWrapper::cloneMethod( - ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, - Heap::Object *wrapper, QObject *object) -{ - Scope scope(engine); - Scoped method(scope, QObjectMethod::create(engine, cloneFrom, wrapper, object)); - return method ? method->d() : nullptr; -} - void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) { if (QQmlData::wasDeleted(object)) @@ -984,7 +976,7 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E return Encode::undefined(); QQmlData *ddata = QQmlData::get(qobj, false); - if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) { + if (auto methodValue = getDestroyOrToStringMethod(engine, name, This->d())) { Scoped method(scope, *methodValue); setupQObjectMethodLookup( lookup, ddata ? ddata : QQmlData::get(qobj, true), nullptr, This, method->d()); @@ -2312,34 +2304,33 @@ ReturnedValue CallArgument::toValue(ExecutionEngine *engine) 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); - Scoped method(valueScope, valueScope.engine->memoryManager->allocate(scope)); - method->d()->setObject(object); - - method->d()->index = index; + Scoped method( + valueScope, + valueScope.engine->memoryManager->allocate(scope, wrapper, index)); return method.asReturnedValue(); } ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index) { Scope valueScope(scope); - Scoped method(valueScope, valueScope.engine->memoryManager->allocate(scope)); - method->d()->index = index; - method->d()->valueTypeWrapper.set(valueScope.engine, valueType); + Scoped method( + valueScope, + valueScope.engine->memoryManager->allocate(scope, valueType, index)); return method.asReturnedValue(); } ReturnedValue QObjectMethod::create( ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, - Heap::Object *wrapper, QObject *object) + Heap::Object *wrapper, Heap::Object *object) { Scope valueScope(engine); Scoped valueTypeWrapper(valueScope); - if (cloneFrom->valueTypeWrapper) { - Scoped ref(valueScope, cloneFrom->valueTypeWrapper); + if (cloneFrom->wrapper) { + Scoped ref(valueScope, cloneFrom->wrapper); if (ref) { valueTypeWrapper = QQmlValueTypeWrapper::create(engine, ref->d(), wrapper); } else { @@ -2352,16 +2343,12 @@ ReturnedValue QObjectMethod::create( Scoped context(valueScope, cloneFrom->scope.get()); Scoped method( - valueScope, engine->memoryManager->allocate(context)); + valueScope, + engine->memoryManager->allocate( + context, valueTypeWrapper ? valueTypeWrapper->d() : object, cloneFrom->index)); - method->d()->index = cloneFrom->index; 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); switch (cloneFrom->methodCount) { case 0: @@ -2384,50 +2371,70 @@ ReturnedValue QObjectMethod::create( 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); + wrapper.set(internalClass->engine, object); + index = methodIndex; } const QMetaObject *Heap::QObjectMethod::metaObject() const { - if (valueTypeWrapper) - return valueTypeWrapper->metaObject(); - if (QObject *self = object()) - return self->metaObject(); + Scope scope(internalClass->engine); + + if (Scoped objectWrapper(scope, wrapper); objectWrapper) + return objectWrapper->metaObject(); + if (Scoped typeWrapper(scope, wrapper); typeWrapper) + return typeWrapper->metaObject(); + if (Scoped valueWrapper(scope, wrapper); valueWrapper) + return valueWrapper->metaObject(); + + return nullptr; +} + +QObject *Heap::QObjectMethod::object() const +{ + Scope scope(internalClass->engine); + + if (Scoped objectWrapper(scope, wrapper); objectWrapper) + return objectWrapper->object(); + if (Scoped typeWrapper(scope, wrapper); typeWrapper) + return typeWrapper->object(); return nullptr; } bool Heap::QObjectMethod::isDetached() const { - if (qObj.isValid()) - return false; + if (!wrapper) + return true; - if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) - return wrapper->object() == nullptr; + QV4::Scope scope(internalClass->engine); + if (Scoped valueWrapper(scope, wrapper); valueWrapper) + return valueWrapper->d()->object() == nullptr; - return true; + return false; } bool Heap::QObjectMethod::isAttachedTo(QObject *o) const { - if (qObj.isValid() && qObj != o) - return false; + QV4::Scope scope(internalClass->engine); + if (Scoped objectWrapper(scope, wrapper); objectWrapper) + return objectWrapper->object() == o; + if (Scoped typeWrapper(scope, wrapper); typeWrapper) + return typeWrapper->object() == o; - if (Heap::QQmlValueTypeWrapper *wrapper = valueTypeWrapper.get()) { + if (Scoped valueWrapper(scope, wrapper); valueWrapper) { QV4::Scope scope(wrapper->internalClass->engine); - QV4::Scoped qobject(scope, wrapper->object()); - if (qobject && qobject->object() == o) - return true; - QV4::Scoped type(scope, wrapper->object()); - if (type && type->object() == o) - return true; + if (QV4::Scoped qobject(scope, valueWrapper->d()->object()); qobject) + return qobject->object() == o; + if (QV4::Scoped type(scope, valueWrapper->d()->object()); type) + return type->object() == o; // Attached to some nested value type or sequence object return false; } - return true; + return false; } Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject( @@ -2437,7 +2444,7 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject( if (!thisMeta) { // 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; } @@ -2476,11 +2483,8 @@ Heap::QObjectMethod::ThisObjectMode Heap::QObjectMethod::checkThisObject( Q_UNREACHABLE_RETURN(Invalid); }; - if (QObject *o = object()) - return check(o->metaObject()); - - if (valueTypeWrapper) - return check(valueTypeWrapper->metaObject()); + if (const QMetaObject *meta = metaObject()) + return check(meta); // If the QObjectMethod is detached, we can only have gotten here via a lookup. // The lookup checks that the QQmlPropertyCache matches. @@ -2554,15 +2558,6 @@ void Heap::QObjectMethod::ensureMethodsCache(const QMetaObject *thisMeta) Q_ASSERT(methodCount > 0); } -static QObject *qObject(const Value *v) -{ - if (const QObjectWrapper *o = v->as()) - return o->object(); - if (const QQmlTypeWrapper *t = v->as()) - return t->object(); - return nullptr; -} - ReturnedValue QObjectMethod::method_toString(ExecutionEngine *engine, QObject *o) const { return engine->newString( @@ -2603,20 +2598,24 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * const QMetaObject *thisMeta = nullptr; - QObject *o = qObject(thisObject); - Heap::QQmlValueTypeWrapper *wrapper = nullptr; - if (o) { - thisMeta = o->metaObject(); - } else if (const QQmlValueTypeWrapper *value = thisObject->as()) { - wrapper = value->d(); - thisMeta = wrapper->metaObject(); + QObject *o = nullptr; + Heap::QQmlValueTypeWrapper *valueWrapper = nullptr; + if (const QObjectWrapper *w = thisObject->as()) { + thisMeta = w->metaObject(); + o = w->object(); + } else if (const QQmlTypeWrapper *w = thisObject->as()) { + thisMeta = w->metaObject(); + o = w->object(); + } else if (const QQmlValueTypeWrapper *w = thisObject->as()) { + thisMeta = w->metaObject(); + valueWrapper = w->d(); } Heap::QObjectMethod::ThisObjectMode mode = Heap::QObjectMethod::Invalid; if (o && o == d()->object()) { mode = Heap::QObjectMethod::Explicit; // 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; // Nothing to do; gadgets are the same. This should be somewhat common } else { @@ -2630,20 +2629,24 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * QQmlObjectOrGadget object = [&](){ if (mode == Heap::QObjectMethod::Included) { - if (QObject *qObject = d()->qObj) - return QQmlObjectOrGadget(qObject); - - wrapper = d()->valueTypeWrapper; - Q_ASSERT(wrapper); - return QQmlObjectOrGadget(wrapper->metaObject(), wrapper->gadgetPtr()); + QV4::Scope scope(v4); + if (QV4::Scoped qobject(scope, d()->wrapper); qobject) + return QQmlObjectOrGadget(qobject->object()); + if (QV4::Scoped type(scope, d()->wrapper); type) + return QQmlObjectOrGadget(type->object()); + if (QV4::Scoped value(scope, d()->wrapper); value) { + valueWrapper = value->d(); + return QQmlObjectOrGadget(valueWrapper->metaObject(), valueWrapper->gadgetPtr()); + } + Q_UNREACHABLE(); } else { if (o) return QQmlObjectOrGadget(o); - Q_ASSERT(wrapper); - if (!wrapper->enforcesLocation()) - QV4::ReferenceObject::readReference(wrapper); - return QQmlObjectOrGadget(thisMeta, wrapper->gadgetPtr()); + Q_ASSERT(valueWrapper); + if (!valueWrapper->enforcesLocation()) + QV4::ReferenceObject::readReference(valueWrapper); + return QQmlObjectOrGadget(thisMeta, valueWrapper->gadgetPtr()); } }(); @@ -2667,9 +2670,9 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value * // The method might change the value. const auto doCall = [&](const auto &call) { if (!method->isConstant()) { - if (wrapper && wrapper->isReference()) { + if (valueWrapper && valueWrapper->isReference()) { ScopedValue rv(scope, call()); - d()->valueTypeWrapper->writeBack(); + valueWrapper->writeBack(); return rv->asReturnedValue(); } } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index bb1ea95fbe..5816613933 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -61,23 +61,21 @@ private: }; #define QObjectMethodMembers(class, Member) \ - Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) + Member(class, Pointer, Object *, wrapper) \ DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { DECLARE_MARKOBJECTS(QObjectMethod) - QV4QPointer qObj; QQmlPropertyData *methods; alignas(alignof(QQmlPropertyData)) std::byte _singleMethod[sizeof(QQmlPropertyData)]; int methodCount; int index; - void init(QV4::ExecutionContext *scope); + void init(QV4::ExecutionContext *scope, Object *wrapper, int index); void destroy() { if (methods != reinterpret_cast(&_singleMethod)) delete[] methods; - qObj.destroy(); FunctionObject::destroy(); } @@ -85,8 +83,7 @@ DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { QString name() const; const QMetaObject *metaObject() const; - QObject *object() const { return qObj.data(); } - void setObject(QObject *o) { qObj = o; } + QObject *object() const; bool isDetached() const; bool isAttachedTo(QObject *o) const; @@ -144,6 +141,13 @@ struct Q_QML_EXPORT QObjectWrapper : public Object static void initializeBindings(ExecutionEngine *engine); + const QMetaObject *metaObject() const + { + if (QObject *o = object()) + return o->metaObject(); + return nullptr; + } + QObject *object() const { return d()->object(); } ReturnedValue getQmlProperty( @@ -220,10 +224,6 @@ protected: private: Q_NEVER_INLINE static ReturnedValue wrap_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) @@ -354,13 +354,11 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject enum { DestroyMethod = -1, ToStringMethod = -2 }; - static ReturnedValue create( - QV4::ExecutionContext *scope, QObject *object, int index); + static ReturnedValue create(QV4::ExecutionContext *scope, Heap::Object *wrapper, int index); static ReturnedValue create( QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index); - static ReturnedValue create( - QV4::ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, - Heap::Object *wrapper, QObject *object); + static ReturnedValue create(QV4::ExecutionEngine *engine, Heap::QObjectMethod *cloneFrom, + Heap::Object *wrapper, Heap::Object *object); int methodIndex() const { return d()->index; } QObject *object() const { return d()->object(); } diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 07a9d998aa..afcc6292d2 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -50,19 +50,31 @@ bool QQmlTypeWrapper::isSingleton() const 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 { const QQmlType type = d()->type(); if (!type.isValid()) return nullptr; - QQmlEngine *qmlEngine = engine()->qmlEngine(); + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine()->qmlEngine()); if (type.isSingleton()) - return QQmlEnginePrivate::get(qmlEngine)->singletonInstance(type); + return qmlEngine->singletonInstance(type); return qmlAttachedPropertiesObject( d()->object, - type.attachedPropertiesFunction(QQmlEnginePrivate::get(qmlEngine))); + type.attachedPropertiesFunction(qmlEngine)); } QObject* QQmlTypeWrapper::singletonObject() const diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index ff2dda0a02..63e170a3d5 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -66,6 +66,7 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object V4_NEEDS_DESTROY bool isSingleton() const; + const QMetaObject *metaObject() const; QObject *object() const; QObject *singletonObject() const; diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index a94d4bdde6..80b004fa9d 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -307,6 +307,7 @@ private slots: void symbolToVariant(); + void garbageCollectedObjectMethodBase(); public: Q_INVOKABLE QJSValue throwingCppMethod1(); Q_INVOKABLE void throwingCppMethod2(); @@ -6163,6 +6164,97 @@ void tst_QJSEngine::symbolToVariant() 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(); + 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 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) #include "tst_qjsengine.moc"