diff options
author | Fabian Kosmale <[email protected]> | 2024-12-12 14:39:37 +0100 |
---|---|---|
committer | Fabian Kosmale <[email protected]> | 2024-12-18 11:55:52 +0100 |
commit | a5feec81934ab0b074d6a8c7621b591851f6b544 (patch) | |
tree | ca409667609e35a3a5dec64ee2cdd14cf59f634f | |
parent | ac2d9bf0f2c32bdd6a64b8421c414a28369cbe2e (diff) |
QtQml: Avoid potential gc issues
Implicitly constructing a value from a ReturnedValue muddies the
responsibility for ensuring that the gc can find the object.
With this change, we disable the implicit conversion. The expectation
for lifetime management is now:
- If a ReturnedValue is stored on the C++ stack, it must be put into a
QV4::Scoped class (or there should be a comment why not doing so is
safe). Passing a ReturnedValue to a function should no longer be
possible, unless the function takes a ReturnedValue, in which case the
expectation is that it stores the value in a place where it can be
seen by the gc, before doing anything that could trigger a gc run.
Using Value::fromReturnedValue can still be used to pass a Value on,
but in that case, the expectation is that there is a comment which
explains why this is safe.
- If a QV4::Value is obtained from a function call, it ought to be
stored in a ScopedValue, too. We currently can't enforce this easily,
so this should be checked during code review. A possible way forward
would be to disallow returning Values, but that would be a larger
change, and is deferred to the future.
- If a functions has a QV4::Value parameter, it's the callers'
responsibilty to ensure that the gc can find it.
Pick-to: 6.9 6.8 6.5
Fixes: QTBUG-131961
Change-Id: Iea055589d35a5f1ac36fe376d4389eb81de87961
Reviewed-by: Ulf Hermann <[email protected]>
32 files changed, 187 insertions, 89 deletions
diff --git a/src/qml/compat/removed_api.cpp b/src/qml/compat/removed_api.cpp index 6ab4ebe2ab..acdc35ce1c 100644 --- a/src/qml/compat/removed_api.cpp +++ b/src/qml/compat/removed_api.cpp @@ -101,8 +101,10 @@ void QQmlPrivate::AOTCompiledContext::initCallQmlContextPropertyLookup(uint inde bool QQmlPrivate::AOTCompiledContext::loadGlobalLookup(uint index, void *target, QMetaType type) const { QV4::Lookup *lookup = compilationUnit->runtimeLookups + index; + QV4::Scope scope(engine->handle()); + QV4::ScopedValue v(scope, lookup->globalGetter(engine->handle())); if (!QV4::ExecutionEngine::metaTypeFromJS( - lookup->globalGetter(engine->handle()), type, target)) { + v, type, target)) { engine->handle()->throwTypeError(); return false; } diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 25a72cb7a2..e6ed87dab9 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -934,7 +934,8 @@ bool QJSEngine::convertV2(const QJSValue &value, QMetaType metaType, void *ptr) return convertString(*string, metaType, ptr); // Does not need scoping since QJSValue still holds on to the value. - return QV4::ExecutionEngine::metaTypeFromJS(QJSValuePrivate::asReturnedValue(&value), metaType, ptr); + return QV4::ExecutionEngine::metaTypeFromJS(QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&value)), + metaType, ptr); } bool QJSEngine::convertVariant(const QVariant &value, QMetaType metaType, void *ptr) @@ -1166,7 +1167,8 @@ void QJSEngine::throwError(QJSValue::ErrorType errorType, const QString &message */ void QJSEngine::throwError(const QJSValue &error) { - m_v4Engine->throwError(QJSValuePrivate::asReturnedValue(&error)); + // safe, QJSValue holds a persistent reference + m_v4Engine->throwError(QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&error))); } /*! diff --git a/src/qml/jsapi/qjsmanagedvalue.cpp b/src/qml/jsapi/qjsmanagedvalue.cpp index 452f991a26..8094f14698 100644 --- a/src/qml/jsapi/qjsmanagedvalue.cpp +++ b/src/qml/jsapi/qjsmanagedvalue.cpp @@ -788,7 +788,8 @@ void QJSManagedValue::setProperty(const QString &name, const QJSValue &value) return; } QV4::ScopedPropertyKey key(scope, scope.engine->identifierTable->asPropertyKey(name)); - obj->put(key, QJSValuePrivate::convertToReturnedValue(scope.engine, value)); + QV4::ScopedValue val(scope, QJSValuePrivate::convertToReturnedValue(scope.engine, value)); + obj->put(key, val); } } @@ -906,7 +907,10 @@ void QJSManagedValue::setProperty(quint32 arrayIndex, const QJSValue &value) "Value was created in different engine."); return; } - obj->put(arrayIndex, QJSValuePrivate::convertToReturnedValue(v4, value)); + v4 = obj->engine(); // in case value was primitive + QV4::Scope scope(v4); + QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(v4, value)); + obj->put(arrayIndex, v); } } @@ -1121,6 +1125,8 @@ QJSManagedValue QJSManagedValue::jsMetaInstantiate(const QJSValueList &values) c *result.d = c->engine()->newObject(c->d()); QV4::Object *o = result.d->as<QV4::Object>(); + QV4::Scope scope(engine); + QV4::ScopedValue val(scope); for (uint i = 0, end = qMin(qsizetype(c->d()->size), values.size()); i < end; ++i) { const QJSValue &arg = values[i]; if (Q_UNLIKELY(!QJSValuePrivate::checkEngine(engine, arg))) { @@ -1128,7 +1134,8 @@ QJSManagedValue QJSManagedValue::jsMetaInstantiate(const QJSValueList &values) c "Argument was created in different engine."); return QJSManagedValue(); } - o->setProperty(i, QJSValuePrivate::convertToReturnedValue(engine, arg)); + val = QJSValuePrivate::convertToReturnedValue(engine, arg); + o->setProperty(i, val); } return result; diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index f6b97262c3..f230013176 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -909,7 +909,10 @@ QJSValue& QJSValue::operator=(const QJSValue& other) if (const QString *string = QJSValuePrivate::asQString(&other)) QJSValuePrivate::setString(this, *string); else - QJSValuePrivate::setValue(this, QJSValuePrivate::asReturnedValue(&other)); + // fomReturnedValue is safe, as the QJSValue still has a persistent reference + QJSValuePrivate::setValue( + this, + QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&other))); return *this; } @@ -997,17 +1000,19 @@ static bool js_equal(const QString &string, const QV4::Value &value) */ bool QJSValue::equals(const QJSValue& other) const { + // QJSValue stores heap items in persistent values, which already ensures marking + // therefore, fromReturnedValue below is safe if (const QString *string = QJSValuePrivate::asQString(this)) { if (const QString *otherString = QJSValuePrivate::asQString(&other)) return *string == *otherString; - return js_equal(*string, QJSValuePrivate::asReturnedValue(&other)); + return js_equal(*string, Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&other))); } if (const QString *otherString = QJSValuePrivate::asQString(&other)) - return js_equal(*otherString, QJSValuePrivate::asReturnedValue(this)); + return js_equal(*otherString, Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this))); - return Runtime::CompareEqual::call(QJSValuePrivate::asReturnedValue(this), - QJSValuePrivate::asReturnedValue(&other)); + return Runtime::CompareEqual::call(Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)), + Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&other))); } /*! @@ -1048,8 +1053,10 @@ bool QJSValue::strictlyEquals(const QJSValue& other) const return false; } - return RuntimeHelpers::strictEqual(QJSValuePrivate::asReturnedValue(this), - QJSValuePrivate::asReturnedValue(&other)); + // QJSValue stores heap objects persistently, so we can be sure that they'll be marked + // thus we can safely use fromReturnedValue + return RuntimeHelpers::strictEqual(Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)), + Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&other))); } /*! diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index 4624652c93..d9da158498 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -197,7 +197,7 @@ public: static QJSValue fromReturnedValue(QV4::ReturnedValue d) { QJSValue result; - setValue(&result, d); + setValue(&result, QV4::Value::fromReturnedValue(d)); return result; } diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 76a1ecc7bf..b952888859 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1951,8 +1951,11 @@ QV4::ReturnedValue ExecutionEngine::fromData( // and build a JS array from them. if (sequence.hasConstIterator() && sequence.canGetValueAtConstIterator()) { QV4::ScopedArrayObject a(scope, newArrayObject()); - for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it) - a->push_back(fromVariant(*it)); + QV4::ScopedValue v(scope); + for (auto it = iterable.constBegin(), end = iterable.constEnd(); it != end; ++it) { + v = fromVariant(*it); + a->push_back(v); + } return a.asReturnedValue(); } } @@ -2499,9 +2502,12 @@ bool convertToIterable(QMetaType metaType, void *data, Source *sequence) return false; const QMetaType elementMetaType = iterable.valueMetaType(); + QV4::Scope scope(sequence->engine()); + QV4::ScopedValue v(scope); for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) { QVariant element(elementMetaType); - ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data()); + v = sequence->get(i); + ExecutionEngine::metaTypeFromJS(v, elementMetaType, element.data()); iterable.addValue(element, QSequentialIterable::AtEnd); } return true; @@ -2561,9 +2567,12 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi QByteArray result; const qint64 length = ao->getLength(); result.reserve(length); + QV4::Scope scope(ao->engine()); + QV4::ScopedValue v(scope); for (qint64 i = 0; i < length; ++i) { char value = 0; - ExecutionEngine::metaTypeFromJS(ao->get(i), QMetaType::fromType<char>(), &value); + v = ao->get(i); + ExecutionEngine::metaTypeFromJS(v, QMetaType::fromType<char>(), &value); result.push_back(value); } *reinterpret_cast<QByteArray*>(data) = std::move(result); @@ -2819,7 +2828,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value &value, QMetaType metaType, voi *reinterpret_cast<void* *>(data) = nullptr; return true; } else if (metaType == QMetaType::fromType<QJSValue>()) { - QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value.asReturnedValue()); + QJSValuePrivate::setValue(reinterpret_cast<QJSValue*>(data), value); return true; } else if (metaType == QMetaType::fromType<QJSPrimitiveValue>()) { *reinterpret_cast<QJSPrimitiveValue *>(data) = createPrimitive(&value); diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h index 85dfa89985..70f8d23e09 100644 --- a/src/qml/jsruntime/qv4jscall_p.h +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -288,8 +288,9 @@ inline ReturnedValue coerceListType( const qsizetype length = array->getLength(); qsizetype i = 0; + ScopedValue v(scope); for (; i < length; ++i) { - ScopedValue v(scope, array->get(i)); + v = array->get(i); listProperty->append(listProperty, coerceQObject(v, qmlType)); } @@ -299,8 +300,11 @@ inline ReturnedValue coerceListType( QV4::Scoped<Sequence> sequence( scope, SequencePrototype::fromData(engine, type, metaSequence(), nullptr)); const qsizetype length = array->getLength(); - for (qsizetype i = 0; i < length; ++i) - sequence->containerPutIndexed(i, array->get(i)); + ScopedValue v(scope); + for (qsizetype i = 0; i < length; ++i) { + v = array->get(i); + sequence->containerPutIndexed(i, v); + } return sequence->asReturnedValue(); } @@ -413,7 +417,7 @@ ReturnedValue coerceAndCall( const CompiledData::Parameter *formals = compiledFunction->formalsTable(); for (qsizetype i = 0; i < jsCallData.argc; ++i) { jsCallData.args[i] = coerce( - engine, i < argc ? argv[i] : Encode::undefined(), + engine, i < argc ? argv[i] : QV4::Value::fromReturnedValue(Encode::undefined()), typedFunction->types[i + 1], formals[i].type.isList()); } diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 00afb71595..ffa17f5a4d 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -45,7 +45,9 @@ void Object::setInternalClass(Heap::InternalClass *ic) for (uint i = 0; i < ic->size; ++i) { // Note that some members might have been deleted. The key may be invalid. const PropertyKey key = ic->nameMap.at(i); - newMembers->set(scope.engine, i, key.isValid() ? get(key) : Encode::undefined()); + // fromReturnedValue is safe, we directly write it through the WriteBarrier + newMembers->set(scope.engine, i, + QV4::Value::fromReturnedValue(key.isValid() ? get(key) : Encode::undefined())); } p->internalClass.set(scope.engine, ic); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index bd357cb2cb..6cd8e399ec 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -2477,7 +2477,9 @@ bool CallArgument::fromValue(QMetaType metaType, ExecutionEngine *engine, const default: if (type == qMetaTypeId<QJSValue>()) { qjsValuePtr = new (&allocData) QJSValue; - QJSValuePrivate::setValue(qjsValuePtr, value.asReturnedValue()); + Scope scope(engine); + ScopedValue v(scope, value); + QJSValuePrivate::setValue(qjsValuePtr, v); return true; } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 76d385e192..d5bb7f2c27 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -300,7 +300,10 @@ ReturnedValue Runtime::Closure::call(ExecutionEngine *engine, int functionId) Scoped<QV4::QQmlContextWrapper> qmlContextWrapper(s, callingQmlContext->d()->qml()); const QV4::QQmlContextWrapper *resource = qmlContextWrapper; QQmlRefPointer<QQmlContextData> context = resource->getContext(); - if (!context->importedScripts().isNullOrUndefined()) { + /* we can use fromReturnedValue here, as isNullOrUndefined doesn't allocate. + fetching importedScripts twvice is less overhead than unconditionally creating a scope, + as script imports are rather rare*/ + if (!QV4::Value::fromReturnedValue(context->importedScripts()).isNullOrUndefined()) { QV4::ScopedString name(s, engine->newString(QLatin1StringView("$importedScripts"))); QV4::ScopedObject scripts(s, context->importedScripts()); closure->insertMember(name, scripts, Attr_Invalid); diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 363c7b069a..416ed959cc 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -666,9 +666,12 @@ bool convertToIterable(QMetaType metaType, void *data, QV4::Object *sequence) return false; const QMetaType elementMetaType = iterable.valueMetaType(); + QV4::Scope scope(sequence->engine()); + QV4::ScopedValue v(scope); for (qsizetype i = 0, end = sequence->getLength(); i < end; ++i) { QVariant element(elementMetaType); - ExecutionEngine::metaTypeFromJS(sequence->get(i), elementMetaType, element.data()); + v = sequence->get(i); + ExecutionEngine::metaTypeFromJS(v, elementMetaType, element.data()); iterable.addValue(element, QSequentialIterable::AtEnd); } return true; diff --git a/src/qml/jsruntime/qv4urlobject.cpp b/src/qml/jsruntime/qv4urlobject.cpp index 89c8b9cda2..ff67af84a1 100644 --- a/src/qml/jsruntime/qv4urlobject.cpp +++ b/src/qml/jsruntime/qv4urlobject.cpp @@ -806,7 +806,8 @@ ReturnedValue UrlSearchParamsCtor::virtualCallAsConstructor(const FunctionObject uint len = argArray->getLength(); for (uint i = 0; i < len; i++) { - QV4::Value pair = argArray->get(i); + // safe to user fromReturnedValue: Normal array, which will ensure marking + QV4::Value pair = Value::fromReturnedValue(argArray->get(i)); auto *pairArrayObject = pair.as<ArrayObject>(); if (pairArrayObject == nullptr) { @@ -895,11 +896,14 @@ void UrlSearchParamsObject::initializeParams(ScopedArrayObject& params) for (uint i = 0; i < len; i++) { - QV4::Value pair = params->get(i); + // fromReturnedValue is safe; everything is reachable via params + // so the gc won't collect it; we control params, so there can't be + // any weird proxy magic + QV4::Value pair = Value::fromReturnedValue(params->get(i)); auto *pairArrayObject = pair.as<ArrayObject>(); - QV4::Value key = pairArrayObject->get(uint(0)); - QV4::Value value = pairArrayObject->get(uint(1)); + QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0))); + QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1))); scopedKeys->put(i, key); scopedValues->put(i, value); @@ -1014,11 +1018,11 @@ QList<QStringList> UrlSearchParamsObject::params() const uint len = scopedArray->getLength(); for (uint i = 0; i < len; i++) { - QV4::Value pair = scopedArray->get(i); + QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i)); auto *pairArrayObject = pair.as<ArrayObject>(); - QV4::Value key = pairArrayObject->get(uint(0)); - QV4::Value value = pairArrayObject->get(uint(1)); + QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0))); + QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(uint(1))); result << QStringList { key.toQString(), value.toQString() }; } @@ -1063,10 +1067,11 @@ int UrlSearchParamsObject::indexOf(QString name, int last) const int len = scopedArray->getLength(); for (int i = last + 1; i < len; i++) { - QV4::Value pair = scopedArray->get(i); + // fromReturnedValue is safe, scopedArray is a normal array and takes care of marking + QV4::Value pair = Value::fromReturnedValue(scopedArray->get(i)); auto *pairArrayObject = pair.as<ArrayObject>(); - QV4::Value key = pairArrayObject->get(uint(0)); + QV4::Value key = Value::fromReturnedValue(pairArrayObject->get(uint(0))); if (key.toQString() == name) return i; @@ -1084,10 +1089,10 @@ QString UrlSearchParamsObject::stringAt(int index, int pairIndex) const if (index >= scopedArray->getLength()) return {}; - QV4::Value pair = scopedArray->get(index); + QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index)); auto *pairArrayObject = pair.as<ArrayObject>(); - QV4::Value value = pairArrayObject->get(pairIndex); + QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex)); return value.toQString(); } @@ -1101,10 +1106,10 @@ QV4::Heap::String * UrlSearchParamsObject::stringAtRaw(int index, int pairIndex) if (index >= scopedArray->getLength()) return nullptr; - QV4::Value pair = scopedArray->get(index); + QV4::Value pair = Value::fromReturnedValue(scopedArray->get(index)); auto *pairArrayObject = pair.as<ArrayObject>(); - QV4::Value value = pairArrayObject->get(pairIndex); + QV4::Value value = Value::fromReturnedValue(pairArrayObject->get(pairIndex)); return value.as<String>()->d(); } diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 9bbbf63bd0..0f9bbd3b06 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -38,9 +38,6 @@ struct Q_QML_EXPORT Value : public StaticValue { using ManagedPtr = Managed *; - Value() = default; - constexpr Value(quint64 val) : StaticValue(val) {} - static constexpr Value fromStaticValue(StaticValue staticValue) { return {staticValue._val}; diff --git a/src/qml/qml/qqml.cpp b/src/qml/qml/qqml.cpp index 5d469ac7c7..624d438664 100644 --- a/src/qml/qml/qqml.cpp +++ b/src/qml/qml/qqml.cpp @@ -1395,7 +1395,9 @@ static PropertyResult storeObjectAsVariant( return storeObjectProperty<true>(lookup, object, variant->data()); QVariant converted(propType); - if (v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data()) + QV4::Scope scope(v4); + QV4::ScopedValue val(scope, v4->fromVariant(*variant)); + if (v4->metaTypeFromJS(val, propType, converted.data()) || QMetaType::convert( variantMetaType, variant->constData(), propType, converted.data())) { return storeObjectProperty<true>(lookup, object, converted.data()); @@ -1429,7 +1431,9 @@ static PropertyResult storeFallbackAsVariant( return storeFallbackProperty(lookup, object, variant->data()); QVariant converted(propType); - if (v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data()) + QV4::Scope scope(v4); + QV4::ScopedValue val(scope, v4->fromVariant(*variant)); + if (v4->metaTypeFromJS(val, propType, converted.data()) || QMetaType::convert( variant->metaType(), variant->constData(), propType, converted.data())) { return storeFallbackProperty(lookup, object, converted.data()); @@ -1697,7 +1701,9 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType QVariant var(propType); QV4::ExecutionEngine *v4 = engine->handle(); - if (v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data()) + QV4::Scope scope(v4); + QV4::ScopedValue val(scope, v4->metaTypeToJS(type, value)); + if (v4->metaTypeFromJS(val, propType, var.data()) || QMetaType::convert(type, value, propType, var.data())) { storeResult = storeObjectProperty(&lookup, qmlScopeObject, var.data()); } @@ -1727,7 +1733,9 @@ void AOTCompiledContext::storeNameSloppy(uint nameIndex, void *value, QMetaType QVariant var(propType); QV4::ExecutionEngine *v4 = engine->handle(); - if (v4->metaTypeFromJS(v4->metaTypeToJS(type, value), propType, var.data()) + QV4::Scope scope(v4); + QV4::ScopedValue val(scope, v4->metaTypeToJS(type, value)); + if (v4->metaTypeFromJS(val, propType, var.data()) || QMetaType::convert(type, value, propType, var.data())) { storeResult = storeFallbackProperty(&lookup, qmlScopeObject, var.data()); } @@ -2200,8 +2208,10 @@ bool AOTCompiledContext::loadGlobalLookup(uint index, void *target) const QV4::Lookup *lookup = compilationUnit->runtimeLookups + index; if (!lookup->protoLookup.metaType) return false; + QV4::Scope scope(engine->handle()); + QV4::ScopedValue val(scope, lookup->globalGetter(engine->handle())); if (!QV4::ExecutionEngine::metaTypeFromJS( - lookup->globalGetter(engine->handle()), + val, QMetaType(lookup->protoLookup.metaType), target)) { engine->handle()->throwTypeError(); return false; @@ -2739,7 +2749,9 @@ static PropertyResult storeValueAsVariant( return storeValueProperty(lookup, metaObject, target, variant->data()); QVariant converted(propType); - if (v4->metaTypeFromJS(v4->fromVariant(*variant), propType, converted.data()) + QV4::Scope scope(v4); + QV4::ScopedValue val(scope, v4->fromVariant(*variant)); + if (v4->metaTypeFromJS(val, propType, converted.data()) || QMetaType::convert( variant->metaType(), variant->constData(), propType, converted.data())) { return storeValueProperty(lookup, metaObject, target, converted.data()); diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 50891d9a25..89c24b09b4 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -218,7 +218,9 @@ protected: } // If the type didn't match, we need to do JavaScript conversion. This should be rare. - return write(engine()->handle()->metaTypeToJS(type, result), isUndefined, flags); + QV4::Scope scope(engine()->handle()); + QV4::ScopedValue value(scope, engine()->handle()->metaTypeToJS(type, result)); + return write(value, isUndefined, flags); } // Returns true if successful, false if an error description was set on expression @@ -441,8 +443,10 @@ bool QQmlBinding::slowWrite( if (core.isVarProperty()) { QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(m_target.data()); Q_ASSERT(vmemo); + QV4::Scope scope(qmlEngine->handle()); + QV4::ScopedValue value(scope, qmlEngine->handle()->metaTypeToJS(resultType, result)); vmemo->setVMEProperty(core.coreIndex(), - qmlEngine->handle()->metaTypeToJS(resultType, result)); + value); } else if (isUndefined && core.isResettable()) { void *args[] = { nullptr }; QMetaObject::metacall(m_target.data(), QMetaObject::ResetProperty, core.coreIndex(), args); @@ -701,7 +705,7 @@ void QQmlBinding::doUpdate(const DeleteWatcher &watcher, QQmlPropertyData::Write if (returnType.flags() & QMetaType::NeedsDestruction) returnType.destruct(result); } else if (canWrite()) { - error = !write(QV4::Encode::undefined(), true, flags); + error = !write(QV4::Value::undefinded(), true, flags); } } } else { diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 21dad031ac..8e61449e6e 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -384,7 +384,8 @@ bool QQmlComponentPrivate::setInitialProperty( } const QString lastProperty = properties.last(); segment = scope.engine->newString(lastProperty); - object->put(segment, scope.engine->metaTypeToJS(value.metaType(), value.constData())); + QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(value.metaType(), value.constData())); + object->put(segment, v); if (scope.engine->hasException) { qmlWarning(base, scope.engine->catchExceptionAsQmlError()); scope.engine->hasException = false; diff --git a/src/qml/qml/qqmlcontextdata_p.h b/src/qml/qml/qqmlcontextdata_p.h index 5e7198938e..e4c06491ed 100644 --- a/src/qml/qml/qqmlcontextdata_p.h +++ b/src/qml/qml/qqmlcontextdata_p.h @@ -274,7 +274,8 @@ public: bool isRootObjectInCreation() const { return m_isRootObjectInCreation; } void setRootObjectInCreation(bool rootInCreation) { m_isRootObjectInCreation = rootInCreation; } - QV4::Value importedScripts() const { + QV4::ReturnedValue importedScripts() const + { if (m_hasWeakImportedScripts) return m_weakImportedScripts.value(); else @@ -286,6 +287,17 @@ public: m_importedScripts.set(engine, scripts); } + /* + we can safely pass a ReturnedValue here, as setImportedScripts will directly store + scripts in a persistentValue, without any intermediate allocation that could trigger + a gc run + */ + void setImportedScripts(QV4::ExecutionEngine *engine, QV4::ReturnedValue scripts) { + // setImportedScripts should not be called on an invalidated context + Q_ASSERT(!m_hasWeakImportedScripts); + m_importedScripts.set(engine, scripts); + } + QQmlRefPointer<QQmlContextData> linkedContext() const { return m_linkedContext; } void setLinkedContext(const QQmlRefPointer<QQmlContextData> &context) { m_linkedContext = context; } diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp index ead8a717f5..38a3f3b441 100644 --- a/src/qml/qml/qqmldelayedcallqueue.cpp +++ b/src/qml/qml/qqmldelayedcallqueue.cpp @@ -154,7 +154,8 @@ void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, QQmlV4Fun QV4::ScopedArrayObject array(scope, engine->newArrayObject(length)); uint i = 0; for (int j = offset, ej = args->length(); j < ej; ++i, ++j) - array->put(i, (*args)[j]); + // fromReturnedValue is safe, args should already ensure marking + array->put(i, QV4::Value::fromReturnedValue((*args)[j])); dfc.m_args.set(engine, array); } diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index f77f72fb40..be83629b37 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -561,10 +561,11 @@ static QVariant byProperties( // Generally, the GC might collect a Value at any point so that // a `ScopedValue` should be used. // In this case, the Value is tied to a `QJSValue` which is - // persistent to the GC and thus the cast is safe. + // persistent to the GC and thus fromReturnedValue is safe. return byProperties( targetMetaObject, targetMetaType, - QV4::Value(QJSValuePrivate::asReturnedValue(&val)), engine); + QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&val)), + engine); } if (source.metaType() == QMetaType::fromType<QVariantMap>()) { @@ -714,9 +715,11 @@ bool QQmlValueTypeProvider::populateValueType( // Generally, the GC might collect a Value at any point so that // a `ScopedValue` should be used. // In this case, the Value is tied to a `QJSValue` which is - // persistent to the GC and thus the cast is safe. + // persistent to the GC and thus fromReturnedValue is safe. return populateValueType( - targetMetaType, target, QV4::Value(QJSValuePrivate::asReturnedValue(val)), engine); + targetMetaType, target, + QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(val)), + engine); } if (!isConstructibleMetaType(targetMetaType)) diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 067ea13ab3..89ca1e325e 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -62,8 +62,10 @@ static void appendWrapped(QQmlListProperty<QObject> *p, QObject *o) } ArrayData::realloc(object.object, Heap::ArrayData::Simple, length + 1, false); + QV4::Scope scope(object.scope.engine); + QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(object.scope.engine, o)); arrayData->vtable()->put( - object.object, length, QV4::QObjectWrapper::wrap(object.scope.engine, o)); + object.object, length, wrappedObject); } static qsizetype countWrapped(QQmlListProperty<QObject> *p) @@ -88,8 +90,10 @@ static void clearWrapped(QQmlListProperty<QObject> *p) static void replaceWrapped(QQmlListProperty<QObject> *p, qsizetype i, QObject *o) { ListWrapperObject object(p); + QV4::Scope scope(object.scope.engine); + QV4::ScopedObject wrappedObject(scope, QV4::QObjectWrapper::wrap(object.scope.engine, o)); object.arrayData()->vtable()->put( - object.object, i, QV4::QObjectWrapper::wrap(object.scope.engine, o)); + object.object, i, wrappedObject); } static void removeLastWrapped(QQmlListProperty<QObject> *p) @@ -481,9 +485,11 @@ ReturnedValue PropertyListPrototype::method_splice(const FunctionObject *b, cons ScopedArrayObject newArray(scope, scope.engine->newArrayObject()); newArray->arrayReserve(deleteCount); ScopedValue v(scope); + QV4::ScopedValue wrappedObject(scope); for (qsizetype i = 0; i < deleteCount; ++i) { + wrappedObject = QObjectWrapper::wrap(scope.engine, property->at(property, start + i)); newArray->arrayPut( - i, QObjectWrapper::wrap(scope.engine, property->at(property, start + i))); + i, wrappedObject); } newArray->setArrayLengthUnchecked(deleteCount); diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index d986ed9b39..7419a2922a 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -198,7 +198,8 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI if (!isComponentRoot && sharedState->creationContext) { // otherwise QQmlEnginePrivate::createInternalContext() handles it - context->setImportedScripts(v4, sharedState->creationContext->importedScripts()); + QV4::ScopedValue scripts(scope, sharedState->creationContext->importedScripts()); + context->setImportedScripts(v4, scripts); } QObject *instance = createInstance(objectToCreate, parent, /*isContextObject*/true); diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 49fbd1c49c..338de40002 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1475,7 +1475,8 @@ static bool tryAssignBinding( if (!f || !f->isBinding()) return false; - QV4::QObjectWrapper::setProperty(f->engine(), object, &property, f->asReturnedValue()); + // fromReturnedValue is safe! f is stored in the QJSValue, so htere's already a persistent reference to it + QV4::QObjectWrapper::setProperty(f->engine(), object, &property, QV4::Value::fromReturnedValue(f->asReturnedValue())); return true; } diff --git a/src/qml/qml/qqmlscriptdata.cpp b/src/qml/qml/qqmlscriptdata.cpp index 815c33587f..29aac49888 100644 --- a/src/qml/qml/qqmlscriptdata.cpp +++ b/src/qml/qml/qqmlscriptdata.cpp @@ -49,7 +49,7 @@ QQmlRefPointer<QQmlContextData> QQmlScriptData::qmlContextDataForContext( QV4::Scope scope(v4); QV4::ScopedObject scriptsArray(scope); - if (qmlContextData->importedScripts().isNullOrUndefined()) { + if (QV4::Value::fromReturnedValue(qmlContextData->importedScripts()).isNullOrUndefined()) { scriptsArray = v4->newArrayObject(scripts.size()); qmlContextData->setImportedScripts(v4, scriptsArray); } else { diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 977733ef91..8a72c2cc5a 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -77,8 +77,9 @@ void QQmlVMEResolvedList::append(QObject *o) const QV4::ScopedObject object(scope, m_list); QV4::ArrayData::realloc(object, QV4::Heap::ArrayData::Simple, length + 1, false); + QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, o)); arrayData->vtable()->put( - object, length, QV4::QObjectWrapper::wrap(scope.engine, o)); + object, length, wrappedObject); } QObject *QQmlVMEResolvedList::at(qsizetype i) const @@ -92,7 +93,8 @@ void QQmlVMEResolvedList::replace(qsizetype i, QObject *o) const { QV4::Scope scope(m_list->internalClass->engine); QV4::ScopedObject object(scope, m_list); - m_list->arrayData->vtable()->put(object, i, QV4::QObjectWrapper::wrap(scope.engine, o)); + QV4::ScopedValue wrappedObject(scope, QV4::QObjectWrapper::wrap(scope.engine, o)); + m_list->arrayData->vtable()->put(object, i, wrappedObject); } QQmlVMEResolvedList::~QQmlVMEResolvedList() = default; diff --git a/src/qmlmodels/qqmllistmodel.cpp b/src/qmlmodels/qqmllistmodel.cpp index 2b919a5dc2..3d255617dc 100644 --- a/src/qmlmodels/qqmllistmodel.cpp +++ b/src/qmlmodels/qqmllistmodel.cpp @@ -628,7 +628,9 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) if (role.type == ListLayout::Role::QObject) roleIndex = e->setQObjectProperty(role, wrapper); } else if (QVariant maybeUrl = QV4::ExecutionEngine::toVariant( - o->asReturnedValue(), QMetaType::fromType<QUrl>(), true); + // gc will hold on to o via the scoped propertyValue; fromReturnedValue is safe + QV4::Value::fromReturnedValue(o->asReturnedValue()), + QMetaType::fromType<QUrl>(), true); maybeUrl.metaType() == QMetaType::fromType<QUrl>()) { const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url); QUrl qurl = maybeUrl.toUrl(); @@ -713,7 +715,9 @@ void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement e->setQObjectPropertyFast(r, wrapper); } else { QVariant maybeUrl = QV4::ExecutionEngine::toVariant( - o->asReturnedValue(), QMetaType::fromType<QUrl>(), true); + // gc will hold on to o via the scoped propertyValue; fromReturnedValue is safe + QV4::Value::fromReturnedValue(o->asReturnedValue()), + QMetaType::fromType<QUrl>(), true); if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) { const QUrl qurl = maybeUrl.toUrl(); const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url); @@ -1535,7 +1539,9 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d roleIndex = setVariantMapProperty(role, o); } else if (role.type == ListLayout::Role::Url) { QVariant maybeUrl = QV4::ExecutionEngine::toVariant( - o.asReturnedValue(), QMetaType::fromType<QUrl>(), true); + // gc will hold on to o via the scoped propertyValue; fromReturnedValue is safe + QV4::Value::fromReturnedValue(o.asReturnedValue()), + QMetaType::fromType<QUrl>(), true); if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) { roleIndex = setUrlProperty(role, maybeUrl.toUrl()); } @@ -1746,9 +1752,11 @@ PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *p if (auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) { auto size = recursiveListModel->count(); auto array = ScopedArrayObject{scope, v4->newArrayObject(size)}; + QV4::ScopedValue val(scope); for (auto i = 0; i < size; i++) { - array->arrayPut(i, QJSValuePrivate::convertToReturnedValue( - v4, recursiveListModel->get(i))); + val = QJSValuePrivate::convertToReturnedValue( + v4, recursiveListModel->get(i)); + array->arrayPut(i, val); } pd->value = array; } else { @@ -2932,7 +2940,7 @@ bool QQmlListModelParser::applyProperty( if (v4->hasException) v4->catchException(); else - QJSValuePrivate::setValue(&v, result->asReturnedValue()); + QJSValuePrivate::setValue(&v, result); value.setValue(v); } else { bool ok; diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 7a79f7fce5..54bf9946c4 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -5158,11 +5158,11 @@ void tst_QJSEngine::mathMinMax() QJSValue result = engine.evaluate("var a = .5; Math.min(1, 2, 3.5 + a, '5')"); QCOMPARE(result.toNumber(), 1.0); - QVERIFY(QV4::Value(QJSValuePrivate::asReturnedValue(&result)).isInteger()); + QVERIFY(QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&result)).isInteger()); result = engine.evaluate("var a = .5; Math.max('0', 1, 2, 3.5 + a)"); QCOMPARE(result.toNumber(), 4.0); - QVERIFY(QV4::Value(QJSValuePrivate::asReturnedValue(&result)).isInteger()); + QVERIFY(QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&result)).isInteger()); } void tst_QJSEngine::mathNegativeZero() diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index 6a10b5910d..34c3b5d8da 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -2808,7 +2808,7 @@ void tst_QJSValue::deleteFromDifferentThread() std::unique_ptr<QThread> thread(QThread::create([&]() { QMutexLocker locker(&mutex); QJSValuePrivate::free(&jsval); - QJSValuePrivate::setValue(&jsval, QV4::Encode::undefined()); + QJSValuePrivate::setValue(&jsval, QV4::Value::fromReturnedValue(QV4::Encode::undefined())); QVERIFY(storage.firstPage != nullptr); condition.wakeOne(); })); diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index 6adf3891ea..dc7490c18a 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -355,7 +355,7 @@ void tst_qmldiskcache::regenerateAfterChange() QCOMPARE(quint32(obj->nBindings), quint32(2)); QCOMPARE(obj->bindingTable()->type(), QV4::CompiledData::Binding::Type_Number); - const QV4::Value value(testUnit->constants()[obj->bindingTable()->value.constantValueIndex]); + const auto value = QV4::Value::fromReturnedValue(testUnit->constants()[obj->bindingTable()->value.constantValueIndex]); QCOMPARE(value.doubleValue(), double(42)); QCOMPARE(quint32(testUnit->functionTableSize), quint32(1)); diff --git a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp index 257ee5417e..233beeb288 100644 --- a/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp +++ b/tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp @@ -815,13 +815,14 @@ void tst_qqmlcontext::contextLeak() QVERIFY(ddata); QQmlRefPointer<QQmlContextData> context = ddata->context; QVERIFY(context); - QVERIFY(!context->importedScripts().isNullOrUndefined()); - QCOMPARE(int(context->importedScripts().as<QV4::Object>()->getLength()), 1); + QV4::Scope scope(engine.handle()); + QV4::ScopedValue scopedScripts(scope, context->importedScripts()); + QVERIFY(!scopedScripts->isNullOrUndefined()); + QCOMPARE(int(scopedScripts->as<QV4::Object>()->getLength()), 1); - QV4::Scope scope(ddata->jsWrapper.engine()); QV4::ScopedValue scriptContextWrapper(scope); - scriptContextWrapper = context->importedScripts() - .as<QV4::Object>()->get(uint(0)); + scriptContextWrapper = scopedScripts + ->as<QV4::Object>()->get(uint(0)); scriptContext = scriptContextWrapper->as<QV4::QQmlContextWrapper>()->getContext(); } diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index f2057ff7a0..e4e8c3d4f2 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -4767,7 +4767,7 @@ void tst_qqmlecmascript::singletonTypeResolution() void tst_qqmlecmascript::verifyContextLifetime(const QQmlRefPointer<QQmlContextData> &ctxt) { QQmlRefPointer<QQmlContextData> childCtxt = ctxt->childContexts(); - if (!ctxt->importedScripts().isNullOrUndefined()) { + if (!QV4::Value::fromReturnedValue(ctxt->importedScripts()).isNullOrUndefined()) { QV4::ExecutionEngine *v4 = ctxt->engine()->handle(); QV4::Scope scope(v4); QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts()); diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index b20902b563..53b1892002 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -1624,8 +1624,10 @@ void tst_qqmlengine::stringToColor() const QMetaType metaType(QMetaType::QColor); QVariant color(metaType); + QV4::Scope scope(engine.handle()); + QV4::ScopedValue colorString(scope, engine.handle()->newString(QStringLiteral("#abcdef"))); QVERIFY(QV4::ExecutionEngine::metaTypeFromJS( - engine.handle()->newString(QStringLiteral("#abcdef"))->asReturnedValue(), + colorString, metaType, color.data())); QVERIFY(color.isValid()); QCOMPARE(color.metaType(), metaType); diff --git a/tests/auto/qml/qv4mm/tst_qv4mm.cpp b/tests/auto/qml/qv4mm/tst_qv4mm.cpp index c50b2e027d..b26951c86e 100644 --- a/tests/auto/qml/qv4mm/tst_qv4mm.cpp +++ b/tests/auto/qml/qv4mm/tst_qv4mm.cpp @@ -74,7 +74,8 @@ void tst_qv4mm::arrayDataWriteBarrierInteraction() QV4::ScopedArrayObject array(scope, engine.newArrayObject()); constexpr int initialCapacity = 8; // compare qv4arraydata.cpp for (int i = 0; i < initialCapacity; ++i) { - array->push_back(unprotectedObject->asReturnedValue()); + // fromReturnedValue would generally be unsafe, but the gc is blocked here anyway + array->push_back(QV4::Value::fromReturnedValue(unprotectedObject->asReturnedValue())); } QVERIFY(!unprotectedObject->isMarked()); engine.memoryManager->gcBlocked = QV4::MemoryManager::Unblocked; @@ -521,7 +522,7 @@ void tst_qv4mm::mapAndSetKeepValuesAlive() QV4::Scope scope(&engine); auto map = jsEngine.evaluate("new Map()"); QV4::ScopedFunctionObject afunction(scope, engine.memoryManager->alloc<QV4::FunctionObject>()); // hack, we just need about any function object - QV4::Value thisObject = QJSValuePrivate::asReturnedValue(&map); + QV4::Value thisObject = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&map)); QVERIFY(!engine.memoryManager->gcBlocked); // no scoped classes, as that would defeat the point of the test @@ -565,7 +566,7 @@ void tst_qv4mm::mapAndSetKeepValuesAlive() QV4::Scope scope(&engine); auto map = jsEngine.evaluate("new WeakMap()"); QV4::ScopedFunctionObject afunction(scope, engine.memoryManager->alloc<QV4::FunctionObject>()); // hack, we just need about any function object - QV4::Value thisObject = QJSValuePrivate::asReturnedValue(&map); + QV4::Value thisObject = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&map)); QVERIFY(!engine.memoryManager->gcBlocked); // no scoped classes, as that would defeat the point of the test @@ -602,7 +603,7 @@ void tst_qv4mm::mapAndSetKeepValuesAlive() QV4::Scope scope(&engine); auto map = jsEngine.evaluate("new Set()"); QV4::ScopedFunctionObject afunction(scope, engine.memoryManager->alloc<QV4::FunctionObject>()); // hack, we just need about any function object - QV4::Value thisObject = QJSValuePrivate::asReturnedValue(&map); + QV4::Value thisObject = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&map)); QVERIFY(!engine.memoryManager->gcBlocked); // no scoped classes, as that would defeat the point of the test @@ -637,7 +638,7 @@ void tst_qv4mm::mapAndSetKeepValuesAlive() QV4::Scope scope(&engine); auto map = jsEngine.evaluate("new WeakSet()"); QV4::ScopedFunctionObject afunction(scope, engine.memoryManager->alloc<QV4::FunctionObject>()); // hack, we just need about any function object - QV4::Value thisObject = QJSValuePrivate::asReturnedValue(&map); + QV4::Value thisObject = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&map)); QVERIFY(!engine.memoryManager->gcBlocked); // no scoped classes, as that would defeat the point of the test |