From 748411fa64412db1650e04ee7b4405b8fbc53d42 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 4 Mar 2020 16:46:42 +0100 Subject: Store a QV4::ReturnedValue in QJSValue Being careful, we can now save primitive values inline. We use the heap pointer of QV4::Value as either QString* or QV4::Value* for complex types. We cannot store persistent managed QV4::Value without the double indirection as those need to be allocated in a special place. The generic QVariant case is not supported anymore. The only place where it was actually needed were the stream operators for QJSValue. Those were fundamentally broken: * A managed QJSValue saved and loaded from a stream was converted to a QVariant-type QJSValue * QVariant-type QJSValues were not callable, could not be objects or arrays, or any of the special types. * Cyclic references were forcibly broken when saving to a data stream. In general the support for saving and loading of managed types to/from a data stream was so abysmally bad that we don't lose much by dropping it. [ChangeLog][QML][Important Behavior Changes] When saving a QJSValue to a QDataStream only primitive values or strings will be retained. Support for objects and arrays was incomplete and unreliable already before. It cannot work correctly as we don't necessarily have a JavaScript heap when loading a QJSValue from a stream. Therefore, we don't have a proper place to keep any managed values. Using QVariant to keep them instead is a bad idea because QVariant cannot represent everything a QJSValue can contain. Fixes: QTBUG-75174 Change-Id: I75697670639bca8d4b1668763d7020c4cf871bda Reviewed-by: Fabian Kosmale --- src/qml/jsapi/qjsengine.cpp | 143 +++------- src/qml/jsapi/qjsengine.h | 10 +- src/qml/jsapi/qjsvalue.cpp | 458 +++++++++++--------------------- src/qml/jsapi/qjsvalue.h | 3 +- src/qml/jsapi/qjsvalue_p.h | 223 ++++++++++------ src/qml/jsapi/qjsvalueiterator.cpp | 6 +- src/qml/jsruntime/qv4engine.cpp | 163 ++++++++---- src/qml/jsruntime/qv4engine_p.h | 2 +- src/qml/jsruntime/qv4persistent.cpp | 4 +- src/qml/jsruntime/qv4persistent_p.h | 2 +- src/qml/jsruntime/qv4qmlcontext.cpp | 2 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 11 +- src/qml/qml/qqmlbinding.cpp | 8 +- src/qml/qml/qqmlboundsignal.cpp | 5 +- src/qml/qml/qqmlengine.cpp | 6 +- src/qml/qml/qqmlengine_p.h | 16 +- src/qml/qml/qqmlobjectcreator.cpp | 2 +- src/qml/qml/qqmltypewrapper.cpp | 4 +- 18 files changed, 473 insertions(+), 595 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index 6946a64d11..65b65f1b21 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -451,10 +451,7 @@ void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSVal } QV4::Scope scope(m_v4Engine); - QV4::ScopedObject obj(scope); - QV4::Value *val = QJSValuePrivate::getValue(&object); - if (val) - obj = val; + QV4::ScopedObject obj(scope, QJSValuePrivate::asReturnedValue(&object)); if (!obj) obj = scope.engine->globalObject; @@ -548,9 +545,7 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in if (v4->isInterrupted.loadAcquire()) result = v4->newErrorObject(QStringLiteral("Interrupted")); - QJSValue retval(v4, result->asReturnedValue()); - - return retval; + return QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); } /*! @@ -578,18 +573,17 @@ QJSValue QJSEngine::importModule(const QString &fileName) const QUrl url = urlForFileName(QFileInfo(fileName).canonicalFilePath()); auto moduleUnit = m_v4Engine->loadModule(url); if (m_v4Engine->hasException) - return QJSValue(m_v4Engine, m_v4Engine->catchException()); + return QJSValuePrivate::fromReturnedValue(m_v4Engine->catchException()); QV4::Scope scope(m_v4Engine); QV4::Scoped moduleNamespace(scope, moduleUnit->instantiate(m_v4Engine)); if (m_v4Engine->hasException) - return QJSValue(m_v4Engine, m_v4Engine->catchException()); + return QJSValuePrivate::fromReturnedValue(m_v4Engine->catchException()); moduleUnit->evaluate(); if (!m_v4Engine->isInterrupted.loadAcquire()) - return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(moduleNamespace->asReturnedValue()); - return QJSValue( - m_v4Engine, + return QJSValuePrivate::fromReturnedValue( m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue()); } @@ -605,7 +599,7 @@ QJSValue QJSEngine::newObject() { QV4::Scope scope(m_v4Engine); QV4::ScopedValue v(scope, m_v4Engine->newObject()); - return QJSValue(m_v4Engine, v->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(v->asReturnedValue()); } /*! @@ -647,7 +641,7 @@ QJSValue QJSEngine::newErrorObject(QJSValue::ErrorType errorType, const QString case QJSValue::NoError: return QJSValue::UndefinedValue; } - return QJSValue(m_v4Engine, error->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(error->asReturnedValue()); } /*! @@ -662,7 +656,7 @@ QJSValue QJSEngine::newArray(uint length) if (length < 0x1000) array->arrayReserve(length); array->setArrayLengthUnchecked(length); - return QJSValue(m_v4Engine, array.asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(array.asReturnedValue()); } /*! @@ -695,7 +689,7 @@ QJSValue QJSEngine::newQObject(QObject *object) QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership); } QV4::ScopedValue v(scope, QV4::QObjectWrapper::wrap(v4, object)); - return QJSValue(v4, v->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(v->asReturnedValue()); } /*! @@ -716,7 +710,7 @@ QJSValue QJSEngine::newQMetaObject(const QMetaObject* metaObject) { QV4::ExecutionEngine *v4 = m_v4Engine; QV4::Scope scope(v4); QV4::ScopedValue v(scope, QV4::QMetaObjectWrapper::create(v4, metaObject)); - return QJSValue(v4, v->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(v->asReturnedValue()); } /*! \fn template QJSValue QJSEngine::newQMetaObject() @@ -743,7 +737,7 @@ QJSValue QJSEngine::globalObject() const { QV4::Scope scope(m_v4Engine); QV4::ScopedValue v(scope, m_v4Engine->globalObject); - return QJSValue(m_v4Engine, v->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(v->asReturnedValue()); } /*! @@ -754,7 +748,7 @@ QJSValue QJSEngine::create(int type, const void *ptr) { QV4::Scope scope(m_v4Engine); QV4::ScopedValue v(scope, scope.engine->metaTypeToJS(type, ptr)); - return QJSValue(m_v4Engine, v->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(v->asReturnedValue()); } /*! @@ -763,118 +757,57 @@ QJSValue QJSEngine::create(int type, const void *ptr) */ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) { - QV4::ExecutionEngine *v4 = QJSValuePrivate::engine(&value); - QV4::Value scratch; - QV4::Value *val = QJSValuePrivate::valueForData(&value, &scratch); - if (v4) { - QV4::Scope scope(v4); - QV4::ScopedValue v(scope, *val); - return scope.engine->metaTypeFromJS(v, type, ptr); - } - - if (!val) { - QVariant *variant = QJSValuePrivate::getVariant(&value); - Q_ASSERT(variant); - - if (variant->userType() == QMetaType::QString) { - QString string = variant->toString(); - // have a string based value without engine. Do conversion manually - if (type == QMetaType::Bool) { - *reinterpret_cast(ptr) = string.length() != 0; - return true; - } - if (type == QMetaType::QString) { - *reinterpret_cast(ptr) = string; - return true; - } - double d = QV4::RuntimeHelpers::stringToNumber(string); - switch (type) { - case QMetaType::Int: - *reinterpret_cast(ptr) = QV4::Value::toInt32(d); - return true; - case QMetaType::UInt: - *reinterpret_cast(ptr) = QV4::Value::toUInt32(d); - return true; - case QMetaType::LongLong: - *reinterpret_cast(ptr) = QV4::Value::toInteger(d); - return true; - case QMetaType::ULongLong: - *reinterpret_cast(ptr) = QV4::Value::toInteger(d); - return true; - case QMetaType::Double: - *reinterpret_cast(ptr) = d; - return true; - case QMetaType::Float: - *reinterpret_cast(ptr) = d; - return true; - case QMetaType::Short: - *reinterpret_cast(ptr) = QV4::Value::toInt32(d); - return true; - case QMetaType::UShort: - *reinterpret_cast(ptr) = QV4::Value::toUInt32(d); - return true; - case QMetaType::Char: - *reinterpret_cast(ptr) = QV4::Value::toInt32(d); - return true; - case QMetaType::UChar: - *reinterpret_cast(ptr) = QV4::Value::toUInt32(d); - return true; - case QMetaType::QChar: - *reinterpret_cast(ptr) = QV4::Value::toUInt32(d); - return true; - default: - return false; - } - } else { - return QMetaType::convert(&variant->data_ptr(), variant->userType(), ptr, type); + if (const QString *string = QJSValuePrivate::asQString(&value)) { + // have a string based value without engine. Do conversion manually + if (type == QMetaType::Bool) { + *reinterpret_cast(ptr) = string->length() != 0; + return true; } - } - - Q_ASSERT(val); - - switch (type) { - case QMetaType::Bool: - *reinterpret_cast(ptr) = val->toBoolean(); + if (type == QMetaType::QString) { + *reinterpret_cast(ptr) = *string; return true; + } + double d = QV4::RuntimeHelpers::stringToNumber(*string); + switch (type) { case QMetaType::Int: - *reinterpret_cast(ptr) = val->toInt32(); + *reinterpret_cast(ptr) = QV4::Value::toInt32(d); return true; case QMetaType::UInt: - *reinterpret_cast(ptr) = val->toUInt32(); + *reinterpret_cast(ptr) = QV4::Value::toUInt32(d); return true; case QMetaType::LongLong: - *reinterpret_cast(ptr) = val->toInteger(); + *reinterpret_cast(ptr) = QV4::Value::toInteger(d); return true; case QMetaType::ULongLong: - *reinterpret_cast(ptr) = val->toInteger(); + *reinterpret_cast(ptr) = QV4::Value::toInteger(d); return true; case QMetaType::Double: - *reinterpret_cast(ptr) = val->toNumber(); - return true; - case QMetaType::QString: - *reinterpret_cast(ptr) = val->toQStringNoThrow(); + *reinterpret_cast(ptr) = d; return true; case QMetaType::Float: - *reinterpret_cast(ptr) = val->toNumber(); + *reinterpret_cast(ptr) = d; return true; case QMetaType::Short: - *reinterpret_cast(ptr) = val->toInt32(); + *reinterpret_cast(ptr) = QV4::Value::toInt32(d); return true; case QMetaType::UShort: - *reinterpret_cast(ptr) = val->toUInt16(); + *reinterpret_cast(ptr) = QV4::Value::toUInt32(d); return true; case QMetaType::Char: - *reinterpret_cast(ptr) = val->toInt32(); + *reinterpret_cast(ptr) = QV4::Value::toInt32(d); return true; case QMetaType::UChar: - *reinterpret_cast(ptr) = val->toUInt16(); + *reinterpret_cast(ptr) = QV4::Value::toUInt32(d); return true; case QMetaType::QChar: - *reinterpret_cast(ptr) = val->toUInt16(); + *reinterpret_cast(ptr) = QV4::Value::toUInt32(d); return true; default: return false; + } } + + return QV4::ExecutionEngine::metaTypeFromJS(QJSValuePrivate::asReturnedValue(&value), type, ptr); } /*! \fn template QJSValue QJSEngine::toScriptValue(const T &value) @@ -991,7 +924,7 @@ void QJSEngine::throwError(QJSValue::ErrorType errorType, const QString &message { QV4::Scope scope(m_v4Engine); QJSValue error = newErrorObject(errorType, message); - QV4::ScopedObject e(scope, QJSValuePrivate::getValue(&error)); + QV4::ScopedObject e(scope, QJSValuePrivate::asReturnedValue(&error)); if (!e) return; m_v4Engine->throwError(e); diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 31229e1f20..16919454e7 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -133,7 +133,8 @@ private: static bool convertV2(const QJSValue &value, int type, void *ptr); - friend inline bool qjsvalue_cast_helper(const QJSValue &, int, void *); + template + friend inline T qjsvalue_cast(const QJSValue &); protected: QJSEngine(QJSEnginePrivate &dd, QObject *parent = nullptr); @@ -146,18 +147,13 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(QJSEngine::Extensions) -inline bool qjsvalue_cast_helper(const QJSValue &value, int type, void *ptr) -{ - return QJSEngine::convertV2(value, type, ptr); -} - template T qjsvalue_cast(const QJSValue &value) { T t; const int id = qMetaTypeId(); - if (qjsvalue_cast_helper(value, id, &t)) + if (QJSEngine::convertV2(value, id, &t)) return t; else if (value.isVariant()) return qvariant_cast(value.toVariant()); diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index 6e632b56b2..1122700334 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -191,41 +191,29 @@ using namespace QV4; /*! Constructs a new QJSValue with a boolean \a value. */ -QJSValue::QJSValue(bool value) +QJSValue::QJSValue(bool value) : d(QV4::Encode(value)) { - QJSValuePrivate::setVariant(this, QVariant(value)); -} - -/*! - \internal -*/ -QJSValue::QJSValue(ExecutionEngine *e, quint64 val) -{ - QJSValuePrivate::setValue(this, e, val); } /*! Constructs a new QJSValue with a number \a value. */ -QJSValue::QJSValue(int value) +QJSValue::QJSValue(int value) : d(QV4::Encode(value)) { - QJSValuePrivate::setVariant(this, QVariant(value)); } /*! Constructs a new QJSValue with a number \a value. */ -QJSValue::QJSValue(uint value) +QJSValue::QJSValue(uint value) : d(QV4::Encode(value)) { - QJSValuePrivate::setVariant(this, QVariant((double)value)); } /*! Constructs a new QJSValue with a number \a value. */ -QJSValue::QJSValue(double value) +QJSValue::QJSValue(double value) : d(QV4::Encode(value)) { - QJSValuePrivate::setVariant(this, QVariant(value)); } /*! @@ -233,17 +221,14 @@ QJSValue::QJSValue(double value) */ QJSValue::QJSValue(const QString& value) { - QJSValuePrivate::setVariant(this, QVariant(value)); + QJSValuePrivate::setString(this, QString(value)); } /*! Constructs a new QJSValue with a special \a value. */ -QJSValue::QJSValue(SpecialValue value) - : d(0) +QJSValue::QJSValue(SpecialValue value) : d(value == NullValue ? QV4::Encode::null() : 0) { - if (value == NullValue) - QJSValuePrivate::setVariant(this, QVariant::fromValue(nullptr)); } /*! @@ -251,7 +236,7 @@ QJSValue::QJSValue(SpecialValue value) */ QJSValue::QJSValue(const QLatin1String &value) { - QJSValuePrivate::setVariant(this, QVariant(value)); + QJSValuePrivate::setString(this, QString(value)); } /*! @@ -260,7 +245,7 @@ QJSValue::QJSValue(const QLatin1String &value) #ifndef QT_NO_CAST_FROM_ASCII QJSValue::QJSValue(const char *value) { - QJSValuePrivate::setVariant(this, QVariant(QString::fromUtf8(value))); + QJSValuePrivate::setString(this, QString(QString::fromUtf8(value))); } #endif @@ -271,15 +256,12 @@ QJSValue::QJSValue(const char *value) true), then only a reference to the underlying object is copied into the new script value (i.e., the object itself is not copied). */ -QJSValue::QJSValue(const QJSValue& other) - : d(0) +QJSValue::QJSValue(const QJSValue &other) : d(0) { - QV4::Value *v = QJSValuePrivate::getValue(&other); - if (v) { - QJSValuePrivate::setValue(this, QJSValuePrivate::engine(&other), *v); - } else if (QVariant *v = QJSValuePrivate::getVariant(&other)) { - QJSValuePrivate::setVariant(this, *v); - } + if (const QString *string = QJSValuePrivate::asQString(&other)) + QJSValuePrivate::setString(this, *string); + else + QJSValuePrivate::setValue(this, QJSValuePrivate::asReturnedValue(&other)); } /*! @@ -310,11 +292,7 @@ QJSValue::~QJSValue() */ bool QJSValue::isBool() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (val) - return val->isBoolean(); - QVariant *variant = QJSValuePrivate::getVariant(this); - return variant && variant->userType() == QMetaType::Bool; + return QV4::Value::fromReturnedValue(d).isBoolean(); } /*! @@ -325,27 +303,7 @@ bool QJSValue::isBool() const */ bool QJSValue::isNumber() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (val) - return val->isNumber(); - QVariant *variant = QJSValuePrivate::getVariant(this); - if (!variant) - return false; - - switch (variant->userType()) { - case QMetaType::Double: - case QMetaType::Int: - case QMetaType::UInt: - case QMetaType::Long: - case QMetaType::ULong: - case QMetaType::Short: - case QMetaType::UShort: - case QMetaType::LongLong: - case QMetaType::ULongLong: - return true; - default: - return false; - } + return QV4::Value::fromReturnedValue(d).isNumber(); } /*! @@ -354,14 +312,7 @@ bool QJSValue::isNumber() const */ bool QJSValue::isNull() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (val) - return val->isNull(); - QVariant *variant = QJSValuePrivate::getVariant(this); - if (!variant) - return false; - const int type = variant->userType(); - return type == QMetaType::Nullptr || type == QMetaType::VoidStar; + return QV4::Value::fromReturnedValue(d).isNull(); } /*! @@ -372,24 +323,27 @@ bool QJSValue::isNull() const */ bool QJSValue::isString() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (val) - return val->isString(); - QVariant *variant = QJSValuePrivate::getVariant(this); - return variant && variant->userType() == QMetaType::QString; + if (QJSValuePrivate::asQString(this)) + return true; + + // String is managed + return QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)).isString(); } /*! - Returns true if this QJSValue is of the primitive type Undefined; - otherwise returns false. + Returns true if this QJSValue is of the primitive type Undefined or if the managed value + has been cleared (by deleting the engine). Otherwise returns false. */ bool QJSValue::isUndefined() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (val) - return val->isUndefined(); - QVariant *variant = QJSValuePrivate::getVariant(this); - return !variant || variant->userType() == QMetaType::UnknownType || variant->userType() == QMetaType::Void; + if (QJSValuePrivate::asQString(this)) + return false; + QV4::Value v = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)); + if (v.isUndefined()) + return true; + if (!v.isManaged()) + return false; + return v.managed() == nullptr; } /*! @@ -400,10 +354,7 @@ bool QJSValue::isUndefined() const */ bool QJSValue::isError() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (!val) - return false; - return val->as(); + return QJSValuePrivate::asManagedType(this); } /*! @@ -415,10 +366,7 @@ bool QJSValue::isError() const */ QJSValue::ErrorType QJSValue::errorType() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (!val) - return NoError; - QV4::ErrorObject *error = val->as(); + const QV4::ErrorObject *error = QJSValuePrivate::asManagedType(this); if (!error) return NoError; switch (error->d()->errorType) { @@ -449,10 +397,7 @@ QJSValue::ErrorType QJSValue::errorType() const */ bool QJSValue::isArray() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (!val) - return false; - return val->as(); + return QJSValuePrivate::asManagedType(this); } /*! @@ -466,10 +411,7 @@ bool QJSValue::isArray() const */ bool QJSValue::isObject() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (!val) - return false; - return val->as(); + return QJSValuePrivate::asManagedType(this); } /*! @@ -480,10 +422,7 @@ bool QJSValue::isObject() const */ bool QJSValue::isCallable() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (!val) - return false; - return val->as(); + return QJSValuePrivate::asManagedType(this); } /*! @@ -494,10 +433,7 @@ bool QJSValue::isCallable() const */ bool QJSValue::isVariant() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (!val) - return false; - return val->as(); + return QJSValuePrivate::asManagedType(this); } /*! @@ -514,27 +450,22 @@ bool QJSValue::isVariant() const */ QString QJSValue::toString() const { - QV4::Value scratch; - QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch); - - if (!val) { - QVariant *variant = QJSValuePrivate::getVariant(this); - Q_ASSERT(variant); - if (variant->userType() == QMetaType::QVariantMap) - return QStringLiteral("[object Object]"); - else if (variant->userType() == QMetaType::QVariantList) { - const QVariantList list = variant->toList(); - QString result; - for (int i = 0; i < list.count(); ++i) { - if (i > 0) - result.append(QLatin1Char(',')); - result.append(list.at(i).toString()); - } - return result; - } - return variant->toString(); + if (const QString *string = QJSValuePrivate::asQString(this)) + return *string; + + return QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)).toQStringNoThrow(); +} + +template +T caughtResult(const QJSValue *v, T (QV4::Value::*convert)() const) +{ + const T result = (QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(v)).*convert)(); + QV4::ExecutionEngine *engine = QJSValuePrivate::engine(v); + if (engine && engine->hasException) { + engine->catchException(); + return T(); } - return val->toQStringNoThrow(); + return result; } /*! @@ -551,28 +482,10 @@ QString QJSValue::toString() const */ double QJSValue::toNumber() const { - QV4::Value scratch; - QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch); - - if (!val) { - QVariant *variant = QJSValuePrivate::getVariant(this); - Q_ASSERT(variant); - - if (variant->userType() == QMetaType::QString) - return RuntimeHelpers::stringToNumber(variant->toString()); - else if (variant->canConvert()) - return variant->value(); - else - return std::numeric_limits::quiet_NaN(); - } + if (const QString *string = QJSValuePrivate::asQString(this)) + return RuntimeHelpers::stringToNumber(*string); - double dbl = val->toNumber(); - QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this); - if (engine && engine->hasException) { - engine->catchException(); - return 0; - } - return dbl; + return caughtResult(this, &QV4::Value::toNumber); } /*! @@ -589,24 +502,10 @@ double QJSValue::toNumber() const */ bool QJSValue::toBool() const { - QV4::Value scratch; - QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch); - - if (!val) { - QVariant *variant = QJSValuePrivate::getVariant(this); - if (variant->userType() == QMetaType::QString) - return variant->toString().length() > 0; - else - return variant->toBool(); - } + if (const QString *string = QJSValuePrivate::asQString(this)) + return string->length() > 0; - bool b = val->toBoolean(); - QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this); - if (engine && engine->hasException) { - engine->catchException(); - return false; - } - return b; + return caughtResult(this, &QV4::Value::toBoolean); } /*! @@ -623,24 +522,10 @@ bool QJSValue::toBool() const */ qint32 QJSValue::toInt() const { - QV4::Value scratch; - QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch); - - if (!val) { - QVariant *variant = QJSValuePrivate::getVariant(this); - if (variant->userType() == QMetaType::QString) - return QV4::Value::toInt32(RuntimeHelpers::stringToNumber(variant->toString())); - else - return variant->toInt(); - } + if (const QString *string = QJSValuePrivate::asQString(this)) + return QV4::Value::toInt32(RuntimeHelpers::stringToNumber(*string)); - qint32 i = val->toInt32(); - QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this); - if (engine && engine->hasException) { - engine->catchException(); - return 0; - } - return i; + return caughtResult(this, &QV4::Value::toInt32); } /*! @@ -657,24 +542,10 @@ qint32 QJSValue::toInt() const */ quint32 QJSValue::toUInt() const { - QV4::Value scratch; - QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch); - - if (!val) { - QVariant *variant = QJSValuePrivate::getVariant(this); - if (variant->userType() == QMetaType::QString) - return QV4::Value::toUInt32(RuntimeHelpers::stringToNumber(variant->toString())); - else - return variant->toUInt(); - } + if (const QString *string = QJSValuePrivate::asQString(this)) + return QV4::Value::toUInt32(RuntimeHelpers::stringToNumber(*string)); - quint32 u = val->toUInt32(); - QV4::ExecutionEngine *engine = QJSValuePrivate::engine(this); - if (engine && engine->hasException) { - engine->catchException(); - return 0; - } - return u; + return caughtResult(this, &QV4::Value::toUInt32); } /*! @@ -701,29 +572,29 @@ quint32 QJSValue::toUInt() const */ QVariant QJSValue::toVariant() const { - QVariant *variant = QJSValuePrivate::getVariant(this); - if (variant) - return *variant; - - QV4::Value scratch; - QV4::Value *val = QJSValuePrivate::valueForData(this, &scratch); - Q_ASSERT(val); - - if (QV4::Object *o = val->as()) - return o->engine()->toVariant(*val, /*typeHint*/ -1, /*createJSValueForObjects*/ false); - - if (String *s = val->stringValue()) - return QVariant(s->toQString()); - if (val->isBoolean()) - return QVariant(val->booleanValue()); - if (val->isNumber()) { - if (val->isInt32()) - return QVariant(val->integerValue()); - return QVariant(val->asDouble()); - } - if (val->isNull()) + if (const QString *string = QJSValuePrivate::asQString(this)) + return QVariant(*string); + + QV4::Value val = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(this)); + if (val.isUndefined()) + return QVariant(); + if (val.isNull()) return QVariant(QMetaType::Nullptr, nullptr); - Q_ASSERT(val->isUndefined()); + if (val.isBoolean()) + return QVariant(val.booleanValue()); + if (val.isInt32()) // Includes doubles that can be losslessly casted to int + return QVariant(val.integerValue()); + if (val.isNumber()) + return QVariant(val.doubleValue()); + + Q_ASSERT(val.isManaged()); + + if (val.isString()) + return QVariant(val.toQString()); + if (QV4::Managed *m = val.as()) + return m->engine()->toVariant(val, /*typeHint*/ -1, /*createJSValueForObjects*/ false); + + Q_ASSERT(false); return QVariant(); } @@ -744,11 +615,7 @@ QVariant QJSValue::toVariant() const */ QJSValue QJSValue::call(const QJSValueList &args) const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (!val) - return QJSValue(); - - FunctionObject *f = val->as(); + const FunctionObject *f = QJSValuePrivate::asManagedType(this); if (!f) return QJSValue(); @@ -763,7 +630,7 @@ QJSValue QJSValue::call(const QJSValueList &args) const qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine"); return QJSValue(); } - jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); + jsCallData->args[i] = QJSValuePrivate::convertToReturnedValue(engine, args.at(i)); } ScopedValue result(scope, f->call(jsCallData)); @@ -772,7 +639,7 @@ QJSValue QJSValue::call(const QJSValueList &args) const if (engine->isInterrupted.loadAcquire()) result = engine->newErrorObject(QStringLiteral("Interrupted")); - return QJSValue(engine, result->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); } /*! @@ -797,11 +664,7 @@ QJSValue QJSValue::call(const QJSValueList &args) const */ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList &args) const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (!val) - return QJSValue(); - - FunctionObject *f = val->as(); + const FunctionObject *f = QJSValuePrivate::asManagedType(this); if (!f) return QJSValue(); @@ -815,13 +678,13 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList } JSCallData jsCallData(scope, args.size()); - *jsCallData->thisObject = QJSValuePrivate::convertedToValue(engine, instance); + *jsCallData->thisObject = QJSValuePrivate::convertToReturnedValue(engine, instance); for (int i = 0; i < args.size(); ++i) { if (!QJSValuePrivate::checkEngine(engine, args.at(i))) { qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine"); return QJSValue(); } - jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); + jsCallData->args[i] = QJSValuePrivate::convertToReturnedValue(engine, args.at(i)); } ScopedValue result(scope, f->call(jsCallData)); @@ -830,7 +693,7 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList if (engine->isInterrupted.loadAcquire()) result = engine->newErrorObject(QStringLiteral("Interrupted")); - return QJSValue(engine, result->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); } /*! @@ -853,11 +716,7 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList */ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (!val) - return QJSValue(); - - FunctionObject *f = val->as(); + const FunctionObject *f = QJSValuePrivate::asManagedType(this); if (!f) return QJSValue(); @@ -871,7 +730,7 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) const qWarning("QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine"); return QJSValue(); } - jsCallData->args[i] = QJSValuePrivate::convertedToValue(engine, args.at(i)); + jsCallData->args[i] = QJSValuePrivate::convertToReturnedValue(engine, args.at(i)); } ScopedValue result(scope, f->callAsConstructor(jsCallData)); @@ -880,7 +739,7 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) const if (engine->isInterrupted.loadAcquire()) result = engine->newErrorObject(QStringLiteral("Interrupted")); - return QJSValue(engine, result->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); } #ifdef QT_DEPRECATED @@ -915,13 +774,13 @@ QJSValue QJSValue::prototype() const if (!engine) return QJSValue(); QV4::Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)->as()); + ScopedObject o(scope, QJSValuePrivate::asManagedType(this)); if (!o) return QJSValue(); ScopedObject p(scope, o->getPrototypeOf()); if (!p) return QJSValue(NullValue); - return QJSValue(o->internalClass()->engine, p.asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(p.asReturnedValue()); } /*! @@ -942,14 +801,11 @@ void QJSValue::setPrototype(const QJSValue& prototype) if (!engine) return; Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)); + ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this)); if (!o) return; - QV4::Value scratch; - QV4::Value *val = QJSValuePrivate::valueForData(&prototype, &scratch); - if (!val) - return; - if (val->isNull()) { + QV4::Value val = QV4::Value::fromReturnedValue(QJSValuePrivate::asReturnedValue(&prototype)); + if (val.isNull()) { o->setPrototypeOf(nullptr); return; } @@ -980,12 +836,11 @@ QJSValue& QJSValue::operator=(const QJSValue& other) QJSValuePrivate::free(this); d = 0; - QV4::Value *v = QJSValuePrivate::getValue(&other); - if (v) { - QJSValuePrivate::setValue(this, QJSValuePrivate::engine(&other), *v); - } else if (QVariant *v = QJSValuePrivate::getVariant(&other)) { - QJSValuePrivate::setVariant(this, *v); - } + if (const QString *string = QJSValuePrivate::asQString(&other)) + QJSValuePrivate::setString(this, *string); + else + QJSValuePrivate::setValue(this, QJSValuePrivate::asReturnedValue(&other)); + return *this; } @@ -1031,23 +886,17 @@ static bool js_equal(const QString &string, const QV4::Value &value) */ bool QJSValue::equals(const QJSValue& other) const { - QV4::Value s1, s2; - QV4::Value *v = QJSValuePrivate::valueForData(this, &s1); - QV4::Value *ov = QJSValuePrivate::valueForData(&other, &s2); - - if (!v) { - QVariant *variant = QJSValuePrivate::getVariant(this); - Q_ASSERT(variant); - if (!ov) - return *variant == *QJSValuePrivate::getVariant(&other); - if (variant->userType() == QMetaType::QVariantMap || variant->userType() == QMetaType::QVariantList) - return false; - return js_equal(variant->toString(), *ov); - } - if (!ov) - return other.equals(*this); + if (const QString *string = QJSValuePrivate::asQString(this)) { + if (const QString *otherString = QJSValuePrivate::asQString(&other)) + return *string == *otherString; + return js_equal(*string, QJSValuePrivate::asReturnedValue(&other)); + } + + if (const QString *otherString = QJSValuePrivate::asQString(&other)) + return js_equal(*otherString, QJSValuePrivate::asReturnedValue(this)); - return Runtime::CompareEqual::call(*v, *ov); + return Runtime::CompareEqual::call(QJSValuePrivate::asReturnedValue(this), + QJSValuePrivate::asReturnedValue(&other)); } /*! @@ -1074,25 +923,22 @@ bool QJSValue::equals(const QJSValue& other) const */ bool QJSValue::strictlyEquals(const QJSValue& other) const { - QV4::Value s1, s2; - QV4::Value *v = QJSValuePrivate::valueForData(this, &s1); - QV4::Value *ov = QJSValuePrivate::valueForData(&other, &s2); - - if (!v) { - QVariant *variant = QJSValuePrivate::getVariant(this); - Q_ASSERT(variant); - if (!ov) - return *variant == *QJSValuePrivate::getVariant(&other); - if (variant->userType() == QMetaType::QVariantMap || variant->userType() == QMetaType::QVariantList) - return false; - if (String *s = ov->stringValue()) - return variant->toString() == s->toQString(); + if (const QString *string = QJSValuePrivate::asQString(this)) { + if (const QString *otherString = QJSValuePrivate::asQString(&other)) + return *string == *otherString; + if (const String *s = QJSValuePrivate::asManagedType(&other)) + return *string == s->toQString(); return false; } - if (!ov) - return other.strictlyEquals(*this); - return RuntimeHelpers::strictEqual(*v, *ov); + if (const QString *otherString = QJSValuePrivate::asQString(&other)) { + if (const String *s = QJSValuePrivate::asManagedType(this)) + return *otherString == s->toQString(); + return false; + } + + return RuntimeHelpers::strictEqual(QJSValuePrivate::asReturnedValue(this), + QJSValuePrivate::asReturnedValue(&other)); } /*! @@ -1119,7 +965,7 @@ QJSValue QJSValue::property(const QString& name) const return QJSValue(); QV4::Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)); + ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this)); if (!o) return QJSValue(); @@ -1128,7 +974,7 @@ QJSValue QJSValue::property(const QString& name) const if (engine->hasException) result = engine->catchException(); - return QJSValue(engine, result->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); } /*! @@ -1167,14 +1013,14 @@ QJSValue QJSValue::property(quint32 arrayIndex) const return QJSValue(); QV4::Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)); + ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this)); if (!o) return QJSValue(); QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->get(arrayIndex)); if (engine->hasException) engine->catchException(); - return QJSValue(engine, result->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(result->asReturnedValue()); } /*! @@ -1199,7 +1045,7 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value) return; Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)); + ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this)); if (!o) return; @@ -1209,7 +1055,7 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value) } ScopedString s(scope, engine->newString(name)); - QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value)); + QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(engine, value)); o->put(s->toPropertyKey(), v); if (engine->hasException) engine->catchException(); @@ -1253,7 +1099,7 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) return; Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)); + ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this)); if (!o) return; @@ -1262,7 +1108,7 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) return; } - QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value)); + QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(engine, value)); PropertyKey id = arrayIndex != UINT_MAX ? PropertyKey::fromArrayIndex(arrayIndex) : engine->id_uintMax()->propertyKey(); o->put(id, v); if (engine->hasException) @@ -1296,7 +1142,7 @@ bool QJSValue::deleteProperty(const QString &name) return false; Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)); + ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this)); if (!o) return false; @@ -1317,7 +1163,7 @@ bool QJSValue::hasProperty(const QString &name) const return false; Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)); + ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this)); if (!o) return false; @@ -1338,7 +1184,7 @@ bool QJSValue::hasOwnProperty(const QString &name) const return false; Scope scope(engine); - ScopedObject o(scope, QJSValuePrivate::getValue(this)); + ScopedObject o(scope, QJSValuePrivate::asReturnedValue(this)); if (!o) return false; @@ -1362,7 +1208,7 @@ QObject *QJSValue::toQObject() const if (!engine) return nullptr; QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, QJSValuePrivate::getValue(this)); + QV4::Scoped wrapper(scope, QJSValuePrivate::asReturnedValue(this)); if (!wrapper) return nullptr; @@ -1383,7 +1229,7 @@ const QMetaObject *QJSValue::toQMetaObject() const if (!engine) return nullptr; QV4::Scope scope(engine); - QV4::Scoped wrapper(scope, QJSValuePrivate::getValue(this)); + QV4::Scoped wrapper(scope, QJSValuePrivate::asReturnedValue(this)); if (!wrapper) return nullptr; @@ -1400,12 +1246,8 @@ const QMetaObject *QJSValue::toQMetaObject() const */ QDateTime QJSValue::toDateTime() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - if (val) { - QV4::DateObject *date = val->as(); - if (date) - return date->toQDateTime(); - } + if (const QV4::DateObject *date = QJSValuePrivate::asManagedType(this)) + return date->toQDateTime(); return QDateTime(); } @@ -1415,8 +1257,7 @@ QDateTime QJSValue::toDateTime() const */ bool QJSValue::isDate() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - return val && val->as(); + return QJSValuePrivate::asManagedType(this); } /*! @@ -1425,8 +1266,7 @@ bool QJSValue::isDate() const */ bool QJSValue::isRegExp() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - return val && val->as(); + return QJSValuePrivate::asManagedType(this); } /*! @@ -1440,8 +1280,7 @@ bool QJSValue::isRegExp() const */ bool QJSValue::isQObject() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - return val && val->as() != nullptr; + return QJSValuePrivate::asManagedType(this); } /*! @@ -1454,8 +1293,7 @@ bool QJSValue::isQObject() const */ bool QJSValue::isQMetaObject() const { - QV4::Value *val = QJSValuePrivate::getValue(this); - return val && val->as() != nullptr; + return QJSValuePrivate::asManagedType(this); } QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h index b491080e56..091f353291 100644 --- a/src/qml/jsapi/qjsvalue.h +++ b/src/qml/jsapi/qjsvalue.h @@ -153,13 +153,12 @@ public: QT_DEPRECATED QJSEngine *engine() const; #endif - QJSValue(QV4::ExecutionEngine *e, quint64 val); private: friend class QJSValuePrivate; // force compile error, prevent QJSValue(bool) to be called QJSValue(void *) Q_DECL_EQ_DELETE; - mutable quintptr d; + quint64 d; }; QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h index 2faffffbae..5533682144 100644 --- a/src/qml/jsapi/qjsvalue_p.h +++ b/src/qml/jsapi/qjsvalue_p.h @@ -64,132 +64,181 @@ QT_BEGIN_NAMESPACE +// QJSValue::d is a QV4::ReturnedValue, but we don't want to expose that in the public header. +// We use the lower bits of the managed pointer to hide a QString* or a QV4::Value* in there. +Q_STATIC_ASSERT(sizeof(QV4::ReturnedValue) == sizeof(quint64)); +Q_STATIC_ASSERT(alignof(QV4::Value) >= 4); +Q_STATIC_ASSERT(alignof(QString) >= 4); + +enum PointerMask: quintptr { + IsV4Value = 0x0, + IsString = 0x1 +}; + class Q_AUTOTEST_EXPORT QJSValuePrivate { + static const QV4::Value *managedValue(const QV4::Value &v) + { + const quintptr m = quintptr(v.m()); + return (m & IsString) ? nullptr : reinterpret_cast(m); + } + + static const QString *qstring(const QV4::Value &v) + { + const quintptr m = quintptr(v.m()); + return (m & IsString) ? reinterpret_cast(m & ~IsString) : nullptr; + } + + static QV4::ReturnedValue encode(const QString &string) + { + const quintptr m = quintptr(new QString(string)) | IsString; + return encodeRawValue(m); + } + + static QV4::ReturnedValue encode(const QV4::Value &managedValue) + { + QV4::Value *m = managedValue.as()->engine() + ->memoryManager->m_persistentValues->allocate(); + *m = managedValue; + return encodeRawValue(quintptr(m)); + } + + static QV4::ReturnedValue encodeRawValue(quintptr m) + { + return QV4::Value::fromHeapObject(reinterpret_cast(m)).asReturnedValue(); + } + +protected: + // Only for the test. You're not supposed to subclass QJSValuePrivate otherwise. + static void setRawValue(QJSValue *jsval, QV4::Value *m) + { + jsval->d = encodeRawValue(quintptr(m)); + } + public: - static inline QV4::Value *getValue(const QJSValue *jsval) + static QJSValue fromReturnedValue(QV4::ReturnedValue d) { - if (jsval->d & 3) - return nullptr; - return reinterpret_cast(jsval->d); + QJSValue result; + setValue(&result, d); + return result; } - static inline QVariant *getVariant(const QJSValue *jsval) + template + static const T *asManagedType(const QJSValue *jsval) { - if (jsval->d & 1) - return reinterpret_cast(jsval->d & ~3); + const QV4::Value v = QV4::Value::fromReturnedValue(jsval->d); + if (!v.isManaged()) + return nullptr; + if (const QV4::Value *value = managedValue(v)) + return value->as(); return nullptr; } - static inline void setRawValue(QJSValue *jsval, QV4::Value *v) + static QV4::ReturnedValue asPrimitiveType(const QJSValue *jsval) { - jsval->d = reinterpret_cast(v); + const QV4::Value v = QV4::Value::fromReturnedValue(jsval->d); + return v.isManaged() ? QV4::Encode::undefined() : v.asReturnedValue(); } - static inline void setVariant(QJSValue *jsval, const QVariant &v) { - QVariant *val = new QVariant(v); - jsval->d = reinterpret_cast(val) | 1; + // Beware: This only returns a non-null string if the QJSValue actually holds one. + // QV4::Strings are kept as managed values. Retrieve those with getValue(). + static const QString *asQString(const QJSValue *jsval) + { + const QV4::Value v = QV4::Value::fromReturnedValue(jsval->d); + return v.isManaged() ? qstring(v) : nullptr; } - static inline void setValue(QJSValue *jsval, QV4::ExecutionEngine *engine, const QV4::Value &v) { - QV4::Value *value = engine->memoryManager->m_persistentValues->allocate(); - *value = v; - jsval->d = reinterpret_cast(value); + static QV4::ReturnedValue asReturnedValue(const QJSValue *jsval) + { + const QV4::Value v = QV4::Value::fromReturnedValue(jsval->d); + if (!v.isManaged()) + return v.asReturnedValue(); + + if (const QV4::Value *value = managedValue(v)) + return value->asReturnedValue(); + + return QV4::Encode::undefined(); } - static inline void setValue(QJSValue *jsval, QV4::ExecutionEngine *engine, QV4::ReturnedValue v) { - QV4::Value *value = engine->memoryManager->m_persistentValues->allocate(); - *value = v; - jsval->d = reinterpret_cast(value); + static void setString(QJSValue *jsval, const QString &s) + { + jsval->d = encode(s); } - static QV4::ReturnedValue convertedToValue(QV4::ExecutionEngine *e, const QJSValue &jsval) + static void setValue(QJSValue *jsval, const QV4::Value &v) { - QV4::Value *v = getValue(&jsval); - if (!v) { - QVariant *variant = getVariant(&jsval); - v = e->memoryManager->m_persistentValues->allocate(); - *v = variant ? e->fromVariant(*variant) : QV4::Encode::undefined(); - jsval.d = reinterpret_cast(v); - delete variant; + jsval->d = v.isManaged() ? encode(v) : v.asReturnedValue(); + } + + // Moves any QString onto the V4 heap, changing the value to reflect that. + static void manageStringOnV4Heap(QV4::ExecutionEngine *e, QJSValue *jsval) + { + if (const QString *string = asQString(jsval)) { + jsval->d = encode(e->newString(*string)->asReturnedValue()); + delete string; } + } + + // Converts any QString on the fly, involving an allocation. + // Does not change the value. + static QV4::ReturnedValue convertToReturnedValue(QV4::ExecutionEngine *e, + const QJSValue &jsval) + { + if (const QString *string = asQString(&jsval)) + return e->newString(*string)->asReturnedValue(); + if (const QV4::Value *val = asManagedType(&jsval)) { + if (QV4::PersistentValueStorage::getEngine(val) == e) + return val->asReturnedValue(); - if (QV4::PersistentValueStorage::getEngine(v) != e) { qWarning("JSValue can't be reassigned to another engine."); return QV4::Encode::undefined(); } - - return v->asReturnedValue(); + return jsval.d; } - static QV4::Value *valueForData(const QJSValue *jsval, QV4::Value *scratch) + static QV4::ExecutionEngine *engine(const QJSValue *jsval) { - QV4::Value *v = getValue(jsval); - if (v) - return v; - v = scratch; - QVariant *variant = getVariant(jsval); - if (!variant) { - *v = QV4::Encode::undefined(); - return v; - } - - switch (variant->userType()) { - case QMetaType::UnknownType: - case QMetaType::Void: - *v = QV4::Encode::undefined(); - break; - case QMetaType::Nullptr: - case QMetaType::VoidStar: - *v = QV4::Encode::null(); - break; - case QMetaType::Bool: - *v = QV4::Encode(variant->toBool()); - break; - case QMetaType::Double: - *v = QV4::Encode(variant->toDouble()); - break; - case QMetaType::Int: - case QMetaType::Short: - case QMetaType::UShort: - case QMetaType::Char: - case QMetaType::UChar: - *v = QV4::Encode(variant->toInt()); - break; - case QMetaType::UInt: - *v = QV4::Encode(variant->toUInt()); - break; - default: + const QV4::Value v = QV4::Value::fromReturnedValue(jsval->d); + if (!v.isManaged()) return nullptr; - } - return v; - } - static QV4::ExecutionEngine *engine(const QJSValue *jsval) { - QV4::Value *v = getValue(jsval); - return v ? QV4::PersistentValueStorage::getEngine(v) : nullptr; + if (const QV4::Value *m = managedValue(v)) + return QV4::PersistentValueStorage::getEngine(m); + + return nullptr; } - static inline bool checkEngine(QV4::ExecutionEngine *e, const QJSValue &jsval) { + static bool checkEngine(QV4::ExecutionEngine *e, const QJSValue &jsval) + { QV4::ExecutionEngine *v4 = engine(&jsval); return !v4 || v4 == e; } - static inline void free(QJSValue *jsval) { - if (QV4::Value *v = QJSValuePrivate::getValue(jsval)) { - if (QV4::ExecutionEngine *e = engine(jsval)) { - if (QJSEngine *jsEngine = e->jsEngine()) { - if (jsEngine->thread() != QThread::currentThread()) { - QMetaObject::invokeMethod( - jsEngine, [v](){ QV4::PersistentValueStorage::free(v); }); - return; - } + static void free(QJSValue *jsval) + { + QV4::Value v = QV4::Value::fromReturnedValue(jsval->d); + if (!v.isManaged()) + return; + + if (const QString *m = qstring(v)) { + delete m; + return; + } + + // We need a mutable value for free(). It needs to write to the actual memory. + Q_ASSERT(!(quintptr(v.m()) & IsString)); + QV4::Value *m = reinterpret_cast(v.m()); + Q_ASSERT(m); // Otherwise it would have been undefined, that is !v.isManaged() above. + if (QV4::ExecutionEngine *e = QV4::PersistentValueStorage::getEngine(m)) { + if (QJSEngine *jsEngine = e->jsEngine()) { + if (jsEngine->thread() != QThread::currentThread()) { + QMetaObject::invokeMethod( + jsEngine, [m](){ QV4::PersistentValueStorage::free(m); }); + return; } } - QV4::PersistentValueStorage::free(v); - } else if (QVariant *v = QJSValuePrivate::getVariant(jsval)) { - delete v; } + QV4::PersistentValueStorage::free(m); } }; diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp index 076b90c5f2..360a39aea3 100644 --- a/src/qml/jsapi/qjsvalueiterator.cpp +++ b/src/qml/jsapi/qjsvalueiterator.cpp @@ -58,12 +58,12 @@ void QJSValueIteratorPrivate::init(const QJSValue &v) QV4::ExecutionEngine *e = QJSValuePrivate::engine(&v); if (!e) return; - QV4::Object *o = QJSValuePrivate::getValue(&v)->objectValue(); + const QV4::Object *o = QJSValuePrivate::asManagedType(&v); if (!o) return; engine = e; - object = o; + object.set(e, o->asReturnedValue()); iterator.reset(o->ownPropertyKeys(object.valueRef())); next(); } @@ -209,7 +209,7 @@ QJSValue QJSValueIterator::value() const scope.engine->catchException(); return QJSValue(); } - return QJSValue(scope.engine, val->asReturnedValue()); + return QJSValuePrivate::fromReturnedValue(val->asReturnedValue()); } diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 1efe09c59f..d4725ce7c2 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -165,8 +165,23 @@ static void saveJSValue(QDataStream &stream, const void *data) if (jsv->isUndefined()) isNullOrUndefined |= 0x2; stream << isNullOrUndefined; - if (!isNullOrUndefined) - reinterpret_cast(data)->toVariant().save(stream); + if (!isNullOrUndefined) { + const QVariant v = reinterpret_cast(data)->toVariant(); + switch (v.userType()) { + case QMetaType::Bool: + case QMetaType::Double: + case QMetaType::Int: + case QMetaType::QString: + v.save(stream); + break; + default: + qWarning() << "QDataStream::operator<< was to save a non-trivial QJSValue." + << "This is not supported anymore, please stream a QVariant instead."; + QVariant().save(stream); + break; + } + + } } static void restoreJSValue(QDataStream &stream, void *data) @@ -183,7 +198,25 @@ static void restoreJSValue(QDataStream &stream, void *data) } else { QVariant v; v.load(stream); - QJSValuePrivate::setVariant(jsv, v); + + switch (v.userType()) { + case QMetaType::Bool: + *jsv = QJSValue(v.toBool()); + break; + case QMetaType::Double: + *jsv = QJSValue(v.toDouble()); + break; + case QMetaType::Int: + *jsv = QJSValue(v.toInt()); + break; + case QMetaType::QString: + *jsv = QJSValue(v.toString()); + break; + default: + qWarning() << "QDataStream::operator>> to restore a non-trivial QJSValue." + << "This is not supported anymore, please stream a QVariant instead."; + break; + } } } @@ -1428,11 +1461,9 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError() typedef QSet V4ObjectSet; static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int typeHint, bool createJSValueForObjects, V4ObjectSet *visitedObjects); -static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value); +static QObject *qtObjectFromJS(const QV4::Value &value); static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V4ObjectSet *visitedObjects = nullptr); -static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, - const QByteArray &targetType, - void **result); +static bool convertToNativeQObject(const QV4::Value &value, const QByteArray &targetType, void **result); static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVariantList &lst); static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst); static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVariantMap &vmap); @@ -1463,7 +1494,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int return QVariant::fromValue(QV4::JsonObject::toJsonValue(value)); if (typeHint == qMetaTypeId()) - return QVariant::fromValue(QJSValue(e, value.asReturnedValue())); + return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(value.asReturnedValue())); if (value.as()) { QV4::ScopedObject object(scope, value); @@ -1583,7 +1614,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int } if (createJSValueForObjects) - return QVariant::fromValue(QJSValue(scope.engine, o->asReturnedValue())); + return QVariant::fromValue(QJSValuePrivate::fromReturnedValue(o->asReturnedValue())); return objectToVariant(e, o, visitedObjects); } @@ -1743,8 +1774,8 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant) return QV4::Encode::null(); } } else if (type == qMetaTypeId()) { - const QJSValue *value = reinterpret_cast(ptr); - return QJSValuePrivate::convertedToValue(this, *value); + return QJSValuePrivate::convertToReturnedValue( + this, *reinterpret_cast(ptr)); } else if (type == qMetaTypeId >()) { // XXX Can this be made more by using Array as a prototype and implementing // directly against QList? @@ -2093,94 +2124,98 @@ void ExecutionEngine::setExtensionData(int index, Deletable *data) // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. -bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) +bool ExecutionEngine::metaTypeFromJS(const Value &value, int type, void *data) { // check if it's one of the types we know switch (QMetaType::Type(type)) { case QMetaType::Bool: - *reinterpret_cast(data) = value->toBoolean(); + *reinterpret_cast(data) = value.toBoolean(); return true; case QMetaType::Int: - *reinterpret_cast(data) = value->toInt32(); + *reinterpret_cast(data) = value.toInt32(); return true; case QMetaType::UInt: - *reinterpret_cast(data) = value->toUInt32(); + *reinterpret_cast(data) = value.toUInt32(); return true; case QMetaType::LongLong: - *reinterpret_cast(data) = qlonglong(value->toInteger()); + *reinterpret_cast(data) = qlonglong(value.toInteger()); return true; case QMetaType::ULongLong: - *reinterpret_cast(data) = qulonglong(value->toInteger()); + *reinterpret_cast(data) = qulonglong(value.toInteger()); return true; case QMetaType::Double: - *reinterpret_cast(data) = value->toNumber(); + *reinterpret_cast(data) = value.toNumber(); return true; case QMetaType::QString: - if (value->isUndefined() || value->isNull()) + if (value.isUndefined() || value.isNull()) *reinterpret_cast(data) = QString(); else - *reinterpret_cast(data) = value->toQString(); + *reinterpret_cast(data) = value.toQString(); return true; case QMetaType::QByteArray: - if (const ArrayBuffer *ab = value->as()) + if (const ArrayBuffer *ab = value.as()) *reinterpret_cast(data) = ab->asByteArray(); else *reinterpret_cast(data) = QByteArray(); return true; case QMetaType::Float: - *reinterpret_cast(data) = value->toNumber(); + *reinterpret_cast(data) = value.toNumber(); return true; case QMetaType::Short: - *reinterpret_cast(data) = short(value->toInt32()); + *reinterpret_cast(data) = short(value.toInt32()); return true; case QMetaType::UShort: - *reinterpret_cast(data) = value->toUInt16(); + *reinterpret_cast(data) = value.toUInt16(); return true; case QMetaType::Char: - *reinterpret_cast(data) = char(value->toInt32()); + *reinterpret_cast(data) = char(value.toInt32()); return true; case QMetaType::UChar: - *reinterpret_cast(data) = (unsigned char)(value->toInt32()); + *reinterpret_cast(data) = (unsigned char)(value.toInt32()); return true; case QMetaType::QChar: - if (String *s = value->stringValue()) { + if (String *s = value.stringValue()) { QString str = s->toQString(); *reinterpret_cast(data) = str.isEmpty() ? QChar() : str.at(0); } else { - *reinterpret_cast(data) = QChar(ushort(value->toUInt16())); + *reinterpret_cast(data) = QChar(ushort(value.toUInt16())); } return true; case QMetaType::QDateTime: - if (const QV4::DateObject *d = value->as()) { + if (const QV4::DateObject *d = value.as()) { *reinterpret_cast(data) = d->toQDateTime(); return true; } break; case QMetaType::QDate: - if (const QV4::DateObject *d = value->as()) { + if (const QV4::DateObject *d = value.as()) { *reinterpret_cast(data) = d->toQDateTime().date(); return true; } break; case QMetaType::QRegExp: - if (const QV4::RegExpObject *r = value->as()) { + if (const QV4::RegExpObject *r = value.as()) { *reinterpret_cast(data) = r->toQRegExp(); return true; } break; #if QT_CONFIG(regularexpression) case QMetaType::QRegularExpression: - if (const QV4::RegExpObject *r = value->as()) { + if (const QV4::RegExpObject *r = value.as()) { *reinterpret_cast(data) = r->toQRegularExpression(); return true; } break; #endif case QMetaType::QObjectStar: { - const QV4::QObjectWrapper *qobjectWrapper = value->as(); - if (qobjectWrapper || value->isNull()) { - *reinterpret_cast(data) = qtObjectFromJS(this, *value); + if (value.isNull()) { + *reinterpret_cast(data) = nullptr; return true; - } break; + } + if (value.as()) { + *reinterpret_cast(data) = qtObjectFromJS(value); + return true; + } + break; } case QMetaType::QStringList: { - const QV4::ArrayObject *a = value->as(); + const QV4::ArrayObject *a = value.as(); if (a) { *reinterpret_cast(data) = a->toQStringList(); return true; @@ -2188,33 +2223,45 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) break; } case QMetaType::QVariantList: { - const QV4::ArrayObject *a = value->as(); + const QV4::ArrayObject *a = value.as(); if (a) { - *reinterpret_cast(data) = toVariant(*a, /*typeHint*/-1, /*createJSValueForObjects*/false).toList(); + *reinterpret_cast(data) = a->engine()->toVariant( + *a, /*typeHint*/-1, /*createJSValueForObjects*/false).toList(); return true; } break; } case QMetaType::QVariantMap: { - const QV4::Object *o = value->as(); + const QV4::Object *o = value.as(); if (o) { - *reinterpret_cast(data) = variantMapFromJS(o); + *reinterpret_cast(data) = o->engine()->variantMapFromJS(o); return true; } break; } case QMetaType::QVariant: - *reinterpret_cast(data) = toVariant(*value, /*typeHint*/-1, /*createJSValueForObjects*/false); + if (const QV4::Managed *m = value.as()) + *reinterpret_cast(data) = m->engine()->toVariant(value, /*typeHint*/-1, /*createJSValueForObjects*/false); + else if (value.isNull()) + *reinterpret_cast(data) = QVariant::fromValue(nullptr); + else if (value.isUndefined()) + *reinterpret_cast(data) = QVariant(); + else if (value.isBoolean()) + *reinterpret_cast(data) = QVariant(value.booleanValue()); + else if (value.isInteger()) + *reinterpret_cast(data) = QVariant(value.integerValue()); + else if (value.isDouble()) + *reinterpret_cast(data) = QVariant(value.doubleValue()); return true; case QMetaType::QJsonValue: - *reinterpret_cast(data) = QV4::JsonObject::toJsonValue(*value); + *reinterpret_cast(data) = QV4::JsonObject::toJsonValue(value); return true; case QMetaType::QJsonObject: { - *reinterpret_cast(data) = QV4::JsonObject::toJsonObject(value->as()); + *reinterpret_cast(data) = QV4::JsonObject::toJsonObject(value.as()); return true; } case QMetaType::QJsonArray: { - const QV4::ArrayObject *a = value->as(); + const QV4::ArrayObject *a = value.as(); if (a) { *reinterpret_cast(data) = JsonObject::toJsonArray(a); return true; @@ -2226,7 +2273,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) } { - const QQmlValueTypeWrapper *vtw = value->as(); + const QQmlValueTypeWrapper *vtw = value.as(); if (vtw && vtw->typeId() == type) { return vtw->toGadget(data); } @@ -2254,18 +2301,18 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) // Try to use magic; for compatibility with qjsvalue_cast. QByteArray name = QMetaType::typeName(type); - if (convertToNativeQObject(this, *value, name, reinterpret_cast(data))) + if (convertToNativeQObject(value, name, reinterpret_cast(data))) return true; - if (value->as() && name.endsWith('*')) { + if (value.as() && name.endsWith('*')) { int valueType = QMetaType::type(name.left(name.size()-1)); - QVariant &var = value->as()->d()->data(); + QVariant &var = value.as()->d()->data(); if (valueType == var.userType()) { // We have T t, T* is requested, so return &t. *reinterpret_cast(data) = var.data(); return true; - } else if (Object *o = value->objectValue()) { + } else if (Object *o = value.objectValue()) { // Look in the prototype chain. - QV4::Scope scope(this); + QV4::Scope scope(o->engine()); QV4::ScopedObject proto(scope, o->getPrototypeOf()); while (proto) { bool canCast = false; @@ -2276,7 +2323,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) else if (proto->as()) { QByteArray className = name.left(name.size()-1); QV4::ScopedObject p(scope, proto.getPointer()); - if (QObject *qobject = qtObjectFromJS(this, p)) + if (QObject *qobject = qtObjectFromJS(p)) canCast = qobject->qt_metacast(className) != nullptr; } if (canCast) { @@ -2290,22 +2337,22 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) proto = proto->getPrototypeOf(); } } - } else if (value->isNull() && name.endsWith('*')) { + } else if (value.isNull() && name.endsWith('*')) { *reinterpret_cast(data) = nullptr; return true; } else if (type == qMetaTypeId()) { - *reinterpret_cast(data) = QJSValue(this, value->asReturnedValue()); + QJSValuePrivate::setValue(reinterpret_cast(data), value.asReturnedValue()); return true; } return false; } -static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, const QByteArray &targetType, void **result) +static bool convertToNativeQObject(const QV4::Value &value, const QByteArray &targetType, void **result) { if (!targetType.endsWith('*')) return false; - if (QObject *qobject = qtObjectFromJS(e, value)) { + if (QObject *qobject = qtObjectFromJS(value)) { int start = targetType.startsWith("const ") ? 6 : 0; QByteArray className = targetType.mid(start, targetType.size()-start-1); if (void *instance = qobject->qt_metacast(className)) { @@ -2316,12 +2363,12 @@ static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &va return false; } -static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value) +static QObject *qtObjectFromJS(const QV4::Value &value) { if (!value.isObject()) return nullptr; - QV4::Scope scope(engine); + QV4::Scope scope(value.as()->engine()); QV4::Scoped v(scope, value); if (v) { diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index efe44a324c..21971c1c23 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -648,7 +648,7 @@ public: QVariantMap variantMapFromJS(const QV4::Object *o); - bool metaTypeFromJS(const Value *value, int type, void *data); + static bool metaTypeFromJS(const Value &value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); int maxJSStackSize() const; diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index f4901e3e4d..dea3019ef4 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -64,7 +64,7 @@ struct Page { Value values[1]; // Really kEntriesPerPage, but keep the compiler happy }; -Page *getPage(Value *val) { +Page *getPage(const Value *val) { return reinterpret_cast(reinterpret_cast(val) & ~((quintptr)(WTF::pageSize() - 1))); } @@ -245,7 +245,7 @@ void PersistentValueStorage::mark(MarkStack *markStack) } } -ExecutionEngine *PersistentValueStorage::getEngine(Value *v) +ExecutionEngine *PersistentValueStorage::getEngine(const Value *v) { return getPage(v)->header.engine; } diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index 55e8eefcb7..79e8e50790 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -83,7 +83,7 @@ struct Q_QML_EXPORT PersistentValueStorage Iterator begin() { return Iterator(firstPage, 0); } Iterator end() { return Iterator(nullptr, 0); } - static ExecutionEngine *getEngine(Value *v); + static ExecutionEngine *getEngine(const Value *v); ExecutionEngine *engine; void *firstPage; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index e2d3b98ff6..4538900d21 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -241,7 +241,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r ).heapObject()); } else { QJSValue singleton = e->singletonInstance(r.type); - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, singleton)); + QV4::ScopedObject o(scope, QJSValuePrivate::asReturnedValue(&singleton)); lookup->qmlContextSingletonLookup.singleton = o->d(); } lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 66b79aa0e8..a924fa2975 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -170,7 +170,7 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object } else if (property.propType() == qMetaTypeId()) { QJSValue v; property.readProperty(object, &v); - return QJSValuePrivate::convertedToValue(v4, v); + return QJSValuePrivate::convertToReturnedValue(v4, v); } else if (property.isQVariant()) { QVariant v; property.readProperty(object, &v); @@ -519,7 +519,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP } else if (value.isUndefined() && property->propType() == QMetaType::QJsonValue) { PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined)); } else if (!newBinding && property->propType() == qMetaTypeId()) { - PROPERTY_STORE(QJSValue, QJSValue(scope.engine, value.asReturnedValue())); + PROPERTY_STORE(QJSValue, QJSValuePrivate::fromReturnedValue(value.asReturnedValue())); } else if (value.isUndefined() && property->propType() != qMetaTypeId()) { QString error = QLatin1String("Cannot assign [undefined] to "); if (!QMetaType::typeName(property->propType())) @@ -1775,7 +1775,8 @@ bool CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q bool queryEngine = false; if (callType == qMetaTypeId()) { - qjsValuePtr = new (&allocData) QJSValue(scope.engine, value.asReturnedValue()); + qjsValuePtr = new (&allocData) QJSValue; + QJSValuePrivate::setValue(qjsValuePtr, value.asReturnedValue()); type = qMetaTypeId(); } else if (callType == QMetaType::Int) { intValue = quint32(value.toInt32()); @@ -1932,7 +1933,9 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine) QV4::Scope scope(engine); if (type == qMetaTypeId()) { - return QJSValuePrivate::convertedToValue(scope.engine, *qjsValuePtr); + // The QJSValue can be passed around via dataPtr() + QJSValuePrivate::manageStringOnV4Heap(engine, qjsValuePtr); + return QJSValuePrivate::asReturnedValue(qjsValuePtr); } else if (type == QMetaType::Int) { return QV4::Encode(int(intValue)); } else if (type == QMetaType::UInt) { diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index b9566d5862..59f2759c81 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #include @@ -455,9 +456,10 @@ Q_NEVER_INLINE bool QQmlBinding::slowWrite(const QQmlPropertyData &core, delayedError()->setErrorDescription(QLatin1String("Invalid use of Qt.binding() in a binding declaration.")); return false; } - QQmlPropertyPrivate::writeValueProperty(m_target.data(), core, valueTypeData, QVariant::fromValue( - QJSValue(v4engine, result.asReturnedValue())), - context(), flags); + QQmlPropertyPrivate::writeValueProperty( + m_target.data(), core, valueTypeData, + QVariant::fromValue(QJSValuePrivate::fromReturnedValue(result.asReturnedValue())), + context(), flags); } else if (isUndefined) { const QLatin1String typeName(QMetaType::typeName(type) ? QMetaType::typeName(type) diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index b347bb3829..6de4ef153e 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -202,10 +202,7 @@ void QQmlBoundSignalExpression::evaluate(void **a) // for several cases (such as QVariant type and QObject-derived types) //args[ii] = engine->metaTypeToJS(type, a[ii + 1]); if (type == qMetaTypeId()) { - if (QV4::Value *v4Value = QJSValuePrivate::valueForData(reinterpret_cast(a[ii + 1]), &jsCall->args[ii])) - jsCall->args[ii] = *v4Value; - else - jsCall->args[ii] = QV4::Encode::undefined(); + jsCall->args[ii] = QJSValuePrivate::asReturnedValue(reinterpret_cast(a[ii + 1])); } else if (type == QMetaType::QVariant) { jsCall->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1])); } else if (type == QMetaType::Int) { diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index c2f8459fb1..0d9326b9a9 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -2518,7 +2518,7 @@ QJSValue QQmlEnginePrivate::singletonInstance(const QQmlType &type) // should behave identically to QML singleton types. q->setContextForObject(o, new QQmlContext(q->rootContext(), q)); } - singletonInstances.insert(type, value); + singletonInstances.convertAndInsert(v4engine(), type, &value); } else if (siinfo->qobjectCallback) { QObject *o = siinfo->qobjectCallback(q, q); @@ -2536,12 +2536,12 @@ QJSValue QQmlEnginePrivate::singletonInstance(const QQmlType &type) // should behave identically to QML singleton types. q->setContextForObject(o, new QQmlContext(q->rootContext(), q)); value = q->newQObject(o); - singletonInstances.insert(type, value); + singletonInstances.convertAndInsert(v4engine(), type, &value); } else if (!siinfo->url.isEmpty()) { QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous); QObject *o = component.beginCreate(q->rootContext()); value = q->newQObject(o); - singletonInstances.insert(type, value); + singletonInstances.convertAndInsert(v4engine(), type, &value); component.completeCreate(); } diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index fe42d4e347..23c69651ed 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -67,6 +67,7 @@ #include #include #include +#include #include #include @@ -285,7 +286,20 @@ public: } private: - QHash singletonInstances; + class SingletonInstances : private QHash + { + public: + void convertAndInsert(QV4::ExecutionEngine *engine, const QQmlType &type, QJSValue *value) + { + QJSValuePrivate::manageStringOnV4Heap(engine, value); + insert(type, *value); + } + + using QHash::value; + using QHash::take; + }; + + SingletonInstances singletonInstances; QHash cachedValueTypeInstances; // These members must be protected by a QQmlEnginePrivate::Locker as they are required by diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 040fa16af8..e0187eb75a 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1068,7 +1068,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper _vmeMetaObject->setVMEProperty(bindingProperty->coreIndex(), wrappedObject); } else { QJSValue value; - QJSValuePrivate::setValue(&value, v4, wrappedObject); + QJSValuePrivate::setValue(&value, wrappedObject); argv[0] = &value; QMetaObject::metacall(_qobject, QMetaObject::WriteProperty, bindingProperty->coreIndex(), argv); } diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 7bcc5e9900..c787d4092e 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -233,7 +233,7 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons QJSValue scriptSingleton = e->singletonInstance(type); if (!scriptSingleton.isUndefined()) { // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, scriptSingleton)); + QV4::ScopedObject o(scope, QJSValuePrivate::asReturnedValue(&scriptSingleton)); if (!!o) return o->get(name); } @@ -349,7 +349,7 @@ bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, } else { QJSValue scriptSingleton = e->singletonInstance(type); if (!scriptSingleton.isUndefined()) { - QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, scriptSingleton)); + QV4::ScopedObject apiprivate(scope, QJSValuePrivate::asReturnedValue(&scriptSingleton)); if (!apiprivate) { QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); scope.engine->throwError(error); -- cgit v1.2.3