Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant
#ifndef QJSENGINE_H
#define QJSENGINE_H
#include <QtCore/qmetatype.h>
#include <QtCore/qvariant.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qobject.h>
#include <QtCore/qtimezone.h>
#include <QtQml/qjsvalue.h>
#include <QtQml/qjsmanagedvalue.h>
#include <QtQml/qqmldebug.h>
QT_BEGIN_NAMESPACE
template <typename T>
inline T qjsvalue_cast(const QJSValue &);
class QJSEnginePrivate;
class Q_QML_EXPORT QJSEngine
: public QObject
{
Q_OBJECT
Q_PROPERTY(QString uiLanguage READ uiLanguage WRITE setUiLanguage NOTIFY uiLanguageChanged)
public:
QJSEngine();
explicit QJSEngine(QObject *parent);
~QJSEngine() override;
QJSValue globalObject() const;
QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1, QStringList *exceptionStackTrace = nullptr);
QJSValue importModule(const QString &fileName);
bool registerModule(const QString &moduleName, const QJSValue &value);
QJSValue newObject();
QJSValue newSymbol(const QString &name);
QJSValue newArray(uint length = 0);
QJSValue newQObject(QObject *object);
QJSValue newQMetaObject(const QMetaObject* metaObject);
template <typename T>
QJSValue newQMetaObject()
{
return newQMetaObject(&T::staticMetaObject);
}
QJSValue newErrorObject(QJSValue::ErrorType errorType, const QString &message = QString());
template <typename T>
inline QJSValue toScriptValue(const T &value)
{
return create(QMetaType::fromType<T>(), &value);
}
template <typename T>
inline QJSManagedValue toManagedValue(const T &value)
{
return createManaged(QMetaType::fromType<T>(), &value);
}
template <typename T>
inline QJSPrimitiveValue toPrimitiveValue(const T &value)
{
// In the common case that the argument fits into QJSPrimitiveValue, use it.
if constexpr (std::disjunction_v<
std::is_same<T, int>,
std::is_same<T, bool>,
std::is_same<T, double>,
std::is_same<T, QString>>) {
return QJSPrimitiveValue(value);
} else {
return createPrimitive(QMetaType::fromType<T>(), &value);
}
}
template <typename T>
inline T fromScriptValue(const QJSValue &value)
{
return qjsvalue_cast<T>(value);
}
template <typename T>
inline T fromManagedValue(const QJSManagedValue &value)
{
return qjsvalue_cast<T>(value);
}
template <typename T>
inline T fromPrimitiveValue(const QJSPrimitiveValue &value)
{
if constexpr (std::is_same_v<T, int>)
return value.toInteger();
if constexpr (std::is_same_v<T, bool>)
return value.toBoolean();
if constexpr (std::is_same_v<T, double>)
return value.toDouble();
if constexpr (std::is_same_v<T, QString>)
return value.toString();
if constexpr (std::is_same_v<T, QVariant>)
return value.toVariant();
if constexpr (std::is_pointer_v<T>)
return nullptr;
return qjsvalue_cast<T>(value);
}
template <typename T>
inline T fromVariant(const QVariant &value)
{
// MSVC doesn't like the constexpr chains and complains about "unreachable code"
QT_WARNING_PUSH
QT_WARNING_DISABLE_MSVC(4702)
if constexpr (std::is_same_v<T, QVariant>)
return value;
const QMetaType sourceType = value.metaType();
const QMetaType targetType = QMetaType::fromType<T>();
if (sourceType == targetType)
return *reinterpret_cast<const T *>(value.constData());
if constexpr (std::is_same_v<T,std::remove_const_t<std::remove_pointer_t<T>> const *>) {
using nonConstT = std::remove_const_t<std::remove_pointer_t<T>> *;
const QMetaType nonConstTargetType = QMetaType::fromType<nonConstT>();
if (value.metaType() == nonConstTargetType)
return *reinterpret_cast<const nonConstT *>(value.constData());
}
if constexpr (std::is_same_v<T, QJSValue>)
return toScriptValue(value);
if constexpr (std::is_same_v<T, QJSManagedValue>)
return toManagedValue(value);
if constexpr (std::is_same_v<T, QJSPrimitiveValue>)
return toPrimitiveValue(value);
if constexpr (std::is_same_v<T, QString>) {
if (sourceType.flags() & QMetaType::PointerToQObject) {
return convertQObjectToString(
*reinterpret_cast<QObject *const *>(value.constData()));
}
}
if constexpr (std::is_same_v<T, double>) {
if (sourceType == QMetaType::fromType<int>())
return double(*static_cast<const int *>(value.constData()));
}
if constexpr (std::is_same_v<T, int>) {
if (sourceType == QMetaType::fromType<double>()) {
return QJSNumberCoercion::toInteger(
*static_cast<const double *>(value.constData()));
}
}
if constexpr (std::is_same_v<QObject, std::remove_const_t<std::remove_pointer_t<T>>>) {
if (sourceType.flags() & QMetaType::PointerToQObject) {
return *static_cast<QObject *const *>(value.constData());
// We should not access source->metaObject() here since that may trigger some
// rather involved code. convertVariant() can do this using property caches.
}
}
if (sourceType == QMetaType::fromType<QJSValue>())
return fromScriptValue<T>(*reinterpret_cast<const QJSValue *>(value.constData()));
if (sourceType == QMetaType::fromType<QJSManagedValue>()) {
return fromManagedValue<T>(
*reinterpret_cast<const QJSManagedValue *>(value.constData()));
}
if (sourceType == QMetaType::fromType<QJSPrimitiveValue>()) {
return fromPrimitiveValue<T>(
*reinterpret_cast<const QJSPrimitiveValue *>(value.constData()));
}
return [&] {
T t{};
if (value.metaType() == QMetaType::fromType<QString>()) {
if (convertString(value.toString(), targetType, &t))
return t;
} else if (convertVariant(value, targetType, &t)) {
return t;
}
QMetaType::convert(value.metaType(), value.constData(), targetType, &t);
return t;
}();
QT_WARNING_POP
}
template<typename From, typename To>
inline To coerceValue(const From &from)
{
if constexpr (std::is_base_of_v<To, From>)
return from;
if constexpr (std::is_same_v<To, QJSValue>)
return toScriptValue(from);
if constexpr (std::is_same_v<From, QJSValue>)
return fromScriptValue<To>(from);
if constexpr (std::is_same_v<To, QJSManagedValue>)
return toManagedValue(from);
if constexpr (std::is_same_v<From, QJSManagedValue>)
return fromManagedValue<To>(from);
if constexpr (std::is_same_v<To, QJSPrimitiveValue>)
return toPrimitiveValue(from);
if constexpr (std::is_same_v<From, QJSPrimitiveValue>)
return fromPrimitiveValue<To>(from);
if constexpr (std::is_same_v<From, QVariant>)
return fromVariant<To>(from);
if constexpr (std::is_same_v<To, QVariant>)
return QVariant::fromValue(from);
if constexpr (std::is_same_v<To, QString>) {
if constexpr (std::is_base_of_v<QObject, std::remove_const_t<std::remove_pointer_t<From>>>)
return convertQObjectToString(from);
}
if constexpr (std::is_same_v<From, QDateTime>) {
if constexpr (std::is_same_v<To, QDate>)
return convertDateTimeToDate(from.toLocalTime());
if constexpr (std::is_same_v<To, QTime>)
return from.toLocalTime().time();
if constexpr (std::is_same_v<To, QString>)
return convertDateTimeToString(from.toLocalTime());
if constexpr (std::is_same_v<To, double>)
return convertDateTimeToNumber(from.toLocalTime());
}
if constexpr (std::is_same_v<From, QDate>) {
if constexpr (std::is_same_v<To, QDateTime>)
return from.startOfDay(QTimeZone::UTC);
if constexpr (std::is_same_v<To, QTime>) {
// This is the current time zone offset, for better or worse
return coerceValue<QDateTime, QTime>(coerceValue<QDate, QDateTime>(from));
}
if constexpr (std::is_same_v<To, QString>)
return convertDateTimeToString(coerceValue<QDate, QDateTime>(from));
if constexpr (std::is_same_v<To, double>)
return convertDateTimeToNumber(coerceValue<QDate, QDateTime>(from));
}
if constexpr (std::is_same_v<From, QTime>) {
if constexpr (std::is_same_v<To, QDate>) {
// Yes. April Fools' 1971. See qv4dateobject.cpp.
return from.isValid() ? QDate(1971, 4, 1) : QDate();
}
if constexpr (std::is_same_v<To, QDateTime>)
return QDateTime(coerceValue<QTime, QDate>(from), from, QTimeZone::LocalTime);
if constexpr (std::is_same_v<To, QString>)
return convertDateTimeToString(coerceValue<QTime, QDateTime>(from));
if constexpr (std::is_same_v<To, double>)
return convertDateTimeToNumber(coerceValue<QTime, QDateTime>(from));
}
if constexpr (std::is_same_v<To, std::remove_const_t<std::remove_pointer_t<To>> const *>) {
using nonConstTo = std::remove_const_t<std::remove_pointer_t<To>> *;
if constexpr (std::is_same_v<From, nonConstTo>)
return from;
}
if constexpr (std::is_same_v<To, double>) {
if constexpr (std::is_same_v<From, int>)
return double(from);
}
if constexpr (std::is_same_v<To, int>) {
if constexpr (std::is_same_v<From, double>)
return QJSNumberCoercion::toInteger(from);
}
return [&] {
const QMetaType sourceType = QMetaType::fromType<From>();
const QMetaType targetType = QMetaType::fromType<To>();
To to{};
if constexpr (std::is_same_v<From, QString>) {
if (convertString(from, targetType, &to))
return to;
} else if (convertMetaType(sourceType, &from, targetType, &to)) {
return to;
}
QMetaType::convert(sourceType, &from, targetType, &to);
return to;
}();
}
void collectGarbage();
enum ObjectOwnership { CppOwnership, JavaScriptOwnership };
static void setObjectOwnership(QObject *, ObjectOwnership);
static ObjectOwnership objectOwnership(QObject *);
enum Extension {
TranslationExtension = 0x1,
ConsoleExtension = 0x2,
GarbageCollectionExtension = 0x4,
AllExtensions = 0xffffffff
};
Q_DECLARE_FLAGS(Extensions, Extension)
void installExtensions(Extensions extensions, const QJSValue &object = QJSValue());
void setInterrupted(bool interrupted);
bool isInterrupted() const;
QV4::ExecutionEngine *handle() const { return m_v4Engine; }
void throwError(const QString &message);
void throwError(QJSValue::ErrorType errorType, const QString &message = QString());
void throwError(const QJSValue &error);
bool hasError() const;
QJSValue catchError();
QString uiLanguage() const;
void setUiLanguage(const QString &language);
Q_SIGNALS:
void uiLanguageChanged();
private:
QJSPrimitiveValue createPrimitive(QMetaType type, const void *ptr);
QJSManagedValue createManaged(QMetaType type, const void *ptr);
QJSValue create(QMetaType type, const void *ptr);
#if QT_QML_REMOVED_SINCE(6, 5)
QJSValue create(int id, const void *ptr); // only there for BC reasons
#endif
static bool convertPrimitive(const QJSPrimitiveValue &value, QMetaType type, void *ptr);
static bool convertManaged(const QJSManagedValue &value, int type, void *ptr);
static bool convertManaged(const QJSManagedValue &value, QMetaType type, void *ptr);
#if QT_QML_REMOVED_SINCE(6, 5)
static bool convertV2(const QJSValue &value, int type, void *ptr); // only there for BC reasons
#endif
static bool convertV2(const QJSValue &value, QMetaType metaType, void *ptr);
static bool convertString(const QString &string, QMetaType metaType, void *ptr);
bool convertVariant(const QVariant &value, QMetaType metaType, void *ptr);
bool convertMetaType(QMetaType fromType, const void *from, QMetaType toType, void *to);
QString convertQObjectToString(QObject *object);
QString convertDateTimeToString(const QDateTime &dateTime);
double convertDateTimeToNumber(const QDateTime &dateTime);
static QDate convertDateTimeToDate(const QDateTime &dateTime);
template<typename T>
friend inline T qjsvalue_cast(const QJSValue &);
template<typename T>
friend inline T qjsvalue_cast(const QJSManagedValue &);
template<typename T>
friend inline T qjsvalue_cast(const QJSPrimitiveValue &);
protected:
QJSEngine(QJSEnginePrivate &dd, QObject *parent = nullptr);
private:
QV4::ExecutionEngine *m_v4Engine;
Q_DISABLE_COPY(QJSEngine)
Q_DECLARE_PRIVATE(QJSEngine)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QJSEngine::Extensions)
template<typename T>
T qjsvalue_cast(const QJSValue &value)
{
if (T t; QJSEngine::convertV2(value, QMetaType::fromType<T>(), &t))
return t;
return qvariant_cast<T>(value.toVariant());
}
template<typename T>
T qjsvalue_cast(const QJSManagedValue &value)
{
if (T t; QJSEngine::convertManaged(value, QMetaType::fromType<T>(), &t))
return t;
return qvariant_cast<T>(value.toVariant());
}
template<typename T>
T qjsvalue_cast(const QJSPrimitiveValue &value)
{
if (T t; QJSEngine::convertPrimitive(value, QMetaType::fromType<T>(), &t))
return t;
return qvariant_cast<T>(value.toVariant());
}
template <>
inline QVariant qjsvalue_cast<QVariant>(const QJSValue &value)
{
return value.toVariant();
}
template <>
inline QVariant qjsvalue_cast<QVariant>(const QJSManagedValue &value)
{
return value.toVariant();
}
template <>
inline QVariant qjsvalue_cast<QVariant>(const QJSPrimitiveValue &value)
{
return value.toVariant();
}
Q_QML_EXPORT QJSEngine *qjsEngine(const QObject *);
QT_END_NAMESPACE
#endif // QJSENGINE_H