aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabian Kosmale <[email protected]>2024-12-12 14:39:37 +0100
committerFabian Kosmale <[email protected]>2024-12-18 11:55:52 +0100
commita5feec81934ab0b074d6a8c7621b591851f6b544 (patch)
treeca409667609e35a3a5dec64ee2cdd14cf59f634f
parentac2d9bf0f2c32bdd6a64b8421c414a28369cbe2e (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]>
-rw-r--r--src/qml/compat/removed_api.cpp4
-rw-r--r--src/qml/jsapi/qjsengine.cpp6
-rw-r--r--src/qml/jsapi/qjsmanagedvalue.cpp13
-rw-r--r--src/qml/jsapi/qjsvalue.cpp21
-rw-r--r--src/qml/jsapi/qjsvalue_p.h2
-rw-r--r--src/qml/jsruntime/qv4engine.cpp19
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h12
-rw-r--r--src/qml/jsruntime/qv4object.cpp4
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp4
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp5
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp5
-rw-r--r--src/qml/jsruntime/qv4urlobject.cpp31
-rw-r--r--src/qml/jsruntime/qv4value_p.h3
-rw-r--r--src/qml/qml/qqml.cpp24
-rw-r--r--src/qml/qml/qqmlbinding.cpp10
-rw-r--r--src/qml/qml/qqmlcomponent.cpp3
-rw-r--r--src/qml/qml/qqmlcontextdata_p.h14
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp3
-rw-r--r--src/qml/qml/qqmlglobal.cpp11
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp12
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp3
-rw-r--r--src/qml/qml/qqmlproperty.cpp3
-rw-r--r--src/qml/qml/qqmlscriptdata.cpp2
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp6
-rw-r--r--src/qmlmodels/qqmllistmodel.cpp20
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp4
-rw-r--r--tests/auto/qml/qjsvalue/tst_qjsvalue.cpp2
-rw-r--r--tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp2
-rw-r--r--tests/auto/qml/qqmlcontext/tst_qqmlcontext.cpp11
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp2
-rw-r--r--tests/auto/qml/qqmlengine/tst_qqmlengine.cpp4
-rw-r--r--tests/auto/qml/qv4mm/tst_qv4mm.cpp11
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