Add a QJSManagedValue

A QJSManagedValue is a view on a QJSValue which always knows the engine
the value belongs to. This allows us to implement the JavaScript
semantics of the various QJSValue methods in a much more rigorous way.

[ChangeLog][QtQml] The new QJSManagedValue should be used instead of
QJSValue for manipulating properties and prototypes of JavaScript
values, as well as for calling JavaScript functions.

Change-Id: I9d445ffcf68dfa72dba9bae0818e83c80665ad66
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2020-12-11 13:35:53 +01:00
parent d5ac54da62
commit 6f181768a3
21 changed files with 3280 additions and 65 deletions

View File

@ -114,6 +114,7 @@ qt_internal_add_module(Qml
debugger/qqmlprofiler_p.h
inlinecomponentutils_p.h
jsapi/qjsengine.cpp jsapi/qjsengine.h jsapi/qjsengine_p.h
jsapi/qjsmanagedvalue.cpp jsapi/qjsmanagedvalue.h
jsapi/qjsprimitivevalue.cpp jsapi/qjsprimitivevalue.h
jsapi/qjsvalue.cpp jsapi/qjsvalue.h jsapi/qjsvalue_p.h
jsapi/qjsvalueiterator.cpp jsapi/qjsvalueiterator.h jsapi/qjsvalueiterator_p.h

View File

@ -115,6 +115,7 @@ qt_internal_add_module(Qml
debugger/qqmlprofiler_p.h
inlinecomponentutils_p.h
jsapi/qjsengine.cpp jsapi/qjsengine.h jsapi/qjsengine_p.h
jsapi/qjsmanagedvalue.cpp jsapi/qjsmanagedvalue.h
jsapi/qjsprimitivevalue.cpp jsapi/qjsprimitivevalue.h
jsapi/qjsvalue.cpp jsapi/qjsvalue.h jsapi/qjsvalue_p.h
jsapi/qjsvalueiterator.cpp jsapi/qjsvalueiterator.h jsapi/qjsvalueiterator_p.h

View File

@ -1,5 +1,6 @@
SOURCES += \
$$PWD/qjsengine.cpp \
$$PWD/qjsmanagedvalue.cpp \
$$PWD/qjsprimitivevalue.cpp \
$$PWD/qjsvalue.cpp \
$$PWD/qjsvalueiterator.cpp \
@ -7,6 +8,7 @@ SOURCES += \
HEADERS += \
$$PWD/qjsengine.h \
$$PWD/qjsengine_p.h \
$$PWD/qjsmanagedvalue.h \
$$PWD/qjsprimitivevalue.h \
$$PWD/qjsvalue.h \
$$PWD/qjsvalue_p.h \

View File

@ -728,6 +728,13 @@ QJSValue QJSEngine::globalObject() const
return QJSValuePrivate::fromReturnedValue(v->asReturnedValue());
}
QJSManagedValue QJSEngine::createManaged(QMetaType type, const void *ptr)
{
QJSManagedValue result(m_v4Engine);
*result.d = m_v4Engine->metaTypeToJS(type.id(), ptr);
return result;
}
/*!
* \internal
* used by QJSEngine::toScriptValue
@ -739,6 +746,11 @@ QJSValue QJSEngine::create(int type, const void *ptr)
return QJSValuePrivate::fromReturnedValue(v->asReturnedValue());
}
bool QJSEngine::convertManaged(const QJSManagedValue &value, int type, void *ptr)
{
return QV4::ExecutionEngine::metaTypeFromJS(*value.d, type, ptr);
}
/*!
\internal
convert \a value to \a type, store the result in \a ptr

View File

@ -46,7 +46,7 @@
#include <QtCore/qsharedpointer.h>
#include <QtCore/qobject.h>
#include <QtQml/qjsvalue.h>
#include <QtQml/qjsmanagedvalue.h>
#include <QtQml/qqmldebug.h>
QT_BEGIN_NAMESPACE
@ -92,12 +92,25 @@ public:
{
return create(qMetaTypeId<T>(), &value);
}
template <typename T>
inline QJSManagedValue toManagedValue(const T &value)
{
return createManaged(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);
}
void collectGarbage();
enum ObjectOwnership { CppOwnership, JavaScriptOwnership };
@ -131,13 +144,18 @@ Q_SIGNALS:
void uiLanguageChanged();
private:
QJSManagedValue createManaged(QMetaType type, const void *ptr);
QJSValue create(int type, const void *ptr);
static bool convertManaged(const QJSManagedValue &value, int type, void *ptr);
static bool convertV2(const QJSValue &value, int type, void *ptr);
template<typename T>
friend inline T qjsvalue_cast(const QJSValue &);
template<typename T>
friend inline T qjsvalue_cast(const QJSManagedValue &);
protected:
QJSEngine(QJSEnginePrivate &dd, QObject *parent = nullptr);
@ -163,12 +181,30 @@ T qjsvalue_cast(const QJSValue &value)
return T();
}
template<typename T>
T qjsvalue_cast(const QJSManagedValue &value)
{
{
T t;
if (QJSEngine::convertManaged(value, qMetaTypeId<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();
}
Q_QML_EXPORT QJSEngine *qjsEngine(const QObject *);
QT_END_NAMESPACE

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QJSMANAGEDVALUE_H
#define QJSMANAGEDVALUE_H
#include <QtQml/qtqmlglobal.h>
#include <QtQml/qjsprimitivevalue.h>
#include <QtQml/qjsvalue.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
struct Value;
struct ExecutionEngine;
}
class QJSEngine;
class Q_QML_EXPORT QJSManagedValue
{
Q_DISABLE_COPY(QJSManagedValue)
public:
enum Type {
Undefined,
Boolean,
Number,
String,
Object,
Symbol
};
QJSManagedValue() = default;
QJSManagedValue(QJSValue value, QJSEngine *engine);
QJSManagedValue(const QJSPrimitiveValue &value, QJSEngine *engine);
QJSManagedValue(const QVariant &variant, QJSEngine *engine);
QJSManagedValue(const QString &string, QJSEngine *engine);
~QJSManagedValue();
QJSManagedValue(QJSManagedValue &&other);
QJSManagedValue &operator=(QJSManagedValue &&other);
bool equals(const QJSManagedValue &other) const;
bool strictlyEquals(const QJSManagedValue &other) const;
QJSEngine *engine() const;
QJSManagedValue prototype() const;
void setPrototype(const QJSManagedValue &prototype);
Type type() const;
// Compatibility with QJSValue
bool isUndefined() const { return type() == Undefined; }
bool isBoolean() const { return type() == Boolean; }
bool isNumber() const { return type() == Number; }
bool isString() const { return type() == String; }
bool isObject() const { return type() == Object; }
bool isSymbol() const { return type() == Symbol; }
// Special case of Number
bool isInteger() const;
// Selected special cases of Object
bool isNull() const;
bool isRegularExpression() const;
bool isArray() const;
bool isUrl() const;
bool isVariant() const;
bool isQObject() const;
bool isQMetaObject() const;
bool isDate() const;
bool isError() const;
bool isCallable() const;
// Native type transformations
QString toString() const;
double toNumber() const;
bool toBoolean() const;
// Variant-like type transformations
QJSPrimitiveValue toPrimitive() const;
QJSValue toJSValue() const;
QVariant toVariant() const;
// Special cases
int toInteger() const;
QRegularExpression toRegularExpression() const;
QUrl toUrl() const;
QObject *toQObject() const;
const QMetaObject *toQMetaObject() const;
QDateTime toDateTime() const;
// Properties of objects
bool hasProperty(const QString &name) const;
bool hasOwnProperty(const QString &name) const;
QJSValue property(const QString &name) const;
void setProperty(const QString &name, const QJSValue &value);
bool deleteProperty(const QString &name);
// Array indexing
bool hasProperty(quint32 arrayIndex) const;
bool hasOwnProperty(quint32 arrayIndex) const;
QJSValue property(quint32 arrayIndex) const;
void setProperty(quint32 arrayIndex, const QJSValue &value);
bool deleteProperty(quint32 arrayIndex);
// Calling functions
QJSValue call(const QJSValueList &arguments = {}) const;
QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &arguments = {}) const;
QJSValue callAsConstructor(const QJSValueList &arguments = {}) const;
private:
friend class QJSValue;
friend class QJSEngine;
QJSManagedValue(QV4::ExecutionEngine *engine);
QV4::Value *d = nullptr;
};
QT_END_NAMESPACE
#endif

View File

@ -439,6 +439,7 @@ public:
}
private:
friend class QJSManagedValue;
friend class QJSValue;
constexpr bool asBoolean() const { return std::get<bool>(d); }

View File

@ -43,6 +43,7 @@
#include "qjsengine.h"
#include "qjsvalue.h"
#include "qjsprimitivevalue.h"
#include "qjsmanagedvalue.h"
#include "qjsvalue_p.h"
#include "qv4value_p.h"
#include "qv4object_p.h"
@ -884,6 +885,20 @@ QJSValue::QJSValue(QJSPrimitiveValue &&value)
Q_UNREACHABLE();
}
QJSValue::QJSValue(QJSManagedValue &&value)
{
if (!value.d) {
d = QV4::Encode::undefined();
} else if (value.d->isManaged()) {
QJSValuePrivate::setRawValue(this, value.d);
value.d = nullptr;
} else {
d = value.d->asReturnedValue();
QV4::PersistentValueStorage::free(value.d);
value.d = nullptr;
}
}
static bool js_equal(const QString &string, const QV4::Value &value)
{
if (String *s = value.stringValue())

View File

@ -61,6 +61,9 @@ namespace QV4 {
struct Value;
}
class QJSPrimitiveValue;
class QJSManagedValue;
class Q_QML_EXPORT QJSValue
{
public:
@ -109,6 +112,7 @@ public:
QJSValue &operator=(const QJSValue &other);
explicit QJSValue(QJSPrimitiveValue &&value);
explicit QJSValue(QJSManagedValue &&value);
bool isBool() const;
bool isNumber() const;

View File

@ -83,6 +83,12 @@ class Q_AUTOTEST_EXPORT QJSValuePrivate
return (m & IsString) ? nullptr : reinterpret_cast<QV4::Value *>(m);
}
static QV4::Value *managedValue(QV4::Value &v)
{
quintptr m = quintptr(v.m());
return (m & IsString) ? nullptr : reinterpret_cast<QV4::Value *>(m);
}
static const QString *qstring(const QV4::Value &v)
{
const quintptr m = quintptr(v.m());
@ -108,14 +114,13 @@ class Q_AUTOTEST_EXPORT QJSValuePrivate
return QV4::Value::fromHeapObject(reinterpret_cast<QV4::Heap::Base *>(m)).asReturnedValue();
}
protected:
// Only for the test. You're not supposed to subclass QJSValuePrivate otherwise.
public:
static void setRawValue(QJSValue *jsval, QV4::Value *m)
{
jsval->d = encodeRawValue(quintptr(m));
}
public:
static QJSValue fromReturnedValue(QV4::ReturnedValue d)
{
QJSValue result;
@ -134,6 +139,19 @@ public:
return nullptr;
}
// This is a move operation and transfers ownership.
static QV4::Value *takeManagedValue(QJSValue *jsval)
{
QV4::Value v = QV4::Value::fromReturnedValue(jsval->d);
if (!v.isManaged())
return nullptr;
if (QV4::Value *value = managedValue(v)) {
setValue(jsval, QV4::Encode::undefined());
return value;
}
return nullptr;
}
static QV4::ReturnedValue asPrimitiveType(const QJSValue *jsval)
{
const QV4::Value v = QV4::Value::fromReturnedValue(jsval->d);
@ -170,6 +188,11 @@ public:
jsval->d = v.isManaged() ? encode(v) : v.asReturnedValue();
}
static void adoptValue(QJSValue *jsval, QV4::Value *v)
{
jsval->d = v->isManaged() ? encodeRawValue(quintptr(v)) : v->asReturnedValue();
}
// Moves any QString onto the V4 heap, changing the value to reflect that.
static void manageStringOnV4Heap(QV4::ExecutionEngine *e, QJSValue *jsval)
{

View File

@ -122,100 +122,125 @@ double Value::toNumberImpl(Value val)
}
}
QString Value::toQStringNoThrow() const
static QString primitiveToQString(const Value *value)
{
switch (type()) {
switch (value->type()) {
case Value::Empty_Type:
Q_ASSERT(!"empty Value encountered");
Q_UNREACHABLE();
return QString();
case Value::Undefined_Type:
return QStringLiteral("undefined");
case Value::Null_Type:
return QStringLiteral("null");
case Value::Boolean_Type:
if (booleanValue())
if (value->booleanValue())
return QStringLiteral("true");
else
return QStringLiteral("false");
case Value::Managed_Type:
Q_UNREACHABLE();
return QString();
case Value::Integer_Type: {
QString str;
RuntimeHelpers::numberToString(&str, (double)value->int_32(), 10);
return str;
}
case Value::Double_Type: {
QString str;
RuntimeHelpers::numberToString(&str, value->doubleValue(), 10);
return str;
}
} // switch
Q_UNREACHABLE();
return QString();
}
QString Value::toQStringNoThrow() const
{
if (isManaged()) {
if (String *s = stringValue())
return s->toQString();
if (Symbol *s = symbolValue())
return s->descriptiveString();
{
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
ScopedValue ex(scope);
bool caughtException = false;
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
ScopedValue ex(scope);
bool caughtException = false;
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
if (scope.hasException()) {
ex = scope.engine->catchException();
caughtException = true;
} else if (prim->isPrimitive()) {
return prim->toQStringNoThrow();
}
// Can't nest try/catch due to CXX ABI limitations for foreign exception nesting.
if (caughtException) {
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(ex, STRING_HINT));
if (scope.hasException()) {
ex = scope.engine->catchException();
caughtException = true;
} else if (prim->isPrimitive()) {
return prim->toQStringNoThrow();
return prim->toQStringNoThrow();
}
// Can't nest try/catch due to CXX ABI limitations for foreign exception nesting.
if (caughtException) {
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(ex, STRING_HINT));
if (scope.hasException()) {
ex = scope.engine->catchException();
} else if (prim->isPrimitive()) {
return prim->toQStringNoThrow();
}
}
return QString();
}
case Value::Integer_Type: {
QString str;
RuntimeHelpers::numberToString(&str, (double)int_32(), 10);
return str;
return QString();
}
default: { // double
QString str;
RuntimeHelpers::numberToString(&str, doubleValue(), 10);
return str;
}
} // switch
return primitiveToQString(this);
}
QString Value::toQString() const
{
switch (type()) {
case Value::Empty_Type:
Q_ASSERT(!"empty Value encountered");
Q_UNREACHABLE();
case Value::Undefined_Type:
return QStringLiteral("undefined");
case Value::Null_Type:
return QStringLiteral("null");
case Value::Boolean_Type:
if (booleanValue())
return QStringLiteral("true");
else
return QStringLiteral("false");
case Value::Managed_Type:
if (String *s = stringValue()) {
if (isManaged()) {
if (String *s = stringValue())
return s->toQString();
} else if (isSymbol()) {
if (isSymbol()) {
static_cast<const Managed *>(this)->engine()->throwTypeError();
return QString();
} else {
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
return prim->toQString();
}
case Value::Integer_Type: {
QString str;
RuntimeHelpers::numberToString(&str, (double)int_32(), 10);
return str;
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
return prim->toQString();
}
default: { // double
QString str;
RuntimeHelpers::numberToString(&str, doubleValue(), 10);
return str;
return primitiveToQString(this);
}
QString Value::toQString(bool *ok) const
{
if (isManaged()) {
if (String *s = stringValue()) {
*ok = true;
return s->toQString();
}
if (isSymbol()) {
static_cast<const Managed *>(this)->engine()->throwTypeError();
*ok = false;
return QString();
}
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
if (scope.hasException()) {
*ok = false;
return QString();
}
return prim->toQString(ok);
}
} // switch
return primitiveToQString(this);
}
QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const

View File

@ -190,8 +190,11 @@ struct Q_QML_PRIVATE_EXPORT Value : public StaticValue
inline double toNumber() const;
static double toNumberImpl(Value v);
double toNumberImpl() const { return toNumberImpl(*this); }
QString toQStringNoThrow() const;
QString toQString() const;
QString toQString(bool *ok) const;
Heap::String *toString(ExecutionEngine *e) const {
if (isString())
return reinterpret_cast<Heap::String *>(m());

View File

@ -34,6 +34,7 @@ add_subdirectory(qqmlmetatype)
if(TARGET Qt::Widgets)
add_subdirectory(qjsengine)
add_subdirectory(qjsvalue)
add_subdirectory(qjsmanagedvalue)
endif()
if(QT_FEATURE_process AND QT_FEATURE_qml_debug)
add_subdirectory(debugger)

View File

@ -34,6 +34,7 @@ add_subdirectory(qqmlmetatype)
if(TARGET Qt::Widgets)
add_subdirectory(qjsengine)
add_subdirectory(qjsvalue)
add_subdirectory(qjsmanagedvalue)
endif()
if(QT_FEATURE_process AND QT_FEATURE_qml_debug)
add_subdirectory(debugger)

View File

@ -0,0 +1,19 @@
# Generated from qjsvalue.pro.
#####################################################################
## tst_qjsvalue Test:
#####################################################################
qt_internal_add_test(tst_qjsvalue
SOURCES
tst_qjsvalue.cpp tst_qjsvalue.h
PUBLIC_LIBRARIES
Qt::Gui
Qt::GuiPrivate
Qt::Qml
Qt::QmlPrivate
Qt::Widgets
)
## Scopes:
#####################################################################

View File

@ -0,0 +1,16 @@
# Generated from qjsmanagedvalue.pro.
#####################################################################
## tst_qjsmanagedvalue Test:
#####################################################################
qt_internal_add_test(tst_qjsmanagedvalue
SOURCES
tst_qjsmanagedvalue.cpp tst_qjsmanagedvalue.h
PUBLIC_LIBRARIES
Qt::Qml
Qt::QmlPrivate
)
## Scopes:
#####################################################################

View File

@ -0,0 +1,6 @@
CONFIG += testcase
TARGET = tst_qjsmanagedvalue
macos:CONFIG -= app_bundle
QT += qml testlib qml-private
SOURCES += tst_qjsmanagedvalue.cpp
HEADERS += tst_qjsmanagedvalue.h

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,116 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef TST_QJSMANAGEDVALUE_H
#define TST_QJSMANAGEDVALUE_H
#include <QtCore/qobject.h>
#include <QtQml/qjsengine.h>
class tst_QJSManagedValue : public QObject
{
Q_OBJECT
private Q_SLOTS:
void ctor_invalid();
void ctor_undefinedWithEngine();
void ctor_nullWithEngine();
void ctor_boolWithEngine();
void ctor_intWithEngine();
void ctor_uintWithEngine();
void ctor_floatWithEngine();
void ctor_stringWithEngine();
void ctor_copyAndAssignWithEngine();
void toString();
void toNumber();
void toBoolean();
void toVariant();
void equals();
void strictlyEquals();
void hasProperty_basic();
void hasProperty_globalObject();
void hasProperty_changePrototype();
void hasProperty_QTBUG56830_data();
void hasProperty_QTBUG56830();
void deleteProperty_basic();
void deleteProperty_globalObject();
void deleteProperty_inPrototype();
void getSetPrototype_cyclicPrototype();
void getSetPrototype_evalCyclicPrototype();
void getSetPrototype_eval();
void getSetPrototype_invalidPrototype();
void getSetPrototype_twoEngines();
void getSetPrototype_null();
void getSetPrototype_notObjectOrNull();
void getSetPrototype();
void getSetProperty_propertyRemoval();
void getSetProperty_resolveMode();
void getSetProperty_twoEngines();
void getSetProperty_gettersAndSettersThrowErrorJS();
void getSetProperty_array();
void getSetProperty();
void call_function();
void call_object();
void call_newObjects();
void call_this();
void call_arguments();
void call();
void call_twoEngines();
void call_nonFunction_data();
void call_nonFunction();
void construct_nonFunction_data();
void construct_nonFunction();
void construct_simple();
void construct_newObjectJS();
void construct_arg();
void construct_proto();
void construct_returnInt();
void construct_throw();
void construct_twoEngines();
void construct_constructorThrowsPrimitive();
void castToPointer();
void engineDeleted();
void valueOfWithClosure();
void jsvalueArrayToSequenceType();
void stringAndUrl();
void jsFunctionInVariant();
private:
void newEngine() { engine.reset(new QJSEngine()); }
QScopedPointer<QJSEngine> engine;
};
#endif // TST_QJSMANAGEDVALUE

View File

@ -82,6 +82,7 @@ qtHaveModule(widgets) {
SUBDIRS += \
qjsengine \
qjsvalue \
qjsmanagedvalue \
# qwidgetsinqml
}