2011-05-11 07:20:40 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2016-01-19 09:38:36 +00:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2011-05-11 07:20:40 +00:00
|
|
|
**
|
2012-02-16 04:43:03 +00:00
|
|
|
** This file is part of the QtQml module of the Qt Toolkit.
|
2011-05-11 07:20:40 +00:00
|
|
|
**
|
2016-01-19 09:38:36 +00:00
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
2012-09-20 05:21:40 +00:00
|
|
|
** 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
|
2015-01-28 11:55:39 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
2016-01-19 09:38:36 +00:00
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2012-09-20 05:21:40 +00:00
|
|
|
**
|
2011-05-11 07:20:40 +00:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-09-20 05:21:40 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2016-01-19 09:38:36 +00:00
|
|
|
** 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.
|
2012-09-20 05:21:40 +00:00
|
|
|
**
|
2016-01-19 09:38:36 +00:00
|
|
|
** 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.
|
2011-05-11 07:20:40 +00:00
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
2013-06-07 09:21:18 +00:00
|
|
|
#include "qv4qobjectwrapper_p.h"
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
#include <private/qqmlpropertycache_p.h>
|
|
|
|
#include <private/qqmlengine_p.h>
|
|
|
|
#include <private/qqmlvmemetaobject_p.h>
|
|
|
|
#include <private/qqmlbinding_p.h>
|
2011-07-29 08:25:44 +00:00
|
|
|
#include <private/qjsvalue_p.h>
|
2012-02-16 04:43:03 +00:00
|
|
|
#include <private/qqmlexpression_p.h>
|
2012-08-09 03:26:32 +00:00
|
|
|
#include <private/qqmlglobal_p.h>
|
2013-05-30 11:16:44 +00:00
|
|
|
#include <private/qqmltypewrapper_p.h>
|
2013-05-31 11:05:37 +00:00
|
|
|
#include <private/qqmlvaluetypewrapper_p.h>
|
2013-05-30 11:45:23 +00:00
|
|
|
#include <private/qqmlcontextwrapper_p.h>
|
2013-05-30 20:09:46 +00:00
|
|
|
#include <private/qqmllistwrapper_p.h>
|
2014-03-04 03:53:52 +00:00
|
|
|
#include <private/qqmlbuiltinfunctions_p.h>
|
2013-06-07 09:21:18 +00:00
|
|
|
#include <private/qv8engine_p.h>
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2015-12-28 12:22:29 +00:00
|
|
|
#include <private/qv4arraybuffer_p.h>
|
2013-05-06 10:43:39 +00:00
|
|
|
#include <private/qv4functionobject_p.h>
|
2013-05-08 05:32:31 +00:00
|
|
|
#include <private/qv4runtime_p.h>
|
2013-05-14 00:01:04 +00:00
|
|
|
#include <private/qv4variantobject_p.h>
|
2013-05-31 14:06:42 +00:00
|
|
|
#include <private/qv4sequenceobject_p.h>
|
2013-06-03 10:52:53 +00:00
|
|
|
#include <private/qv4objectproto_p.h>
|
2013-06-03 19:28:07 +00:00
|
|
|
#include <private/qv4jsonobject_p.h>
|
2013-06-07 09:02:03 +00:00
|
|
|
#include <private/qv4regexpobject_p.h>
|
2015-02-13 10:31:45 +00:00
|
|
|
#include <private/qv4dateobject_p.h>
|
2013-09-05 11:22:23 +00:00
|
|
|
#include <private/qv4scopedvalue_p.h>
|
2014-03-26 14:11:48 +00:00
|
|
|
#include <private/qv4mm_p.h>
|
2014-08-12 11:52:50 +00:00
|
|
|
#include <private/qqmlscriptstring_p.h>
|
|
|
|
#include <private/qv4compileddata_p.h>
|
2013-05-06 10:43:39 +00:00
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
#include <QtQml/qjsvalue.h>
|
2012-04-24 16:22:06 +00:00
|
|
|
#include <QtCore/qjsonarray.h>
|
|
|
|
#include <QtCore/qjsonobject.h>
|
2012-03-23 17:14:29 +00:00
|
|
|
#include <QtCore/qjsonvalue.h>
|
2011-05-11 07:20:40 +00:00
|
|
|
#include <QtCore/qvarlengtharray.h>
|
|
|
|
#include <QtCore/qtimer.h>
|
2011-06-07 03:19:27 +00:00
|
|
|
#include <QtCore/qatomic.h>
|
2016-05-29 19:33:59 +00:00
|
|
|
#include <QtCore/qmetaobject.h>
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2011-09-28 07:57:04 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2011-05-11 07:20:40 +00:00
|
|
|
// The code in this file does not violate strict aliasing, but GCC thinks it does
|
|
|
|
// so turn off the warnings for us to have a clean build
|
2014-10-31 05:58:18 +00:00
|
|
|
QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
|
2013-05-22 14:51:34 +00:00
|
|
|
|
|
|
|
using namespace QV4;
|
|
|
|
|
2016-02-04 04:23:10 +00:00
|
|
|
QPair<QObject *, int> QObjectMethod::extractQtMethod(const QV4::FunctionObject *function)
|
2013-06-06 12:18:11 +00:00
|
|
|
{
|
2013-09-26 20:07:27 +00:00
|
|
|
QV4::ExecutionEngine *v4 = function->engine();
|
|
|
|
if (v4) {
|
|
|
|
QV4::Scope scope(v4);
|
|
|
|
QV4::Scoped<QObjectMethod> method(scope, function->as<QObjectMethod>());
|
|
|
|
if (method)
|
|
|
|
return qMakePair(method->object(), method->methodIndex());
|
2013-06-06 12:18:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return qMakePair((QObject *)0, -1);
|
|
|
|
}
|
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
static QPair<QObject *, int> extractQtSignal(const Value &value)
|
2013-06-06 12:18:11 +00:00
|
|
|
{
|
2015-01-15 10:36:57 +00:00
|
|
|
if (value.isObject()) {
|
2015-02-13 12:39:20 +00:00
|
|
|
QV4::ExecutionEngine *v4 = value.as<Object>()->engine();
|
2013-09-26 20:07:27 +00:00
|
|
|
QV4::Scope scope(v4);
|
|
|
|
QV4::ScopedFunctionObject function(scope, value);
|
|
|
|
if (function)
|
2016-02-04 04:23:10 +00:00
|
|
|
return QObjectMethod::extractQtMethod(function);
|
2013-09-26 20:07:27 +00:00
|
|
|
|
|
|
|
QV4::Scoped<QV4::QmlSignalHandler> handler(scope, value);
|
|
|
|
if (handler)
|
|
|
|
return qMakePair(handler->object(), handler->signalIndex());
|
|
|
|
}
|
2013-06-06 12:18:11 +00:00
|
|
|
|
|
|
|
return qMakePair((QObject *)0, -1);
|
|
|
|
}
|
|
|
|
|
2016-08-08 11:39:12 +00:00
|
|
|
static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object,
|
|
|
|
const QQmlPropertyData &property)
|
2013-06-06 13:18:03 +00:00
|
|
|
{
|
|
|
|
Q_ASSERT(!property.isFunction());
|
2013-09-12 20:37:41 +00:00
|
|
|
QV4::Scope scope(v4);
|
2013-06-06 13:18:03 +00:00
|
|
|
|
|
|
|
if (property.isQObject()) {
|
|
|
|
QObject *rv = 0;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &rv);
|
2013-06-06 13:18:03 +00:00
|
|
|
return QV4::QObjectWrapper::wrap(v4, rv);
|
|
|
|
} else if (property.isQList()) {
|
2016-08-04 10:38:43 +00:00
|
|
|
return QmlListWrapper::create(v4, object, property.coreIndex(), property.propType());
|
|
|
|
} else if (property.propType() == QMetaType::QReal) {
|
2013-06-06 13:18:03 +00:00
|
|
|
qreal v = 0;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &v);
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(v);
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property.propType() == QMetaType::Int || property.isEnum()) {
|
2013-06-06 13:18:03 +00:00
|
|
|
int v = 0;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &v);
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(v);
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property.propType() == QMetaType::Bool) {
|
2013-06-06 13:18:03 +00:00
|
|
|
bool v = false;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &v);
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(v);
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property.propType() == QMetaType::QString) {
|
2013-06-06 13:18:03 +00:00
|
|
|
QString v;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &v);
|
2013-09-26 10:04:52 +00:00
|
|
|
return v4->newString(v)->asReturnedValue();
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property.propType() == QMetaType::UInt) {
|
2013-06-06 13:18:03 +00:00
|
|
|
uint v = 0;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &v);
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(v);
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property.propType() == QMetaType::Float) {
|
2013-06-06 13:18:03 +00:00
|
|
|
float v = 0;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &v);
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(v);
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property.propType() == QMetaType::Double) {
|
2013-06-06 13:18:03 +00:00
|
|
|
double v = 0;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &v);
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(v);
|
2013-06-06 13:18:03 +00:00
|
|
|
} else if (property.isV4Handle()) {
|
|
|
|
QQmlV4Handle handle;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &handle);
|
2013-09-24 10:13:42 +00:00
|
|
|
return handle;
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property.propType() == qMetaTypeId<QJSValue>()) {
|
2013-06-06 13:18:03 +00:00
|
|
|
QJSValue v;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &v);
|
2015-01-14 15:22:33 +00:00
|
|
|
return QJSValuePrivate::convertedToValue(v4, v);
|
2013-06-06 13:18:03 +00:00
|
|
|
} else if (property.isQVariant()) {
|
|
|
|
QVariant v;
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, &v);
|
2013-06-06 13:18:03 +00:00
|
|
|
|
|
|
|
if (QQmlValueTypeFactory::isValueType(v.userType())) {
|
2014-11-10 10:39:03 +00:00
|
|
|
if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(v.userType()))
|
2016-08-04 10:38:43 +00:00
|
|
|
return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, v.userType()); // VariantReference value-type.
|
2013-06-06 13:18:03 +00:00
|
|
|
}
|
|
|
|
|
2015-01-02 14:07:35 +00:00
|
|
|
return scope.engine->fromVariant(v);
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (QQmlValueTypeFactory::isValueType(property.propType())) {
|
|
|
|
if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType()))
|
|
|
|
return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType());
|
2013-06-06 13:18:03 +00:00
|
|
|
} else {
|
|
|
|
// see if it's a sequence type
|
|
|
|
bool succeeded = false;
|
2016-08-04 10:38:43 +00:00
|
|
|
QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded));
|
2013-06-06 13:18:03 +00:00
|
|
|
if (succeeded)
|
2015-01-15 20:28:01 +00:00
|
|
|
return retn->asReturnedValue();
|
2013-06-06 13:18:03 +00:00
|
|
|
}
|
|
|
|
|
2016-08-04 10:38:43 +00:00
|
|
|
if (property.propType() == QMetaType::UnknownType) {
|
|
|
|
QMetaProperty p = object->metaObject()->property(property.coreIndex());
|
2013-06-06 13:18:03 +00:00
|
|
|
qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
|
|
|
|
"'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::undefined();
|
2013-06-06 13:18:03 +00:00
|
|
|
} else {
|
2016-08-04 10:38:43 +00:00
|
|
|
QVariant v(property.propType(), (void *)0);
|
2016-08-08 11:39:12 +00:00
|
|
|
property.readProperty(object, v.data());
|
2015-01-02 14:07:35 +00:00
|
|
|
return scope.engine->fromVariant(v);
|
2013-06-06 13:18:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-06 12:18:11 +00:00
|
|
|
void QObjectWrapper::initializeBindings(ExecutionEngine *engine)
|
|
|
|
{
|
2015-03-25 13:40:35 +00:00
|
|
|
engine->functionPrototype()->defineDefaultProperty(QStringLiteral("connect"), method_connect);
|
|
|
|
engine->functionPrototype()->defineDefaultProperty(QStringLiteral("disconnect"), method_disconnect);
|
2013-06-06 12:18:11 +00:00
|
|
|
}
|
|
|
|
|
2013-06-12 05:27:51 +00:00
|
|
|
QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const
|
|
|
|
{
|
2013-11-01 11:38:32 +00:00
|
|
|
Q_UNUSED(revisionMode);
|
|
|
|
|
2016-09-07 11:31:14 +00:00
|
|
|
QQmlData *ddata = QQmlData::get(d()->object(), false);
|
2013-06-12 05:27:51 +00:00
|
|
|
if (!ddata)
|
|
|
|
return 0;
|
2013-06-24 09:26:22 +00:00
|
|
|
QQmlPropertyData *result = 0;
|
2013-06-12 05:27:51 +00:00
|
|
|
if (ddata && ddata->propertyCache)
|
2016-09-07 11:31:14 +00:00
|
|
|
result = ddata->propertyCache->property(name, d()->object(), qmlContext);
|
2013-10-16 12:50:57 +00:00
|
|
|
else
|
2016-09-07 11:31:14 +00:00
|
|
|
result = QQmlPropertyCache::property(engine->jsEngine(), d()->object(), name, qmlContext, *local);
|
2013-06-24 09:26:22 +00:00
|
|
|
return result;
|
2013-06-12 05:27:51 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 15:07:29 +00:00
|
|
|
ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode,
|
2015-02-13 09:42:01 +00:00
|
|
|
bool *hasProperty, bool includeImports) const
|
2013-05-22 14:51:34 +00:00
|
|
|
{
|
2016-09-07 11:31:14 +00:00
|
|
|
if (QQmlData::wasDeleted(d()->object())) {
|
2013-05-22 14:51:34 +00:00
|
|
|
if (hasProperty)
|
|
|
|
*hasProperty = false;
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::undefined();
|
2013-05-22 14:51:34 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 15:07:29 +00:00
|
|
|
ExecutionEngine *v4 = engine();
|
2013-09-12 20:37:41 +00:00
|
|
|
|
2015-09-09 15:07:29 +00:00
|
|
|
if (name->equals(v4->id_destroy()) || name->equals(v4->id_toString())) {
|
|
|
|
int index = name->equals(v4->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod;
|
2013-05-27 18:23:13 +00:00
|
|
|
if (hasProperty)
|
|
|
|
*hasProperty = true;
|
2015-09-09 15:07:29 +00:00
|
|
|
ExecutionContext *global = v4->rootContext();
|
2016-09-07 11:31:14 +00:00
|
|
|
return QV4::QObjectMethod::create(global, d()->object(), index);
|
2013-05-27 18:23:13 +00:00
|
|
|
}
|
|
|
|
|
2013-06-06 13:18:03 +00:00
|
|
|
QQmlPropertyData local;
|
2015-09-09 15:07:29 +00:00
|
|
|
QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local);
|
2013-05-22 14:51:34 +00:00
|
|
|
|
2013-06-06 13:18:03 +00:00
|
|
|
if (!result) {
|
|
|
|
if (includeImports && name->startsWithUpper()) {
|
|
|
|
// Check for attached properties
|
|
|
|
if (qmlContext && qmlContext->imports) {
|
2014-12-01 15:13:20 +00:00
|
|
|
QQmlTypeNameCache::Result r = qmlContext->imports->query(name);
|
2013-06-06 13:18:03 +00:00
|
|
|
|
|
|
|
if (hasProperty)
|
|
|
|
*hasProperty = true;
|
|
|
|
|
|
|
|
if (r.isValid()) {
|
|
|
|
if (r.scriptIndex != -1) {
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::undefined();
|
2013-06-06 13:18:03 +00:00
|
|
|
} else if (r.type) {
|
2016-09-07 11:31:14 +00:00
|
|
|
return QmlTypeWrapper::create(v4, d()->object(),
|
2014-11-07 01:06:42 +00:00
|
|
|
r.type, Heap::QmlTypeWrapper::ExcludeEnums);
|
2013-06-06 13:18:03 +00:00
|
|
|
} else if (r.importNamespace) {
|
2016-09-07 11:31:14 +00:00
|
|
|
return QmlTypeWrapper::create(v4, d()->object(),
|
2014-11-07 01:06:42 +00:00
|
|
|
qmlContext->imports, r.importNamespace, Heap::QmlTypeWrapper::ExcludeEnums);
|
2013-06-06 13:18:03 +00:00
|
|
|
}
|
|
|
|
Q_ASSERT(!"Unreachable");
|
2013-05-22 14:51:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-12-01 15:13:20 +00:00
|
|
|
return QV4::Object::get(this, name, hasProperty);
|
2013-06-06 13:18:03 +00:00
|
|
|
}
|
|
|
|
|
2016-09-07 11:31:14 +00:00
|
|
|
QQmlData *ddata = QQmlData::get(d()->object(), false);
|
2013-06-06 13:18:03 +00:00
|
|
|
|
|
|
|
if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
|
|
|
|
if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) {
|
|
|
|
if (hasProperty)
|
|
|
|
*hasProperty = false;
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::undefined();
|
2013-06-06 13:18:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hasProperty)
|
|
|
|
*hasProperty = true;
|
|
|
|
|
2016-09-07 11:31:14 +00:00
|
|
|
return getProperty(v4, d()->object(), result);
|
2013-10-24 12:51:02 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 14:09:39 +00:00
|
|
|
ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired)
|
2013-10-24 12:51:02 +00:00
|
|
|
{
|
2016-08-04 10:38:43 +00:00
|
|
|
QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex()));
|
[Regression] Fix lazy binding evaluation
Commit 04774bb14c81688f86a2b31b8624bde8ebf59062 long time ago introduced the
concept of fixed order binding initialization with lazy evaluation, where a bit
is reserved for each binding that indicates whether it's been initialized the
first time or not. When reading a property on a QObject, we'd check if the
corresponding binding for the property has been initialized or not and flush
(i.e. execute) the binding if necessary.
As part of the V4/V8 clean-up, commit 1eb41200948ab414f1c47d93123b41c547a993df
removed the StoreV8Binding instruction, which made the call for setting the
this-binding-is-not-evaluated-yet bit. Nowadays we only use StoreBinding, for
which this optimization was never implemented (and not needed really). Now that
we have a unified JS code path, we need to set the pending binding bit and also
make sure that we call flushPendingBinding for any JS side property access
(accelerated or not).
Also flushPendingBindingImpl had two bugs:
* In an attempt of trying to find the binding to flush, it could happen that
we'd try to flush a previously destroyed binding (m_mePtr is null), so
the b variable would remain the first binding in the object and we'd flush
the wrong one (instead of none). Added a missing check to verify that the
property index matches.
* Also resetting the mePtr must be done through clear(), to ensure that the
pointer in bindValues in the VME is also cleared, to avoid re-enabling the
same binding again in complete();
Task-number: QTBUG-36441
Change-Id: Icdb0c8fb036051fd5d6c4d33b10cd0c0ed9a9d5c
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
2014-01-28 13:04:58 +00:00
|
|
|
|
2013-10-24 12:51:02 +00:00
|
|
|
if (property->isFunction() && !property->isVarProperty()) {
|
|
|
|
if (property->isVMEFunction()) {
|
2013-11-14 23:06:18 +00:00
|
|
|
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
|
2013-06-06 13:18:03 +00:00
|
|
|
Q_ASSERT(vmemo);
|
2016-08-04 10:38:43 +00:00
|
|
|
return vmemo->vmeMethod(property->coreIndex());
|
2013-10-24 12:51:02 +00:00
|
|
|
} else if (property->isV4Function()) {
|
2015-09-09 15:07:29 +00:00
|
|
|
Scope scope(engine);
|
|
|
|
ScopedContext global(scope, engine->qmlContext());
|
2016-02-01 08:33:38 +00:00
|
|
|
if (!global)
|
|
|
|
global = engine->rootContext();
|
2016-08-04 10:38:43 +00:00
|
|
|
return QV4::QObjectMethod::create(global, object, property->coreIndex());
|
2013-10-24 12:51:02 +00:00
|
|
|
} else if (property->isSignalHandler()) {
|
2015-09-09 13:23:18 +00:00
|
|
|
QmlSignalHandler::initProto(engine);
|
2016-08-04 10:38:43 +00:00
|
|
|
return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
|
2013-06-06 13:18:03 +00:00
|
|
|
} else {
|
2015-09-09 15:07:29 +00:00
|
|
|
ExecutionContext *global = engine->rootContext();
|
2016-08-04 10:38:43 +00:00
|
|
|
return QV4::QObjectMethod::create(global, object, property->coreIndex());
|
2013-06-06 13:18:03 +00:00
|
|
|
}
|
2013-05-22 14:51:34 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 15:07:29 +00:00
|
|
|
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0;
|
2013-06-06 13:18:03 +00:00
|
|
|
|
2015-06-10 11:32:20 +00:00
|
|
|
if (captureRequired && ep && ep->propertyCapture && !property->isConstant())
|
2016-08-04 10:38:43 +00:00
|
|
|
ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex());
|
2013-06-06 13:18:03 +00:00
|
|
|
|
2013-10-24 12:51:02 +00:00
|
|
|
if (property->isVarProperty()) {
|
2013-11-14 23:06:18 +00:00
|
|
|
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
|
2013-06-06 13:18:03 +00:00
|
|
|
Q_ASSERT(vmemo);
|
2016-08-04 10:38:43 +00:00
|
|
|
return vmemo->vmeProperty(property->coreIndex());
|
2013-06-06 13:18:03 +00:00
|
|
|
} else {
|
2016-08-08 11:39:12 +00:00
|
|
|
return loadProperty(engine, object, *property);
|
2013-06-06 13:18:03 +00:00
|
|
|
}
|
2013-06-03 14:29:16 +00:00
|
|
|
}
|
|
|
|
|
2014-11-13 10:41:52 +00:00
|
|
|
ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty)
|
2013-06-06 13:59:41 +00:00
|
|
|
{
|
|
|
|
if (QQmlData::wasDeleted(object)) {
|
|
|
|
if (hasProperty)
|
|
|
|
*hasProperty = false;
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::null();
|
2013-06-06 13:59:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!QQmlData::get(object, true)) {
|
|
|
|
if (hasProperty)
|
|
|
|
*hasProperty = false;
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::null();
|
2013-06-06 13:59:41 +00:00
|
|
|
}
|
|
|
|
|
2015-09-09 15:07:29 +00:00
|
|
|
QV4::Scope scope(engine);
|
2014-11-13 10:41:52 +00:00
|
|
|
QV4::Scoped<QObjectWrapper> wrapper(scope, wrap(engine, object));
|
2013-06-06 13:59:41 +00:00
|
|
|
if (!wrapper) {
|
|
|
|
if (hasProperty)
|
|
|
|
*hasProperty = false;
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::null();
|
2013-06-06 13:59:41 +00:00
|
|
|
}
|
2014-11-13 10:41:52 +00:00
|
|
|
return wrapper->getQmlProperty(qmlContext, name, revisionMode, hasProperty);
|
2013-06-06 13:59:41 +00:00
|
|
|
}
|
|
|
|
|
2014-11-13 10:41:52 +00:00
|
|
|
bool QObjectWrapper::setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name,
|
2015-01-15 10:36:57 +00:00
|
|
|
QObjectWrapper::RevisionMode revisionMode, const Value &value)
|
2013-06-06 14:11:23 +00:00
|
|
|
{
|
|
|
|
if (QQmlData::wasDeleted(object))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
QQmlPropertyData local;
|
2015-09-09 15:07:29 +00:00
|
|
|
QQmlPropertyData *result = QQmlPropertyCache::property(engine->jsEngine(), object, name, qmlContext, local);
|
2013-06-06 14:11:23 +00:00
|
|
|
if (!result)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (revisionMode == QV4::QObjectWrapper::CheckRevision && result->hasRevision()) {
|
|
|
|
QQmlData *ddata = QQmlData::get(object);
|
|
|
|
if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-06-15 14:09:39 +00:00
|
|
|
setProperty(engine, object, result, value);
|
2013-10-28 14:18:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-06-15 14:09:39 +00:00
|
|
|
void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value)
|
2013-10-28 14:18:31 +00:00
|
|
|
{
|
|
|
|
if (!property->isWritable() && !property->isQList()) {
|
2013-06-06 14:11:23 +00:00
|
|
|
QString error = QLatin1String("Cannot assign to read-only property \"") +
|
2013-10-28 14:18:31 +00:00
|
|
|
property->name(object) + QLatin1Char('\"');
|
2015-06-15 14:09:39 +00:00
|
|
|
engine->throwTypeError(error);
|
2013-10-28 14:18:31 +00:00
|
|
|
return;
|
2013-06-06 14:11:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QQmlBinding *newBinding = 0;
|
2015-06-15 14:09:39 +00:00
|
|
|
QV4::Scope scope(engine);
|
2013-09-23 13:52:10 +00:00
|
|
|
QV4::ScopedFunctionObject f(scope, value);
|
|
|
|
if (f) {
|
2015-01-09 11:11:09 +00:00
|
|
|
if (!f->isBinding()) {
|
2016-08-04 10:38:43 +00:00
|
|
|
if (!property->isVarProperty() && property->propType() != qMetaTypeId<QJSValue>()) {
|
2013-06-06 14:11:23 +00:00
|
|
|
// assigning a JS function to a non var or QJSValue property or is not allowed.
|
|
|
|
QString error = QLatin1String("Cannot assign JavaScript function to ");
|
2016-08-04 10:38:43 +00:00
|
|
|
if (!QMetaType::typeName(property->propType()))
|
2013-06-06 14:11:23 +00:00
|
|
|
error += QLatin1String("[unknown property type]");
|
|
|
|
else
|
2016-08-04 10:38:43 +00:00
|
|
|
error += QLatin1String(QMetaType::typeName(property->propType()));
|
2015-06-15 14:09:39 +00:00
|
|
|
scope.engine->throwError(error);
|
2013-10-28 14:18:31 +00:00
|
|
|
return;
|
2013-06-06 14:11:23 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// binding assignment.
|
2015-06-15 14:41:24 +00:00
|
|
|
QQmlContextData *callingQmlContext = scope.engine->callingQmlContext();
|
2013-06-06 14:11:23 +00:00
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
|
2014-03-04 03:53:52 +00:00
|
|
|
bindingFunction->initBindingLocation();
|
2013-06-06 14:11:23 +00:00
|
|
|
|
2016-04-08 13:20:23 +00:00
|
|
|
newBinding = QQmlBinding::create(property, value, object, callingQmlContext);
|
2016-08-03 14:17:19 +00:00
|
|
|
newBinding->setTarget(object, *property, nullptr);
|
2013-06-06 14:11:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-15 12:26:50 +00:00
|
|
|
if (newBinding)
|
|
|
|
QQmlPropertyPrivate::setBinding(newBinding);
|
|
|
|
else
|
2016-08-04 10:38:43 +00:00
|
|
|
QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
|
2013-06-06 14:11:23 +00:00
|
|
|
|
2013-10-28 14:18:31 +00:00
|
|
|
if (!newBinding && property->isVarProperty()) {
|
2013-06-06 14:11:23 +00:00
|
|
|
// allow assignment of "special" values (null, undefined, function) to var properties
|
|
|
|
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
|
|
|
|
Q_ASSERT(vmemo);
|
2016-08-04 10:38:43 +00:00
|
|
|
vmemo->setVMEProperty(property->coreIndex(), value);
|
2013-10-28 14:18:31 +00:00
|
|
|
return;
|
2013-06-06 14:11:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define PROPERTY_STORE(cpptype, value) \
|
|
|
|
cpptype o = value; \
|
|
|
|
int status = -1; \
|
|
|
|
int flags = 0; \
|
|
|
|
void *argv[] = { &o, 0, &status, &flags }; \
|
2016-08-04 10:38:43 +00:00
|
|
|
QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex(), argv);
|
2013-06-06 14:11:23 +00:00
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
if (value.isNull() && property->isQObject()) {
|
2013-06-06 14:11:23 +00:00
|
|
|
PROPERTY_STORE(QObject*, 0);
|
2015-01-15 10:36:57 +00:00
|
|
|
} else if (value.isUndefined() && property->isResettable()) {
|
2013-06-06 14:11:23 +00:00
|
|
|
void *a[] = { 0 };
|
2016-08-04 10:38:43 +00:00
|
|
|
QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex(), a);
|
|
|
|
} else if (value.isUndefined() && property->propType() == qMetaTypeId<QVariant>()) {
|
2013-06-06 14:11:23 +00:00
|
|
|
PROPERTY_STORE(QVariant, QVariant());
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (value.isUndefined() && property->propType() == QMetaType::QJsonValue) {
|
2013-06-06 14:11:23 +00:00
|
|
|
PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (!newBinding && property->propType() == qMetaTypeId<QJSValue>()) {
|
2015-06-15 14:09:39 +00:00
|
|
|
PROPERTY_STORE(QJSValue, QJSValue(scope.engine, value.asReturnedValue()));
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (value.isUndefined() && property->propType() != qMetaTypeId<QQmlScriptString>()) {
|
2013-06-06 14:11:23 +00:00
|
|
|
QString error = QLatin1String("Cannot assign [undefined] to ");
|
2016-08-04 10:38:43 +00:00
|
|
|
if (!QMetaType::typeName(property->propType()))
|
2013-06-06 14:11:23 +00:00
|
|
|
error += QLatin1String("[unknown property type]");
|
|
|
|
else
|
2016-08-04 10:38:43 +00:00
|
|
|
error += QLatin1String(QMetaType::typeName(property->propType()));
|
2015-06-15 14:09:39 +00:00
|
|
|
scope.engine->throwError(error);
|
2013-10-28 14:18:31 +00:00
|
|
|
return;
|
2015-02-13 12:56:05 +00:00
|
|
|
} else if (value.as<FunctionObject>()) {
|
2013-06-06 14:11:23 +00:00
|
|
|
// this is handled by the binding creation above
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property->propType() == QMetaType::Int && value.isNumber()) {
|
2015-01-15 10:36:57 +00:00
|
|
|
PROPERTY_STORE(int, value.asDouble());
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property->propType() == QMetaType::QReal && value.isNumber()) {
|
2015-01-15 10:36:57 +00:00
|
|
|
PROPERTY_STORE(qreal, qreal(value.asDouble()));
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property->propType() == QMetaType::Float && value.isNumber()) {
|
2015-01-15 10:36:57 +00:00
|
|
|
PROPERTY_STORE(float, float(value.asDouble()));
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property->propType() == QMetaType::Double && value.isNumber()) {
|
2015-01-15 10:36:57 +00:00
|
|
|
PROPERTY_STORE(double, double(value.asDouble()));
|
2016-08-04 10:38:43 +00:00
|
|
|
} else if (property->propType() == QMetaType::QString && value.isString()) {
|
2015-01-15 10:36:57 +00:00
|
|
|
PROPERTY_STORE(QString, value.toQStringNoThrow());
|
2013-10-28 14:18:31 +00:00
|
|
|
} else if (property->isVarProperty()) {
|
2013-06-06 14:11:23 +00:00
|
|
|
QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
|
|
|
|
Q_ASSERT(vmemo);
|
2016-08-04 10:38:43 +00:00
|
|
|
vmemo->setVMEProperty(property->coreIndex(), value);
|
|
|
|
} else if (property->propType() == qMetaTypeId<QQmlScriptString>() && (value.isUndefined() || value.isPrimitive())) {
|
2015-01-15 10:36:57 +00:00
|
|
|
QQmlScriptString ss(value.toQStringNoThrow(), 0 /* context */, object);
|
|
|
|
if (value.isNumber()) {
|
|
|
|
ss.d->numberValue = value.toNumber();
|
2014-08-12 11:52:50 +00:00
|
|
|
ss.d->isNumberLiteral = true;
|
2015-01-15 10:36:57 +00:00
|
|
|
} else if (value.isString()) {
|
2014-08-12 11:52:50 +00:00
|
|
|
ss.d->script = QV4::CompiledData::Binding::escapedString(ss.d->script);
|
|
|
|
ss.d->isStringLiteral = true;
|
|
|
|
}
|
|
|
|
PROPERTY_STORE(QQmlScriptString, ss);
|
2013-06-06 14:11:23 +00:00
|
|
|
} else {
|
|
|
|
QVariant v;
|
2013-10-28 14:18:31 +00:00
|
|
|
if (property->isQList())
|
2015-06-15 14:09:39 +00:00
|
|
|
v = scope.engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
|
2013-06-06 14:11:23 +00:00
|
|
|
else
|
2016-08-04 10:38:43 +00:00
|
|
|
v = scope.engine->toVariant(value, property->propType());
|
2013-06-06 14:11:23 +00:00
|
|
|
|
2015-06-15 14:41:24 +00:00
|
|
|
QQmlContextData *callingQmlContext = scope.engine->callingQmlContext();
|
2013-10-28 14:18:31 +00:00
|
|
|
if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) {
|
2013-06-06 14:11:23 +00:00
|
|
|
const char *valueType = 0;
|
|
|
|
if (v.userType() == QVariant::Invalid) valueType = "null";
|
|
|
|
else valueType = QMetaType::typeName(v.userType());
|
|
|
|
|
2016-08-04 10:38:43 +00:00
|
|
|
const char *targetTypeName = QMetaType::typeName(property->propType());
|
2013-06-06 14:11:23 +00:00
|
|
|
if (!targetTypeName)
|
|
|
|
targetTypeName = "an unregistered type";
|
|
|
|
|
|
|
|
QString error = QLatin1String("Cannot assign ") +
|
|
|
|
QLatin1String(valueType) +
|
|
|
|
QLatin1String(" to ") +
|
|
|
|
QLatin1String(targetTypeName);
|
2015-06-15 14:09:39 +00:00
|
|
|
scope.engine->throwError(error);
|
2013-10-28 14:18:31 +00:00
|
|
|
return;
|
2013-06-06 14:11:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-30 12:01:40 +00:00
|
|
|
ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *object)
|
2013-06-04 14:35:00 +00:00
|
|
|
{
|
|
|
|
if (QQmlData::wasDeleted(object))
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::null();
|
2013-06-04 14:35:00 +00:00
|
|
|
|
|
|
|
QQmlData *ddata = QQmlData::get(object, true);
|
|
|
|
if (!ddata)
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::undefined();
|
|
|
|
|
|
|
|
Scope scope(engine);
|
2013-06-04 14:35:00 +00:00
|
|
|
|
2016-03-30 12:01:40 +00:00
|
|
|
if (ddata->jsWrapper.isUndefined() &&
|
2013-06-04 14:35:00 +00:00
|
|
|
(ddata->jsEngineId == engine->m_engineId || // We own the QObject
|
|
|
|
ddata->jsEngineId == 0 || // No one owns the QObject
|
2015-04-28 13:38:09 +00:00
|
|
|
!ddata->hasTaintedV4Object)) { // Someone else has used the QObject, but it isn't tainted
|
2013-06-04 14:35:00 +00:00
|
|
|
|
2013-11-14 23:06:18 +00:00
|
|
|
QV4::ScopedValue rv(scope, create(engine, object));
|
2015-01-13 08:01:29 +00:00
|
|
|
ddata->jsWrapper.set(scope.engine, rv);
|
2013-06-04 14:35:00 +00:00
|
|
|
ddata->jsEngineId = engine->m_engineId;
|
2015-01-15 20:28:01 +00:00
|
|
|
return rv->asReturnedValue();
|
2013-06-04 14:35:00 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
// If this object is tainted, we have to check to see if it is in our
|
|
|
|
// tainted object list
|
2014-12-31 18:37:47 +00:00
|
|
|
ScopedObject alternateWrapper(scope, (Object *)0);
|
2015-04-28 13:38:09 +00:00
|
|
|
if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
|
2013-09-26 20:07:27 +00:00
|
|
|
alternateWrapper = engine->m_multiplyWrappedQObjects->value(object);
|
2013-06-04 14:35:00 +00:00
|
|
|
|
|
|
|
// If our tainted handle doesn't exist or has been collected, and there isn't
|
2015-04-28 13:38:09 +00:00
|
|
|
// a handle in the ddata, we can assume ownership of the ddata->jsWrapper
|
2013-09-17 16:16:35 +00:00
|
|
|
if (ddata->jsWrapper.isUndefined() && !alternateWrapper) {
|
2013-11-14 23:06:18 +00:00
|
|
|
QV4::ScopedValue result(scope, create(engine, object));
|
2015-01-13 08:01:29 +00:00
|
|
|
ddata->jsWrapper.set(scope.engine, result);
|
2013-06-04 14:35:00 +00:00
|
|
|
ddata->jsEngineId = engine->m_engineId;
|
2015-01-15 20:28:01 +00:00
|
|
|
return result->asReturnedValue();
|
2013-06-04 14:35:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!alternateWrapper) {
|
2013-11-14 23:06:18 +00:00
|
|
|
alternateWrapper = create(engine, object);
|
2013-06-04 14:35:00 +00:00
|
|
|
if (!engine->m_multiplyWrappedQObjects)
|
|
|
|
engine->m_multiplyWrappedQObjects = new MultiplyWrappedQObjectMap;
|
2015-04-28 13:38:09 +00:00
|
|
|
engine->m_multiplyWrappedQObjects->insert(object, alternateWrapper->d());
|
|
|
|
ddata->hasTaintedV4Object = true;
|
2013-06-04 14:35:00 +00:00
|
|
|
}
|
|
|
|
|
2013-09-12 20:37:41 +00:00
|
|
|
return alternateWrapper.asReturnedValue();
|
2013-06-04 14:35:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-07 14:22:24 +00:00
|
|
|
void QObjectWrapper::markWrapper(QObject *object, ExecutionEngine *engine)
|
|
|
|
{
|
|
|
|
if (QQmlData::wasDeleted(object))
|
|
|
|
return;
|
|
|
|
|
|
|
|
QQmlData *ddata = QQmlData::get(object);
|
|
|
|
if (!ddata)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ddata->jsEngineId == engine->m_engineId)
|
|
|
|
ddata->jsWrapper.markOnce(engine);
|
|
|
|
else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object)
|
|
|
|
engine->m_multiplyWrappedQObjects->mark(object, engine);
|
|
|
|
}
|
|
|
|
|
2015-06-15 14:09:39 +00:00
|
|
|
ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired)
|
2013-10-24 12:51:02 +00:00
|
|
|
{
|
2013-11-14 23:06:18 +00:00
|
|
|
if (QQmlData::wasDeleted(object))
|
2013-10-24 12:51:02 +00:00
|
|
|
return QV4::Encode::null();
|
2013-11-14 23:06:18 +00:00
|
|
|
QQmlData *ddata = QQmlData::get(object, /*create*/false);
|
2013-10-24 12:51:02 +00:00
|
|
|
if (!ddata)
|
|
|
|
return QV4::Encode::undefined();
|
|
|
|
|
|
|
|
QQmlPropertyCache *cache = ddata->propertyCache;
|
|
|
|
Q_ASSERT(cache);
|
|
|
|
QQmlPropertyData *property = cache->property(propertyIndex);
|
|
|
|
Q_ASSERT(property); // We resolved this property earlier, so it better exist!
|
2015-06-15 14:09:39 +00:00
|
|
|
return getProperty(engine, object, property, captureRequired);
|
2013-10-24 12:51:02 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 14:09:39 +00:00
|
|
|
void QObjectWrapper::setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value)
|
2013-10-28 14:18:31 +00:00
|
|
|
{
|
2016-09-07 11:31:14 +00:00
|
|
|
setProperty(engine, d()->object(), propertyIndex, value);
|
2015-06-19 12:18:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value)
|
|
|
|
{
|
2016-08-03 14:17:19 +00:00
|
|
|
Q_ASSERT(propertyIndex < 0xffff);
|
|
|
|
Q_ASSERT(propertyIndex >= 0);
|
|
|
|
|
2015-06-19 12:18:13 +00:00
|
|
|
if (QQmlData::wasDeleted(object))
|
2013-10-28 14:18:31 +00:00
|
|
|
return;
|
2015-06-19 12:18:13 +00:00
|
|
|
QQmlData *ddata = QQmlData::get(object, /*create*/false);
|
2013-10-28 14:18:31 +00:00
|
|
|
if (!ddata)
|
|
|
|
return;
|
|
|
|
|
|
|
|
QQmlPropertyCache *cache = ddata->propertyCache;
|
|
|
|
Q_ASSERT(cache);
|
|
|
|
QQmlPropertyData *property = cache->property(propertyIndex);
|
|
|
|
Q_ASSERT(property); // We resolved this property earlier, so it better exist!
|
2015-06-19 12:18:13 +00:00
|
|
|
return setProperty(engine, object, property, value);
|
2013-10-28 14:18:31 +00:00
|
|
|
}
|
|
|
|
|
2013-11-13 12:31:46 +00:00
|
|
|
bool QObjectWrapper::isEqualTo(Managed *a, Managed *b)
|
|
|
|
{
|
2014-10-30 21:30:01 +00:00
|
|
|
Q_ASSERT(a->as<QV4::QObjectWrapper>());
|
|
|
|
QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a);
|
2015-02-13 12:39:20 +00:00
|
|
|
QV4::Object *o = b->as<Object>();
|
2014-10-30 21:30:01 +00:00
|
|
|
if (o) {
|
|
|
|
if (QV4::QmlTypeWrapper *qmlTypeWrapper = o->as<QV4::QmlTypeWrapper>())
|
|
|
|
return qmlTypeWrapper->toVariant().value<QObject*>() == qobjectWrapper->object();
|
|
|
|
}
|
2013-11-13 12:31:46 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-11-14 23:06:18 +00:00
|
|
|
ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
|
2013-06-04 10:02:29 +00:00
|
|
|
{
|
2016-05-02 14:55:36 +00:00
|
|
|
if (QJSEngine *jsEngine = engine->jsEngine()) {
|
|
|
|
if (QQmlPropertyCache *cache = QQmlData::ensurePropertyCache(jsEngine, object)) {
|
|
|
|
ReturnedValue result = QV4::Encode::null();
|
|
|
|
void *args[] = { &result, &engine };
|
|
|
|
if (cache->callJSFactoryMethod(object, args))
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
2015-08-28 11:33:10 +00:00
|
|
|
return (engine->memoryManager->allocObject<QV4::QObjectWrapper>(object))->asReturnedValue();
|
2013-06-04 10:02:29 +00:00
|
|
|
}
|
|
|
|
|
2015-02-13 09:42:01 +00:00
|
|
|
QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *hasProperty)
|
2013-06-03 14:29:16 +00:00
|
|
|
{
|
2015-02-13 09:42:01 +00:00
|
|
|
const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
|
2015-06-15 14:41:24 +00:00
|
|
|
QQmlContextData *qmlContext = that->engine()->callingQmlContext();
|
2014-11-13 10:41:52 +00:00
|
|
|
return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true);
|
2013-05-22 14:51:34 +00:00
|
|
|
}
|
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
void QObjectWrapper::put(Managed *m, String *name, const Value &value)
|
2013-05-22 14:51:34 +00:00
|
|
|
{
|
|
|
|
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
|
2015-01-11 15:30:29 +00:00
|
|
|
ExecutionEngine *v4 = that->engine();
|
2013-05-22 14:51:34 +00:00
|
|
|
|
2016-09-07 11:31:14 +00:00
|
|
|
if (v4->hasException || QQmlData::wasDeleted(that->d()->object()))
|
2013-05-22 14:51:34 +00:00
|
|
|
return;
|
|
|
|
|
2015-06-15 14:41:24 +00:00
|
|
|
QQmlContextData *qmlContext = v4->callingQmlContext();
|
2016-09-07 11:31:14 +00:00
|
|
|
if (!setQmlProperty(v4, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) {
|
|
|
|
QQmlData *ddata = QQmlData::get(that->d()->object());
|
2014-04-09 10:17:40 +00:00
|
|
|
// Types created by QML are not extensible at run-time, but for other QObjects we can store them
|
|
|
|
// as regular JavaScript properties, like on JavaScript objects.
|
2014-04-10 10:58:15 +00:00
|
|
|
if (ddata && ddata->context) {
|
2014-04-09 10:17:40 +00:00
|
|
|
QString error = QLatin1String("Cannot assign to non-existent property \"") +
|
|
|
|
name->toQString() + QLatin1Char('\"');
|
2014-07-28 08:07:57 +00:00
|
|
|
v4->throwError(error);
|
2014-04-09 10:17:40 +00:00
|
|
|
} else {
|
|
|
|
QV4::Object::put(m, name, value);
|
|
|
|
}
|
2013-05-22 14:51:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-07 11:33:24 +00:00
|
|
|
PropertyAttributes QObjectWrapper::query(const Managed *m, String *name)
|
2013-06-12 05:27:51 +00:00
|
|
|
{
|
|
|
|
const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
|
|
|
|
ExecutionEngine *engine = that->engine();
|
2015-06-15 14:41:24 +00:00
|
|
|
QQmlContextData *qmlContext = engine->callingQmlContext();
|
2013-06-12 05:27:51 +00:00
|
|
|
QQmlPropertyData local;
|
|
|
|
if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local)
|
2015-04-26 07:22:17 +00:00
|
|
|
|| name->equals(engine->id_destroy()) || name->equals(engine->id_toString()))
|
2013-06-12 05:27:51 +00:00
|
|
|
return QV4::Attr_Data;
|
|
|
|
else
|
|
|
|
return QV4::Object::query(m, name);
|
|
|
|
}
|
|
|
|
|
Remove type punning from QV4::Value.
The union in QV4::Value is used to do type punning. In C++, this is
compiler-defined behavior. For example, Clang and GCC will try to detect
it and try to do the proper thing. However, it can play havoc with Alias
Analysis, and it is not guaranteed that some Undefined Behavior (or
Compiler depenedent behavior) might occur.
The really problematic part is the struct inside the union: depending on
the calling convention and the register size, it results in some
exciting code. For example, the AMD64 ABI specifies that a struct of two
values of INTEGER class can be passed in separate registers when doing a
function call. Now, if the AA in the compiler looses track of the fact
that the tag overlaps with the double, you might get:
ecx := someTag
... conditional jumps
double_case:
rdx := xorredDoubleValue
callq someWhere
If the someWhere function checks for the tag first, mayhem ensues: the
double value in rdx does not overwrite the tag that is passed in ecx.
Changing the code to do reinterpret_cast<>s might also give problems
on 32bit architectures, because there is a double, whose size is not the
same as the size of the tag, which could confuse AA.
So, to fix this, the following is changed:
- only have a quint64 field in the QV4::Value, which has the added
benefit that it's very clear for the compiler that it's a POD
- as memcpy is the only approved way to ensure bit-by-bit "conversion"
between types (esp. FP<->non-FP types), change all conversions to use
memcpy. Use bitops (shift/and/or) for anything else.
- only use accessor functions for non-quint64 values
As any modern compiler has memcpy as an intrinsic, the call will be
replaced with one or a few move instructions. The accessor functions
also get inlined, the bitops get optimized, so in all cases the compiler
can generate the most compact code possible.
This patch obsoletes f558bc48585c69de36151248c969a484a969ebb4 (which had
the exact aliassing problem of the double and the tag as described
above).
Change-Id: I60a39d8564be5ce6106403a56a8de90943217006
Reviewed-by: Ulf Hermann <ulf.hermann@theqtcompany.com>
2015-07-08 08:52:59 +00:00
|
|
|
void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
|
2013-05-22 14:51:34 +00:00
|
|
|
{
|
2014-06-24 10:05:35 +00:00
|
|
|
// Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
|
|
|
|
static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
|
|
|
|
static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
|
|
|
|
static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
|
|
|
|
|
Remove type punning from QV4::Value.
The union in QV4::Value is used to do type punning. In C++, this is
compiler-defined behavior. For example, Clang and GCC will try to detect
it and try to do the proper thing. However, it can play havoc with Alias
Analysis, and it is not guaranteed that some Undefined Behavior (or
Compiler depenedent behavior) might occur.
The really problematic part is the struct inside the union: depending on
the calling convention and the register size, it results in some
exciting code. For example, the AMD64 ABI specifies that a struct of two
values of INTEGER class can be passed in separate registers when doing a
function call. Now, if the AA in the compiler looses track of the fact
that the tag overlaps with the double, you might get:
ecx := someTag
... conditional jumps
double_case:
rdx := xorredDoubleValue
callq someWhere
If the someWhere function checks for the tag first, mayhem ensues: the
double value in rdx does not overwrite the tag that is passed in ecx.
Changing the code to do reinterpret_cast<>s might also give problems
on 32bit architectures, because there is a double, whose size is not the
same as the size of the tag, which could confuse AA.
So, to fix this, the following is changed:
- only have a quint64 field in the QV4::Value, which has the added
benefit that it's very clear for the compiler that it's a POD
- as memcpy is the only approved way to ensure bit-by-bit "conversion"
between types (esp. FP<->non-FP types), change all conversions to use
memcpy. Use bitops (shift/and/or) for anything else.
- only use accessor functions for non-quint64 values
As any modern compiler has memcpy as an intrinsic, the call will be
replaced with one or a few move instructions. The accessor functions
also get inlined, the bitops get optimized, so in all cases the compiler
can generate the most compact code possible.
This patch obsoletes f558bc48585c69de36151248c969a484a969ebb4 (which had
the exact aliassing problem of the double and the tag as described
above).
Change-Id: I60a39d8564be5ce6106403a56a8de90943217006
Reviewed-by: Ulf Hermann <ulf.hermann@theqtcompany.com>
2015-07-08 08:52:59 +00:00
|
|
|
name->setM(0);
|
2013-06-12 07:19:37 +00:00
|
|
|
*index = UINT_MAX;
|
2013-05-22 14:51:34 +00:00
|
|
|
|
2013-06-12 07:19:37 +00:00
|
|
|
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
|
2013-05-22 14:51:34 +00:00
|
|
|
|
2016-09-07 11:31:14 +00:00
|
|
|
if (that->d()->object()) {
|
|
|
|
const QMetaObject *mo = that->d()->object()->metaObject();
|
2014-11-19 15:56:40 +00:00
|
|
|
// These indices don't apply to gadgets, so don't block them.
|
|
|
|
const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
|
2014-01-08 13:51:33 +00:00
|
|
|
const int propertyCount = mo->propertyCount();
|
|
|
|
if (it->arrayIndex < static_cast<uint>(propertyCount)) {
|
2014-11-11 14:08:30 +00:00
|
|
|
Scope scope(that->engine());
|
|
|
|
ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name())));
|
Remove type punning from QV4::Value.
The union in QV4::Value is used to do type punning. In C++, this is
compiler-defined behavior. For example, Clang and GCC will try to detect
it and try to do the proper thing. However, it can play havoc with Alias
Analysis, and it is not guaranteed that some Undefined Behavior (or
Compiler depenedent behavior) might occur.
The really problematic part is the struct inside the union: depending on
the calling convention and the register size, it results in some
exciting code. For example, the AMD64 ABI specifies that a struct of two
values of INTEGER class can be passed in separate registers when doing a
function call. Now, if the AA in the compiler looses track of the fact
that the tag overlaps with the double, you might get:
ecx := someTag
... conditional jumps
double_case:
rdx := xorredDoubleValue
callq someWhere
If the someWhere function checks for the tag first, mayhem ensues: the
double value in rdx does not overwrite the tag that is passed in ecx.
Changing the code to do reinterpret_cast<>s might also give problems
on 32bit architectures, because there is a double, whose size is not the
same as the size of the tag, which could confuse AA.
So, to fix this, the following is changed:
- only have a quint64 field in the QV4::Value, which has the added
benefit that it's very clear for the compiler that it's a POD
- as memcpy is the only approved way to ensure bit-by-bit "conversion"
between types (esp. FP<->non-FP types), change all conversions to use
memcpy. Use bitops (shift/and/or) for anything else.
- only use accessor functions for non-quint64 values
As any modern compiler has memcpy as an intrinsic, the call will be
replaced with one or a few move instructions. The accessor functions
also get inlined, the bitops get optimized, so in all cases the compiler
can generate the most compact code possible.
This patch obsoletes f558bc48585c69de36151248c969a484a969ebb4 (which had
the exact aliassing problem of the double and the tag as described
above).
Change-Id: I60a39d8564be5ce6106403a56a8de90943217006
Reviewed-by: Ulf Hermann <ulf.hermann@theqtcompany.com>
2015-07-08 08:52:59 +00:00
|
|
|
name->setM(propName->d());
|
2014-01-08 13:51:33 +00:00
|
|
|
++it->arrayIndex;
|
2013-06-12 07:19:37 +00:00
|
|
|
*attributes = QV4::Attr_Data;
|
2014-11-28 12:25:56 +00:00
|
|
|
p->value = that->get(propName);
|
2014-01-08 13:51:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
const int methodCount = mo->methodCount();
|
2014-06-24 10:05:35 +00:00
|
|
|
while (it->arrayIndex < static_cast<uint>(propertyCount + methodCount)) {
|
|
|
|
const int index = it->arrayIndex - propertyCount;
|
|
|
|
const QMetaMethod method = mo->method(index);
|
2014-01-08 13:51:33 +00:00
|
|
|
++it->arrayIndex;
|
2014-11-19 15:56:40 +00:00
|
|
|
if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
|
2014-06-24 10:05:35 +00:00
|
|
|
continue;
|
2014-11-11 14:08:30 +00:00
|
|
|
Scope scope(that->engine());
|
|
|
|
ScopedString methodName(scope, that->engine()->newString(QString::fromUtf8(method.name())));
|
Remove type punning from QV4::Value.
The union in QV4::Value is used to do type punning. In C++, this is
compiler-defined behavior. For example, Clang and GCC will try to detect
it and try to do the proper thing. However, it can play havoc with Alias
Analysis, and it is not guaranteed that some Undefined Behavior (or
Compiler depenedent behavior) might occur.
The really problematic part is the struct inside the union: depending on
the calling convention and the register size, it results in some
exciting code. For example, the AMD64 ABI specifies that a struct of two
values of INTEGER class can be passed in separate registers when doing a
function call. Now, if the AA in the compiler looses track of the fact
that the tag overlaps with the double, you might get:
ecx := someTag
... conditional jumps
double_case:
rdx := xorredDoubleValue
callq someWhere
If the someWhere function checks for the tag first, mayhem ensues: the
double value in rdx does not overwrite the tag that is passed in ecx.
Changing the code to do reinterpret_cast<>s might also give problems
on 32bit architectures, because there is a double, whose size is not the
same as the size of the tag, which could confuse AA.
So, to fix this, the following is changed:
- only have a quint64 field in the QV4::Value, which has the added
benefit that it's very clear for the compiler that it's a POD
- as memcpy is the only approved way to ensure bit-by-bit "conversion"
between types (esp. FP<->non-FP types), change all conversions to use
memcpy. Use bitops (shift/and/or) for anything else.
- only use accessor functions for non-quint64 values
As any modern compiler has memcpy as an intrinsic, the call will be
replaced with one or a few move instructions. The accessor functions
also get inlined, the bitops get optimized, so in all cases the compiler
can generate the most compact code possible.
This patch obsoletes f558bc48585c69de36151248c969a484a969ebb4 (which had
the exact aliassing problem of the double and the tag as described
above).
Change-Id: I60a39d8564be5ce6106403a56a8de90943217006
Reviewed-by: Ulf Hermann <ulf.hermann@theqtcompany.com>
2015-07-08 08:52:59 +00:00
|
|
|
name->setM(methodName->d());
|
2013-06-21 14:05:53 +00:00
|
|
|
*attributes = QV4::Attr_Data;
|
2014-11-28 12:25:56 +00:00
|
|
|
p->value = that->get(methodName);
|
2014-01-08 13:51:33 +00:00
|
|
|
return;
|
|
|
|
}
|
2013-06-21 14:05:53 +00:00
|
|
|
}
|
2014-01-08 13:51:33 +00:00
|
|
|
QV4::Object::advanceIterator(m, it, name, index, p, attributes);
|
2013-05-22 14:51:34 +00:00
|
|
|
}
|
|
|
|
|
2013-06-06 12:18:11 +00:00
|
|
|
namespace QV4 {
|
|
|
|
|
|
|
|
struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase
|
|
|
|
{
|
|
|
|
QV4::PersistentValue function;
|
|
|
|
QV4::PersistentValue thisObject;
|
|
|
|
int signalIndex;
|
|
|
|
|
|
|
|
QObjectSlotDispatcher()
|
|
|
|
: QtPrivate::QSlotObjectBase(&impl)
|
|
|
|
, signalIndex(-1)
|
|
|
|
{}
|
|
|
|
|
|
|
|
static void impl(int which, QSlotObjectBase *this_, QObject *r, void **metaArgs, bool *ret)
|
|
|
|
{
|
|
|
|
switch (which) {
|
|
|
|
case Destroy: {
|
|
|
|
delete static_cast<QObjectSlotDispatcher*>(this_);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Call: {
|
|
|
|
QObjectSlotDispatcher *This = static_cast<QObjectSlotDispatcher*>(this_);
|
2014-03-12 08:31:38 +00:00
|
|
|
QV4::ExecutionEngine *v4 = This->function.engine();
|
|
|
|
// Might be that we're still connected to a signal that's emitted long
|
|
|
|
// after the engine died. We don't track connections in a global list, so
|
|
|
|
// we need this safeguard.
|
|
|
|
if (!v4)
|
|
|
|
break;
|
|
|
|
|
2016-07-05 08:35:56 +00:00
|
|
|
QQmlMetaObject::ArgTypeStorage storage;
|
|
|
|
int *argsTypes = QQmlMetaObject(r).methodParameterTypes(This->signalIndex, &storage, 0);
|
2013-06-06 12:18:11 +00:00
|
|
|
|
|
|
|
int argCount = argsTypes ? argsTypes[0]:0;
|
|
|
|
|
2013-09-23 13:52:10 +00:00
|
|
|
QV4::Scope scope(v4);
|
|
|
|
QV4::ScopedFunctionObject f(scope, This->function.value());
|
2013-06-06 12:18:11 +00:00
|
|
|
|
2013-09-11 12:36:01 +00:00
|
|
|
QV4::ScopedCallData callData(scope, argCount);
|
2015-03-13 16:21:18 +00:00
|
|
|
callData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value();
|
2013-06-06 12:18:11 +00:00
|
|
|
for (int ii = 0; ii < argCount; ++ii) {
|
|
|
|
int type = argsTypes[ii + 1];
|
|
|
|
if (type == qMetaTypeId<QVariant>()) {
|
2015-01-02 14:07:35 +00:00
|
|
|
callData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1]));
|
2013-06-06 12:18:11 +00:00
|
|
|
} else {
|
2015-01-02 14:07:35 +00:00
|
|
|
callData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1]));
|
2013-06-06 12:18:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-22 08:12:13 +00:00
|
|
|
f->call(scope, callData);
|
2014-12-30 15:04:32 +00:00
|
|
|
if (scope.hasException()) {
|
2015-01-14 11:00:56 +00:00
|
|
|
QQmlError error = v4->catchExceptionAsQmlError();
|
2015-01-05 14:31:20 +00:00
|
|
|
if (error.description().isEmpty()) {
|
|
|
|
QV4::ScopedString name(scope, f->name());
|
2015-08-12 12:17:58 +00:00
|
|
|
error.setDescription(QStringLiteral("Unknown exception occurred during evaluation of connected function: %1").arg(name->toQString()));
|
2015-01-05 14:31:20 +00:00
|
|
|
}
|
2014-12-30 15:38:20 +00:00
|
|
|
if (QQmlEngine *qmlEngine = v4->qmlEngine()) {
|
2014-02-07 11:23:17 +00:00
|
|
|
QQmlEnginePrivate::get(qmlEngine)->warning(error);
|
2015-01-05 14:31:20 +00:00
|
|
|
} else {
|
|
|
|
QMessageLogger(error.url().toString().toLatin1().constData(),
|
|
|
|
error.line(), 0).warning().noquote()
|
|
|
|
<< error.toString();
|
2014-02-07 11:23:17 +00:00
|
|
|
}
|
2013-06-06 12:18:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Compare: {
|
|
|
|
QObjectSlotDispatcher *connection = static_cast<QObjectSlotDispatcher*>(this_);
|
2013-09-17 16:16:35 +00:00
|
|
|
if (connection->function.isUndefined()) {
|
2013-06-06 12:18:11 +00:00
|
|
|
*ret = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is tricky. Normally the metaArgs[0] pointer is a pointer to the _function_
|
|
|
|
// for the new-style QObject::connect. Here we use the engine pointer as sentinel
|
|
|
|
// to distinguish those type of QSlotObjectBase connections from our QML connections.
|
|
|
|
QV4::ExecutionEngine *v4 = reinterpret_cast<QV4::ExecutionEngine*>(metaArgs[0]);
|
|
|
|
if (v4 != connection->function.engine()) {
|
|
|
|
*ret = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-11 12:47:34 +00:00
|
|
|
QV4::Scope scope(v4);
|
2013-09-09 11:38:10 +00:00
|
|
|
QV4::ScopedValue function(scope, *reinterpret_cast<QV4::Value*>(metaArgs[1]));
|
|
|
|
QV4::ScopedValue thisObject(scope, *reinterpret_cast<QV4::Value*>(metaArgs[2]));
|
2013-06-06 12:18:11 +00:00
|
|
|
QObject *receiverToDisconnect = reinterpret_cast<QObject*>(metaArgs[3]);
|
|
|
|
int slotIndexToDisconnect = *reinterpret_cast<int*>(metaArgs[4]);
|
|
|
|
|
|
|
|
if (slotIndexToDisconnect != -1) {
|
|
|
|
// This is a QObject function wrapper
|
2013-09-17 16:16:35 +00:00
|
|
|
if (connection->thisObject.isUndefined() == thisObject->isUndefined() &&
|
2015-01-15 10:36:57 +00:00
|
|
|
(connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
|
2013-06-06 12:18:11 +00:00
|
|
|
|
2013-09-23 13:52:10 +00:00
|
|
|
QV4::ScopedFunctionObject f(scope, connection->function.value());
|
2016-02-04 04:23:10 +00:00
|
|
|
QPair<QObject *, int> connectedFunctionData = QObjectMethod::extractQtMethod(f);
|
2013-06-06 12:18:11 +00:00
|
|
|
if (connectedFunctionData.first == receiverToDisconnect &&
|
|
|
|
connectedFunctionData.second == slotIndexToDisconnect) {
|
|
|
|
*ret = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This is a normal JS function
|
2015-01-15 10:36:57 +00:00
|
|
|
if (RuntimeHelpers::strictEqual(*connection->function.valueRef(), function) &&
|
2013-09-17 16:16:35 +00:00
|
|
|
connection->thisObject.isUndefined() == thisObject->isUndefined() &&
|
2015-01-15 10:36:57 +00:00
|
|
|
(connection->thisObject.isUndefined() || RuntimeHelpers::strictEqual(*connection->thisObject.valueRef(), thisObject))) {
|
2013-06-06 12:18:11 +00:00
|
|
|
*ret = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NumOperations:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace QV4
|
|
|
|
|
2013-11-03 14:23:05 +00:00
|
|
|
ReturnedValue QObjectWrapper::method_connect(CallContext *ctx)
|
2013-06-06 12:18:11 +00:00
|
|
|
{
|
2015-01-15 20:54:12 +00:00
|
|
|
if (ctx->argc() == 0)
|
2013-06-06 12:18:11 +00:00
|
|
|
V4THROW_ERROR("Function.prototype.connect: no arguments given");
|
|
|
|
|
2015-01-15 20:54:12 +00:00
|
|
|
QPair<QObject *, int> signalInfo = extractQtSignal(ctx->thisObject());
|
2013-06-06 12:18:11 +00:00
|
|
|
QObject *signalObject = signalInfo.first;
|
2013-11-28 15:57:41 +00:00
|
|
|
int signalIndex = signalInfo.second; // in method range, not signal range!
|
2013-06-06 12:18:11 +00:00
|
|
|
|
|
|
|
if (signalIndex < 0)
|
|
|
|
V4THROW_ERROR("Function.prototype.connect: this object is not a signal");
|
|
|
|
|
|
|
|
if (!signalObject)
|
|
|
|
V4THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
|
|
|
|
|
|
|
|
if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
|
|
|
|
V4THROW_ERROR("Function.prototype.connect: this object is not a signal");
|
|
|
|
|
2013-09-23 13:52:10 +00:00
|
|
|
QV4::Scope scope(ctx);
|
|
|
|
QV4::ScopedFunctionObject f(scope);
|
|
|
|
QV4::ScopedValue thisObject (scope, QV4::Encode::undefined());
|
2013-06-06 12:18:11 +00:00
|
|
|
|
2015-01-15 20:54:12 +00:00
|
|
|
if (ctx->argc() == 1) {
|
|
|
|
f = ctx->args()[0];
|
|
|
|
} else if (ctx->argc() >= 2) {
|
|
|
|
thisObject = ctx->args()[0];
|
|
|
|
f = ctx->args()[1];
|
2013-06-06 12:18:11 +00:00
|
|
|
}
|
|
|
|
|
2013-09-23 13:52:10 +00:00
|
|
|
if (!f)
|
2013-06-06 12:18:11 +00:00
|
|
|
V4THROW_ERROR("Function.prototype.connect: target is not a function");
|
|
|
|
|
2013-09-23 13:52:10 +00:00
|
|
|
if (!thisObject->isUndefined() && !thisObject->isObject())
|
2013-06-06 12:18:11 +00:00
|
|
|
V4THROW_ERROR("Function.prototype.connect: target this is not an object");
|
|
|
|
|
2013-09-23 13:52:10 +00:00
|
|
|
QV4::QObjectSlotDispatcher *slot = new QV4::QObjectSlotDispatcher;
|
|
|
|
slot->signalIndex = signalIndex;
|
|
|
|
|
2015-01-13 08:01:29 +00:00
|
|
|
slot->thisObject.set(scope.engine, thisObject);
|
|
|
|
slot->function.set(scope.engine, f);
|
2013-09-23 13:52:10 +00:00
|
|
|
|
2013-11-28 15:57:41 +00:00
|
|
|
if (QQmlData *ddata = QQmlData::get(signalObject)) {
|
|
|
|
if (QQmlPropertyCache *propertyCache = ddata->propertyCache) {
|
|
|
|
QQmlPropertyPrivate::flushSignal(signalObject, propertyCache->methodIndexToSignalIndex(signalIndex));
|
|
|
|
}
|
|
|
|
}
|
2013-06-06 12:18:11 +00:00
|
|
|
QObjectPrivate::connect(signalObject, signalIndex, slot, Qt::AutoConnection);
|
|
|
|
|
2013-09-12 09:13:03 +00:00
|
|
|
return Encode::undefined();
|
2013-06-06 12:18:11 +00:00
|
|
|
}
|
|
|
|
|
2013-11-03 14:23:05 +00:00
|
|
|
ReturnedValue QObjectWrapper::method_disconnect(CallContext *ctx)
|
2013-06-06 12:18:11 +00:00
|
|
|
{
|
2015-01-15 20:54:12 +00:00
|
|
|
if (ctx->argc() == 0)
|
2013-06-06 12:18:11 +00:00
|
|
|
V4THROW_ERROR("Function.prototype.disconnect: no arguments given");
|
|
|
|
|
2013-09-26 20:07:27 +00:00
|
|
|
QV4::Scope scope(ctx);
|
|
|
|
|
2015-01-15 20:54:12 +00:00
|
|
|
QPair<QObject *, int> signalInfo = extractQtSignal(ctx->thisObject());
|
2013-06-06 12:18:11 +00:00
|
|
|
QObject *signalObject = signalInfo.first;
|
|
|
|
int signalIndex = signalInfo.second;
|
|
|
|
|
|
|
|
if (signalIndex == -1)
|
|
|
|
V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
|
|
|
|
|
|
|
|
if (!signalObject)
|
|
|
|
V4THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
|
|
|
|
|
|
|
|
if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
|
|
|
|
V4THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
|
|
|
|
|
2013-09-26 20:07:27 +00:00
|
|
|
QV4::ScopedFunctionObject functionValue(scope);
|
|
|
|
QV4::ScopedValue functionThisValue(scope, QV4::Encode::undefined());
|
2013-06-06 12:18:11 +00:00
|
|
|
|
2015-01-15 20:54:12 +00:00
|
|
|
if (ctx->argc() == 1) {
|
|
|
|
functionValue = ctx->args()[0];
|
|
|
|
} else if (ctx->argc() >= 2) {
|
|
|
|
functionThisValue = ctx->args()[0];
|
|
|
|
functionValue = ctx->args()[1];
|
2013-06-06 12:18:11 +00:00
|
|
|
}
|
|
|
|
|
2013-09-26 20:07:27 +00:00
|
|
|
if (!functionValue)
|
2013-06-06 12:18:11 +00:00
|
|
|
V4THROW_ERROR("Function.prototype.disconnect: target is not a function");
|
|
|
|
|
2013-09-26 20:07:27 +00:00
|
|
|
if (!functionThisValue->isUndefined() && !functionThisValue->isObject())
|
2013-06-06 12:18:11 +00:00
|
|
|
V4THROW_ERROR("Function.prototype.disconnect: target this is not an object");
|
|
|
|
|
2016-02-04 04:23:10 +00:00
|
|
|
QPair<QObject *, int> functionData = QObjectMethod::extractQtMethod(functionValue);
|
2013-06-06 12:18:11 +00:00
|
|
|
|
|
|
|
void *a[] = {
|
2014-05-06 07:23:59 +00:00
|
|
|
ctx->d()->engine,
|
2013-09-26 20:07:27 +00:00
|
|
|
functionValue.ptr,
|
|
|
|
functionThisValue.ptr,
|
2013-06-06 12:18:11 +00:00
|
|
|
functionData.first,
|
|
|
|
&functionData.second
|
|
|
|
};
|
|
|
|
|
|
|
|
QObjectPrivate::disconnect(signalObject, signalIndex, reinterpret_cast<void**>(&a));
|
|
|
|
|
2013-09-12 09:13:03 +00:00
|
|
|
return Encode::undefined();
|
2013-06-06 12:18:11 +00:00
|
|
|
}
|
|
|
|
|
2013-11-02 15:30:26 +00:00
|
|
|
static void markChildQObjectsRecursively(QObject *parent, QV4::ExecutionEngine *e)
|
2013-06-14 10:21:45 +00:00
|
|
|
{
|
|
|
|
const QObjectList &children = parent->children();
|
|
|
|
for (int i = 0; i < children.count(); ++i) {
|
|
|
|
QObject *child = children.at(i);
|
2014-06-17 22:47:43 +00:00
|
|
|
if (!child)
|
|
|
|
continue;
|
2015-05-07 14:22:24 +00:00
|
|
|
QObjectWrapper::markWrapper(child, e);
|
2013-11-02 15:30:26 +00:00
|
|
|
markChildQObjectsRecursively(child, e);
|
2013-06-14 10:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-01 22:04:20 +00:00
|
|
|
void QObjectWrapper::markObjects(Heap::Base *that, QV4::ExecutionEngine *e)
|
2013-05-24 15:12:57 +00:00
|
|
|
{
|
2014-11-01 19:56:47 +00:00
|
|
|
QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that);
|
2013-05-24 15:12:57 +00:00
|
|
|
|
2016-09-07 11:31:14 +00:00
|
|
|
if (QObject *o = This->object()) {
|
2013-06-14 10:21:45 +00:00
|
|
|
QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o);
|
|
|
|
if (vme)
|
2013-11-02 15:30:26 +00:00
|
|
|
vme->mark(e);
|
2013-06-14 10:21:45 +00:00
|
|
|
|
|
|
|
// Children usually don't need to be marked, the gc keeps them alive.
|
|
|
|
// But in the rare case of a "floating" QObject without a parent that
|
|
|
|
// _gets_ marked (we've been called here!) then we also need to
|
|
|
|
// propagate the marking down to the children recursively.
|
|
|
|
if (!o->parent())
|
2013-11-02 15:30:26 +00:00
|
|
|
markChildQObjectsRecursively(o, e);
|
2013-06-14 10:21:45 +00:00
|
|
|
}
|
2013-05-24 15:12:57 +00:00
|
|
|
|
2013-11-02 15:30:26 +00:00
|
|
|
QV4::Object::markObjects(that, e);
|
2013-05-24 15:12:57 +00:00
|
|
|
}
|
|
|
|
|
2015-08-07 12:26:43 +00:00
|
|
|
void QObjectWrapper::destroyObject(bool lastCall)
|
2013-06-13 13:27:00 +00:00
|
|
|
{
|
2015-08-07 12:26:43 +00:00
|
|
|
Heap::QObjectWrapper *h = d();
|
|
|
|
if (!h->internalClass)
|
|
|
|
return; // destroyObject already got called
|
|
|
|
|
2016-09-07 11:31:14 +00:00
|
|
|
if (h->object()) {
|
|
|
|
QQmlData *ddata = QQmlData::get(h->object(), false);
|
2015-08-07 12:26:43 +00:00
|
|
|
if (ddata) {
|
2016-09-07 11:31:14 +00:00
|
|
|
if (!h->object()->parent() && !ddata->indestructible) {
|
2015-08-07 12:26:43 +00:00
|
|
|
if (ddata && ddata->ownContext && ddata->context)
|
|
|
|
ddata->context->emitDestruction();
|
|
|
|
// This object is notionally destroyed now
|
|
|
|
ddata->isQueuedForDeletion = true;
|
|
|
|
if (lastCall)
|
2016-09-07 11:31:14 +00:00
|
|
|
delete h->object();
|
2015-08-07 12:26:43 +00:00
|
|
|
else
|
2016-09-07 11:31:14 +00:00
|
|
|
h->object()->deleteLater();
|
2016-10-04 09:42:10 +00:00
|
|
|
} else {
|
|
|
|
// If the object is C++-owned, we still have to release the weak reference we have
|
|
|
|
// to it.
|
|
|
|
ddata->jsWrapper.clear();
|
2015-08-07 12:26:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-06-13 13:27:00 +00:00
|
|
|
|
2015-08-07 12:26:43 +00:00
|
|
|
h->internalClass = 0;
|
|
|
|
h->~Data();
|
2013-06-13 13:27:00 +00:00
|
|
|
}
|
|
|
|
|
2014-03-26 14:11:48 +00:00
|
|
|
|
|
|
|
DEFINE_OBJECT_VTABLE(QObjectWrapper);
|
2013-05-22 14:51:34 +00:00
|
|
|
|
2011-05-11 07:20:40 +00:00
|
|
|
namespace {
|
2011-09-05 13:36:19 +00:00
|
|
|
|
2012-04-24 16:22:06 +00:00
|
|
|
template<typename A, typename B, typename C, typename D, typename E,
|
|
|
|
typename F, typename G, typename H>
|
|
|
|
class MaxSizeOf8 {
|
2011-09-05 13:36:19 +00:00
|
|
|
template<typename Z, typename X>
|
|
|
|
struct SMax {
|
2012-04-24 16:22:06 +00:00
|
|
|
char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
|
2011-09-05 13:36:19 +00:00
|
|
|
};
|
|
|
|
public:
|
2012-04-24 16:22:06 +00:00
|
|
|
static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >);
|
2011-09-05 13:36:19 +00:00
|
|
|
};
|
|
|
|
|
2011-11-28 16:04:33 +00:00
|
|
|
struct CallArgument {
|
|
|
|
inline CallArgument();
|
|
|
|
inline ~CallArgument();
|
2011-05-11 07:20:40 +00:00
|
|
|
inline void *dataPtr();
|
|
|
|
|
|
|
|
inline void initAsType(int type);
|
2015-01-15 10:36:57 +00:00
|
|
|
inline void fromValue(int type, ExecutionEngine *, const Value &);
|
2014-12-30 15:38:20 +00:00
|
|
|
inline ReturnedValue toValue(ExecutionEngine *);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
|
|
|
private:
|
2011-11-28 16:04:33 +00:00
|
|
|
CallArgument(const CallArgument &);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
|
|
|
inline void cleanup();
|
|
|
|
|
2011-07-29 02:28:03 +00:00
|
|
|
union {
|
|
|
|
float floatValue;
|
|
|
|
double doubleValue;
|
|
|
|
quint32 intValue;
|
|
|
|
bool boolValue;
|
|
|
|
QObject *qobjectPtr;
|
|
|
|
|
2012-04-24 16:22:06 +00:00
|
|
|
char allocData[MaxSizeOf8<QVariant,
|
2011-09-05 13:36:19 +00:00
|
|
|
QString,
|
|
|
|
QList<QObject *>,
|
|
|
|
QJSValue,
|
2013-04-19 08:59:41 +00:00
|
|
|
QQmlV4Handle,
|
2012-04-24 16:22:06 +00:00
|
|
|
QJsonArray,
|
|
|
|
QJsonObject,
|
|
|
|
QJsonValue>::Size];
|
2011-09-05 13:36:19 +00:00
|
|
|
qint64 q_for_alignment;
|
2011-07-29 02:28:03 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Pointers to allocData
|
|
|
|
union {
|
|
|
|
QString *qstringPtr;
|
2015-12-28 12:22:29 +00:00
|
|
|
QByteArray *qbyteArrayPtr;
|
2011-07-29 02:28:03 +00:00
|
|
|
QVariant *qvariantPtr;
|
|
|
|
QList<QObject *> *qlistPtr;
|
2011-07-29 08:25:44 +00:00
|
|
|
QJSValue *qjsValuePtr;
|
2013-04-19 08:59:41 +00:00
|
|
|
QQmlV4Handle *handlePtr;
|
2012-04-24 16:22:06 +00:00
|
|
|
QJsonArray *jsonArrayPtr;
|
|
|
|
QJsonObject *jsonObjectPtr;
|
|
|
|
QJsonValue *jsonValuePtr;
|
2011-07-29 02:28:03 +00:00
|
|
|
};
|
|
|
|
|
2011-05-11 07:20:40 +00:00
|
|
|
int type;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2014-11-14 20:53:20 +00:00
|
|
|
static QV4::ReturnedValue CallMethod(const QQmlObjectOrGadget &object, int index, int returnType, int argCount,
|
2016-05-29 19:33:59 +00:00
|
|
|
int *argTypes, QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
|
|
|
|
QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
|
|
|
if (argCount > 0) {
|
2012-07-16 06:32:49 +00:00
|
|
|
// Convert all arguments.
|
2011-11-28 16:04:33 +00:00
|
|
|
QVarLengthArray<CallArgument, 9> args(argCount + 1);
|
2011-05-11 07:20:40 +00:00
|
|
|
args[0].initAsType(returnType);
|
|
|
|
for (int ii = 0; ii < argCount; ++ii)
|
2013-09-24 13:25:10 +00:00
|
|
|
args[ii + 1].fromValue(argTypes[ii], engine, callArgs->args[ii]);
|
2011-05-11 07:20:40 +00:00
|
|
|
QVarLengthArray<void *, 9> argData(args.count());
|
|
|
|
for (int ii = 0; ii < args.count(); ++ii)
|
|
|
|
argData[ii] = args[ii].dataPtr();
|
|
|
|
|
2016-05-29 19:33:59 +00:00
|
|
|
object.metacall(callType, index, argData.data());
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2013-09-12 20:37:41 +00:00
|
|
|
return args[0].toValue(engine);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2012-05-23 21:09:15 +00:00
|
|
|
} else if (returnType != QMetaType::Void) {
|
2014-01-15 21:01:15 +00:00
|
|
|
|
2011-11-28 16:04:33 +00:00
|
|
|
CallArgument arg;
|
2011-05-11 07:20:40 +00:00
|
|
|
arg.initAsType(returnType);
|
|
|
|
|
|
|
|
void *args[] = { arg.dataPtr() };
|
|
|
|
|
2016-05-29 19:33:59 +00:00
|
|
|
object.metacall(callType, index, args);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2013-09-12 20:37:41 +00:00
|
|
|
return arg.toValue(engine);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
void *args[] = { 0 };
|
2016-05-29 19:33:59 +00:00
|
|
|
object.metacall(callType, index, args);
|
2013-09-12 09:13:03 +00:00
|
|
|
return Encode::undefined();
|
2011-05-11 07:20:40 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2014-01-15 21:01:15 +00:00
|
|
|
Returns the match score for converting \a actual to be of type \a conversionType. A
|
2011-05-11 07:20:40 +00:00
|
|
|
zero score means "perfect match" whereas a higher score is worse.
|
|
|
|
|
2013-06-24 09:26:22 +00:00
|
|
|
The conversion table is copied out of the \l QScript::callQtMethod() function.
|
2011-05-11 07:20:40 +00:00
|
|
|
*/
|
2015-01-15 10:36:57 +00:00
|
|
|
static int MatchScore(const QV4::Value &actual, int conversionType)
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
2015-01-15 10:36:57 +00:00
|
|
|
if (actual.isNumber()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::Double:
|
|
|
|
return 0;
|
|
|
|
case QMetaType::Float:
|
|
|
|
return 1;
|
|
|
|
case QMetaType::LongLong:
|
|
|
|
case QMetaType::ULongLong:
|
|
|
|
return 2;
|
|
|
|
case QMetaType::Long:
|
|
|
|
case QMetaType::ULong:
|
|
|
|
return 3;
|
|
|
|
case QMetaType::Int:
|
|
|
|
case QMetaType::UInt:
|
|
|
|
return 4;
|
|
|
|
case QMetaType::Short:
|
|
|
|
case QMetaType::UShort:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case QMetaType::Char:
|
|
|
|
case QMetaType::UChar:
|
|
|
|
return 6;
|
2012-04-24 16:22:06 +00:00
|
|
|
case QMetaType::QJsonValue:
|
|
|
|
return 5;
|
2011-05-11 07:20:40 +00:00
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
2015-01-15 10:36:57 +00:00
|
|
|
} else if (actual.isString()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QString:
|
|
|
|
return 0;
|
2012-04-24 16:22:06 +00:00
|
|
|
case QMetaType::QJsonValue:
|
|
|
|
return 5;
|
2011-05-11 07:20:40 +00:00
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
2015-01-15 10:36:57 +00:00
|
|
|
} else if (actual.isBoolean()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::Bool:
|
|
|
|
return 0;
|
2012-04-24 16:22:06 +00:00
|
|
|
case QMetaType::QJsonValue:
|
|
|
|
return 5;
|
2011-05-11 07:20:40 +00:00
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
2015-02-13 10:31:45 +00:00
|
|
|
} else if (actual.as<DateObject>()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QDateTime:
|
|
|
|
return 0;
|
|
|
|
case QMetaType::QDate:
|
|
|
|
return 1;
|
|
|
|
case QMetaType::QTime:
|
|
|
|
return 2;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
2015-01-15 10:36:57 +00:00
|
|
|
} else if (actual.as<QV4::RegExpObject>()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QRegExp:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
2015-12-28 12:22:29 +00:00
|
|
|
} else if (actual.as<ArrayBuffer>()) {
|
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QByteArray:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
2015-02-13 09:42:01 +00:00
|
|
|
} else if (actual.as<ArrayObject>()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
switch (conversionType) {
|
2012-04-24 16:22:06 +00:00
|
|
|
case QMetaType::QJsonArray:
|
|
|
|
return 3;
|
2011-05-11 07:20:40 +00:00
|
|
|
case QMetaType::QStringList:
|
|
|
|
case QMetaType::QVariantList:
|
|
|
|
return 5;
|
2012-07-16 06:32:49 +00:00
|
|
|
case QMetaType::QVector4D:
|
|
|
|
case QMetaType::QMatrix4x4:
|
|
|
|
return 6;
|
|
|
|
case QMetaType::QVector3D:
|
|
|
|
return 7;
|
2011-05-11 07:20:40 +00:00
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
2015-01-15 10:36:57 +00:00
|
|
|
} else if (actual.isNull()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
switch (conversionType) {
|
2016-08-05 08:02:52 +00:00
|
|
|
case QMetaType::Nullptr:
|
2011-05-11 07:20:40 +00:00
|
|
|
case QMetaType::VoidStar:
|
|
|
|
case QMetaType::QObjectStar:
|
2012-04-24 16:22:06 +00:00
|
|
|
case QMetaType::QJsonValue:
|
2011-05-11 07:20:40 +00:00
|
|
|
return 0;
|
2011-10-25 14:41:33 +00:00
|
|
|
default: {
|
|
|
|
const char *typeName = QMetaType::typeName(conversionType);
|
|
|
|
if (typeName && typeName[strlen(typeName) - 1] == '*')
|
2011-05-11 07:20:40 +00:00
|
|
|
return 0;
|
2011-10-25 14:41:33 +00:00
|
|
|
else
|
|
|
|
return 10;
|
|
|
|
}
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
2015-02-13 12:39:20 +00:00
|
|
|
} else if (const Object *obj = actual.as<Object>()) {
|
2013-11-01 11:38:32 +00:00
|
|
|
if (obj->as<QV4::VariantObject>()) {
|
2013-05-14 00:01:04 +00:00
|
|
|
if (conversionType == qMetaTypeId<QVariant>())
|
|
|
|
return 0;
|
2015-01-02 14:07:35 +00:00
|
|
|
if (obj->engine()->toVariant(actual, -1).userType() == conversionType)
|
2013-05-14 00:01:04 +00:00
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
|
2013-06-01 12:07:44 +00:00
|
|
|
if (obj->as<QObjectWrapper>()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QObjectStar:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
2013-05-22 14:51:34 +00:00
|
|
|
}
|
|
|
|
|
2014-11-18 16:15:10 +00:00
|
|
|
if (obj->as<QV4::QQmlValueTypeWrapper>()) {
|
2015-01-02 14:07:35 +00:00
|
|
|
if (obj->engine()->toVariant(actual, -1).userType() == conversionType)
|
2012-07-16 06:32:49 +00:00
|
|
|
return 0;
|
|
|
|
return 10;
|
2012-04-24 16:22:06 +00:00
|
|
|
} else if (conversionType == QMetaType::QJsonObject) {
|
|
|
|
return 5;
|
2016-04-12 13:14:55 +00:00
|
|
|
} else if (conversionType == qMetaTypeId<QJSValue>()) {
|
|
|
|
return 0;
|
2011-05-11 07:20:40 +00:00
|
|
|
} else {
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int QMetaObject_methods(const QMetaObject *metaObject)
|
|
|
|
{
|
|
|
|
struct Private
|
|
|
|
{
|
|
|
|
int revision;
|
|
|
|
int className;
|
|
|
|
int classInfoCount, classInfoData;
|
|
|
|
int methodCount, methodData;
|
|
|
|
};
|
|
|
|
|
|
|
|
return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Returns the next related method, if one, or 0.
|
|
|
|
*/
|
2014-11-14 20:53:20 +00:00
|
|
|
static const QQmlPropertyData * RelatedMethod(const QQmlObjectOrGadget &object,
|
|
|
|
const QQmlPropertyData *current,
|
|
|
|
QQmlPropertyData &dummy,
|
|
|
|
const QQmlPropertyCache *propertyCache)
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
2011-10-25 14:41:33 +00:00
|
|
|
if (!current->isOverload())
|
2011-05-11 07:20:40 +00:00
|
|
|
return 0;
|
|
|
|
|
2016-08-04 10:38:43 +00:00
|
|
|
Q_ASSERT(!current->overrideIndexIsProperty());
|
2011-10-25 14:41:33 +00:00
|
|
|
|
2014-11-14 20:53:20 +00:00
|
|
|
if (propertyCache) {
|
2016-08-04 10:38:43 +00:00
|
|
|
return propertyCache->method(current->overrideIndex());
|
2011-05-11 07:20:40 +00:00
|
|
|
} else {
|
2014-11-14 20:53:20 +00:00
|
|
|
const QMetaObject *mo = object.metaObject();
|
2011-05-11 07:20:40 +00:00
|
|
|
int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
|
|
|
|
|
2016-08-04 10:38:43 +00:00
|
|
|
while (methodOffset > current->overrideIndex()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
mo = mo->superClass();
|
|
|
|
methodOffset -= QMetaObject_methods(mo);
|
|
|
|
}
|
|
|
|
|
2014-03-24 13:31:05 +00:00
|
|
|
// If we've been called before with the same override index, then
|
|
|
|
// we can't go any further...
|
2016-08-04 10:38:43 +00:00
|
|
|
if (&dummy == current && dummy.coreIndex() == current->overrideIndex())
|
2014-03-24 13:31:05 +00:00
|
|
|
return 0;
|
|
|
|
|
2016-08-04 10:38:43 +00:00
|
|
|
QMetaMethod method = mo->method(current->overrideIndex());
|
2011-05-11 07:20:40 +00:00
|
|
|
dummy.load(method);
|
2014-01-15 21:01:15 +00:00
|
|
|
|
2011-05-11 07:20:40 +00:00
|
|
|
// Look for overloaded methods
|
2012-03-05 07:31:00 +00:00
|
|
|
QByteArray methodName = method.name();
|
2016-08-04 10:38:43 +00:00
|
|
|
for (int ii = current->overrideIndex() - 1; ii >= methodOffset; --ii) {
|
2012-03-05 07:31:00 +00:00
|
|
|
if (methodName == mo->method(ii).name()) {
|
2016-08-04 10:27:02 +00:00
|
|
|
dummy.setOverload(true);
|
2016-08-04 10:38:43 +00:00
|
|
|
dummy.setOverrideIndexIsProperty(0);
|
|
|
|
dummy.setOverrideIndex(ii);
|
2011-05-11 07:20:40 +00:00
|
|
|
return &dummy;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &dummy;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-14 20:53:20 +00:00
|
|
|
static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
|
2016-05-29 19:33:59 +00:00
|
|
|
QV4::ExecutionEngine *engine, QV4::CallData *callArgs,
|
|
|
|
QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
2012-06-18 05:06:16 +00:00
|
|
|
QByteArray unknownTypeError;
|
|
|
|
|
2014-11-14 20:53:20 +00:00
|
|
|
int returnType = object.methodReturnType(data, &unknownTypeError);
|
2012-06-18 05:06:16 +00:00
|
|
|
|
|
|
|
if (returnType == QMetaType::UnknownType) {
|
|
|
|
QString typeName = QString::fromLatin1(unknownTypeError);
|
2015-08-12 12:17:58 +00:00
|
|
|
QString error = QStringLiteral("Unknown method return type: %1").arg(typeName);
|
2014-12-30 15:38:20 +00:00
|
|
|
return engine->throwError(error);
|
2012-06-18 05:06:16 +00:00
|
|
|
}
|
|
|
|
|
2011-07-04 02:41:21 +00:00
|
|
|
if (data.hasArguments()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2011-10-25 14:41:33 +00:00
|
|
|
int *args = 0;
|
2016-07-05 08:35:56 +00:00
|
|
|
QQmlMetaObject::ArgTypeStorage storage;
|
2011-10-25 14:41:33 +00:00
|
|
|
|
2016-05-29 19:33:59 +00:00
|
|
|
if (data.isConstructor())
|
2016-07-05 08:35:56 +00:00
|
|
|
args = static_cast<const QQmlStaticMetaObject&>(object).constructorParameterTypes(
|
2016-08-04 10:38:43 +00:00
|
|
|
data.coreIndex(), &storage, &unknownTypeError);
|
2016-05-29 19:33:59 +00:00
|
|
|
else
|
2016-08-04 10:38:43 +00:00
|
|
|
args = object.methodParameterTypes(data.coreIndex(), &storage, &unknownTypeError);
|
2011-10-25 14:41:33 +00:00
|
|
|
|
|
|
|
if (!args) {
|
|
|
|
QString typeName = QString::fromLatin1(unknownTypeError);
|
2015-08-12 12:17:58 +00:00
|
|
|
QString error = QStringLiteral("Unknown method parameter type: %1").arg(typeName);
|
2014-12-30 15:38:20 +00:00
|
|
|
return engine->throwError(error);
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
|
2013-09-24 13:25:10 +00:00
|
|
|
if (args[0] > callArgs->argc) {
|
2011-05-11 07:20:40 +00:00
|
|
|
QString error = QLatin1String("Insufficient arguments");
|
2014-12-30 15:38:20 +00:00
|
|
|
return engine->throwError(error);
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
|
2016-08-04 10:38:43 +00:00
|
|
|
return CallMethod(object, data.coreIndex(), returnType, args[0], args + 1, engine, callArgs, callType);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
|
2016-08-04 10:38:43 +00:00
|
|
|
return CallMethod(object, data.coreIndex(), returnType, 0, 0, engine, callArgs, callType);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Resolve the overloaded method to call. The algorithm works conceptually like this:
|
|
|
|
1. Resolve the set of overloads it is *possible* to call.
|
2014-01-15 21:01:15 +00:00
|
|
|
Impossible overloads include those that have too many parameters or have parameters
|
|
|
|
of unknown type.
|
|
|
|
2. Filter the set of overloads to only contain those with the closest number of
|
2011-05-11 07:20:40 +00:00
|
|
|
parameters.
|
|
|
|
For example, if we are called with 3 parameters and there are 2 overloads that
|
|
|
|
take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
|
2014-01-15 21:01:15 +00:00
|
|
|
3. Find the best remaining overload based on its match score.
|
2011-05-11 07:20:40 +00:00
|
|
|
If two or more overloads have the same match score, call the last one. The match
|
|
|
|
score is constructed by adding the matchScore() result for each of the parameters.
|
|
|
|
*/
|
2014-11-14 20:53:20 +00:00
|
|
|
static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QQmlPropertyData &data,
|
2016-05-29 19:33:59 +00:00
|
|
|
QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache,
|
|
|
|
QMetaObject::Call callType = QMetaObject::InvokeMetaMethod)
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
2013-09-24 13:25:10 +00:00
|
|
|
int argumentCount = callArgs->argc;
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2014-06-04 11:17:30 +00:00
|
|
|
QQmlPropertyData best;
|
2011-05-11 07:20:40 +00:00
|
|
|
int bestParameterScore = INT_MAX;
|
|
|
|
int bestMatchScore = INT_MAX;
|
|
|
|
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlPropertyData dummy;
|
|
|
|
const QQmlPropertyData *attempt = &data;
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2014-12-30 15:38:20 +00:00
|
|
|
QV4::Scope scope(engine);
|
2013-09-24 13:25:10 +00:00
|
|
|
QV4::ScopedValue v(scope);
|
|
|
|
|
2011-05-11 07:20:40 +00:00
|
|
|
do {
|
2016-07-05 08:35:56 +00:00
|
|
|
QQmlMetaObject::ArgTypeStorage storage;
|
2011-10-25 14:41:33 +00:00
|
|
|
int methodArgumentCount = 0;
|
|
|
|
int *methodArgTypes = 0;
|
|
|
|
if (attempt->hasArguments()) {
|
2016-08-04 10:38:43 +00:00
|
|
|
int *args = object.methodParameterTypes(attempt->coreIndex(), &storage, 0);
|
2011-10-25 14:41:33 +00:00
|
|
|
if (!args) // Must be an unknown argument
|
|
|
|
continue;
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2011-10-25 14:41:33 +00:00
|
|
|
methodArgumentCount = args[0];
|
|
|
|
methodArgTypes = args + 1;
|
|
|
|
}
|
2011-05-11 07:20:40 +00:00
|
|
|
|
|
|
|
if (methodArgumentCount > argumentCount)
|
|
|
|
continue; // We don't have sufficient arguments to call this method
|
|
|
|
|
|
|
|
int methodParameterScore = argumentCount - methodArgumentCount;
|
|
|
|
if (methodParameterScore > bestParameterScore)
|
|
|
|
continue; // We already have a better option
|
|
|
|
|
|
|
|
int methodMatchScore = 0;
|
2014-01-15 21:01:15 +00:00
|
|
|
for (int ii = 0; ii < methodArgumentCount; ++ii)
|
2013-09-24 13:25:10 +00:00
|
|
|
methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
|
|
|
if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
|
2014-06-04 11:17:30 +00:00
|
|
|
best = *attempt;
|
2011-05-11 07:20:40 +00:00
|
|
|
bestParameterScore = methodParameterScore;
|
|
|
|
bestMatchScore = methodMatchScore;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bestParameterScore == 0 && bestMatchScore == 0)
|
|
|
|
break; // We can't get better than that
|
|
|
|
|
2014-11-14 20:53:20 +00:00
|
|
|
} while ((attempt = RelatedMethod(object, attempt, dummy, propertyCache)) != 0);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2014-06-04 11:17:30 +00:00
|
|
|
if (best.isValid()) {
|
2016-05-29 19:33:59 +00:00
|
|
|
return CallPrecise(object, best, engine, callArgs, callType);
|
2011-05-11 07:20:40 +00:00
|
|
|
} else {
|
|
|
|
QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
|
2012-02-16 04:43:03 +00:00
|
|
|
const QQmlPropertyData *candidate = &data;
|
2011-05-11 07:20:40 +00:00
|
|
|
while (candidate) {
|
2014-01-15 21:01:15 +00:00
|
|
|
error += QLatin1String("\n ") +
|
2016-08-04 10:38:43 +00:00
|
|
|
QString::fromUtf8(object.metaObject()->method(candidate->coreIndex())
|
2015-07-30 09:51:02 +00:00
|
|
|
.methodSignature());
|
2014-11-14 20:53:20 +00:00
|
|
|
candidate = RelatedMethod(object, candidate, dummy, propertyCache);
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
|
2014-12-30 15:38:20 +00:00
|
|
|
return engine->throwError(error);
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-28 16:04:33 +00:00
|
|
|
CallArgument::CallArgument()
|
2011-07-29 02:28:03 +00:00
|
|
|
: type(QVariant::Invalid)
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-11-28 16:04:33 +00:00
|
|
|
CallArgument::~CallArgument()
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
|
|
|
cleanup();
|
|
|
|
}
|
|
|
|
|
2011-11-28 16:04:33 +00:00
|
|
|
void CallArgument::cleanup()
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
|
|
|
if (type == QMetaType::QString) {
|
2011-07-29 02:28:03 +00:00
|
|
|
qstringPtr->~QString();
|
2015-12-28 12:22:29 +00:00
|
|
|
} else if (type == QMetaType::QByteArray) {
|
|
|
|
qbyteArrayPtr->~QByteArray();
|
2011-07-29 02:28:03 +00:00
|
|
|
} else if (type == -1 || type == QMetaType::QVariant) {
|
|
|
|
qvariantPtr->~QVariant();
|
2011-07-29 08:25:44 +00:00
|
|
|
} else if (type == qMetaTypeId<QJSValue>()) {
|
|
|
|
qjsValuePtr->~QJSValue();
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == qMetaTypeId<QList<QObject *> >()) {
|
2011-07-29 02:28:03 +00:00
|
|
|
qlistPtr->~QList<QObject *>();
|
2012-04-24 16:22:06 +00:00
|
|
|
} else if (type == QMetaType::QJsonArray) {
|
|
|
|
jsonArrayPtr->~QJsonArray();
|
|
|
|
} else if (type == QMetaType::QJsonObject) {
|
|
|
|
jsonObjectPtr->~QJsonObject();
|
|
|
|
} else if (type == QMetaType::QJsonValue) {
|
|
|
|
jsonValuePtr->~QJsonValue();
|
2014-01-15 21:01:15 +00:00
|
|
|
}
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
|
2011-11-28 16:04:33 +00:00
|
|
|
void *CallArgument::dataPtr()
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
|
|
|
if (type == -1)
|
2011-07-29 02:28:03 +00:00
|
|
|
return qvariantPtr->data();
|
2013-10-21 12:43:19 +00:00
|
|
|
else if (type != 0)
|
2011-07-29 02:28:03 +00:00
|
|
|
return (void *)&allocData;
|
2013-10-21 12:43:19 +00:00
|
|
|
return 0;
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
|
2011-11-28 16:04:33 +00:00
|
|
|
void CallArgument::initAsType(int callType)
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
|
|
|
if (type != 0) { cleanup(); type = 0; }
|
2013-10-21 12:43:19 +00:00
|
|
|
if (callType == QMetaType::UnknownType || callType == QMetaType::Void) return;
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2011-07-29 08:25:44 +00:00
|
|
|
if (callType == qMetaTypeId<QJSValue>()) {
|
|
|
|
qjsValuePtr = new (&allocData) QJSValue();
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::Int ||
|
|
|
|
callType == QMetaType::UInt ||
|
|
|
|
callType == QMetaType::Bool ||
|
|
|
|
callType == QMetaType::Double ||
|
|
|
|
callType == QMetaType::Float) {
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::QObjectStar) {
|
2011-07-29 02:28:03 +00:00
|
|
|
qobjectPtr = 0;
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::QString) {
|
2011-07-29 02:28:03 +00:00
|
|
|
qstringPtr = new (&allocData) QString();
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
2011-07-29 02:28:03 +00:00
|
|
|
} else if (callType == QMetaType::QVariant) {
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
2011-07-29 02:28:03 +00:00
|
|
|
qvariantPtr = new (&allocData) QVariant();
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (callType == qMetaTypeId<QList<QObject *> >()) {
|
|
|
|
type = callType;
|
2011-07-29 02:28:03 +00:00
|
|
|
qlistPtr = new (&allocData) QList<QObject *>();
|
2013-04-19 08:59:41 +00:00
|
|
|
} else if (callType == qMetaTypeId<QQmlV4Handle>()) {
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
2013-04-19 08:59:41 +00:00
|
|
|
handlePtr = new (&allocData) QQmlV4Handle;
|
2012-04-24 16:22:06 +00:00
|
|
|
} else if (callType == QMetaType::QJsonArray) {
|
|
|
|
type = callType;
|
|
|
|
jsonArrayPtr = new (&allocData) QJsonArray();
|
|
|
|
} else if (callType == QMetaType::QJsonObject) {
|
|
|
|
type = callType;
|
|
|
|
jsonObjectPtr = new (&allocData) QJsonObject();
|
|
|
|
} else if (callType == QMetaType::QJsonValue) {
|
|
|
|
type = callType;
|
|
|
|
jsonValuePtr = new (&allocData) QJsonValue();
|
2011-05-11 07:20:40 +00:00
|
|
|
} else {
|
|
|
|
type = -1;
|
2011-07-29 02:28:03 +00:00
|
|
|
qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-15 10:36:57 +00:00
|
|
|
void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value)
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
2013-10-10 14:17:28 +00:00
|
|
|
if (type != 0) {
|
|
|
|
cleanup();
|
|
|
|
type = 0;
|
|
|
|
}
|
|
|
|
|
2014-12-30 15:38:20 +00:00
|
|
|
QV4::Scope scope(engine);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
2013-11-13 10:03:52 +00:00
|
|
|
bool queryEngine = false;
|
2011-07-29 08:25:44 +00:00
|
|
|
if (callType == qMetaTypeId<QJSValue>()) {
|
2015-01-14 11:50:34 +00:00
|
|
|
qjsValuePtr = new (&allocData) QJSValue(scope.engine, value.asReturnedValue());
|
2011-07-29 08:25:44 +00:00
|
|
|
type = qMetaTypeId<QJSValue>();
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (callType == QMetaType::Int) {
|
2015-01-15 10:36:57 +00:00
|
|
|
intValue = quint32(value.toInt32());
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::UInt) {
|
2015-01-15 10:36:57 +00:00
|
|
|
intValue = quint32(value.toUInt32());
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::Bool) {
|
2015-01-15 10:36:57 +00:00
|
|
|
boolValue = value.toBoolean();
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::Double) {
|
2015-01-15 10:36:57 +00:00
|
|
|
doubleValue = double(value.toNumber());
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::Float) {
|
2015-01-15 10:36:57 +00:00
|
|
|
floatValue = float(value.toNumber());
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::QString) {
|
2015-01-15 10:36:57 +00:00
|
|
|
if (value.isNull() || value.isUndefined())
|
2011-07-29 02:28:03 +00:00
|
|
|
qstringPtr = new (&allocData) QString();
|
2011-05-11 07:20:40 +00:00
|
|
|
else
|
2015-01-15 10:36:57 +00:00
|
|
|
qstringPtr = new (&allocData) QString(value.toQStringNoThrow());
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::QObjectStar) {
|
2013-06-03 13:51:45 +00:00
|
|
|
qobjectPtr = 0;
|
2015-02-13 09:02:28 +00:00
|
|
|
if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>())
|
2013-06-03 13:51:45 +00:00
|
|
|
qobjectPtr = qobjectWrapper->object();
|
2015-02-13 09:02:28 +00:00
|
|
|
else if (const QV4::QmlTypeWrapper *qmlTypeWrapper = value.as<QV4::QmlTypeWrapper>())
|
2013-11-13 10:03:52 +00:00
|
|
|
queryEngine = qmlTypeWrapper->isSingleton();
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == qMetaTypeId<QVariant>()) {
|
2015-01-02 14:07:35 +00:00
|
|
|
qvariantPtr = new (&allocData) QVariant(scope.engine->toVariant(value, -1));
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == qMetaTypeId<QList<QObject*> >()) {
|
2011-07-29 02:28:03 +00:00
|
|
|
qlistPtr = new (&allocData) QList<QObject *>();
|
2013-10-10 14:17:28 +00:00
|
|
|
QV4::ScopedArrayObject array(scope, value);
|
|
|
|
if (array) {
|
2013-09-11 19:48:23 +00:00
|
|
|
Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
|
|
|
|
|
2014-11-21 06:16:53 +00:00
|
|
|
uint length = array->getLength();
|
|
|
|
for (uint ii = 0; ii < length; ++ii) {
|
2013-06-03 13:51:45 +00:00
|
|
|
QObject *o = 0;
|
2013-09-11 19:48:23 +00:00
|
|
|
qobjectWrapper = array->getIndexed(ii);
|
|
|
|
if (!!qobjectWrapper)
|
2013-06-03 13:51:45 +00:00
|
|
|
o = qobjectWrapper->object();
|
|
|
|
qlistPtr->append(o);
|
|
|
|
}
|
2011-05-11 07:20:40 +00:00
|
|
|
} else {
|
2013-06-03 13:51:45 +00:00
|
|
|
QObject *o = 0;
|
2015-02-13 09:02:28 +00:00
|
|
|
if (const QV4::QObjectWrapper *qobjectWrapper = value.as<QV4::QObjectWrapper>())
|
2013-06-03 13:51:45 +00:00
|
|
|
o = qobjectWrapper->object();
|
|
|
|
qlistPtr->append(o);
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
type = callType;
|
2013-04-19 08:59:41 +00:00
|
|
|
} else if (callType == qMetaTypeId<QQmlV4Handle>()) {
|
2015-01-15 10:36:57 +00:00
|
|
|
handlePtr = new (&allocData) QQmlV4Handle(value.asReturnedValue());
|
2011-05-11 07:20:40 +00:00
|
|
|
type = callType;
|
2012-04-24 16:22:06 +00:00
|
|
|
} else if (callType == QMetaType::QJsonArray) {
|
2013-09-25 11:34:23 +00:00
|
|
|
QV4::ScopedArrayObject a(scope, value);
|
|
|
|
jsonArrayPtr = new (&allocData) QJsonArray(QV4::JsonObject::toJsonArray(a));
|
2012-04-24 16:22:06 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::QJsonObject) {
|
2013-09-25 11:34:23 +00:00
|
|
|
QV4::ScopedObject o(scope, value);
|
|
|
|
jsonObjectPtr = new (&allocData) QJsonObject(QV4::JsonObject::toJsonObject(o));
|
2012-04-24 16:22:06 +00:00
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::QJsonValue) {
|
2013-06-03 19:28:07 +00:00
|
|
|
jsonValuePtr = new (&allocData) QJsonValue(QV4::JsonObject::toJsonValue(value));
|
2012-04-24 16:22:06 +00:00
|
|
|
type = callType;
|
2012-03-05 13:45:34 +00:00
|
|
|
} else if (callType == QMetaType::Void) {
|
|
|
|
*qvariantPtr = QVariant();
|
2011-05-11 07:20:40 +00:00
|
|
|
} else {
|
2013-11-13 10:03:52 +00:00
|
|
|
queryEngine = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (queryEngine) {
|
2011-07-29 02:28:03 +00:00
|
|
|
qvariantPtr = new (&allocData) QVariant();
|
2011-05-11 07:20:40 +00:00
|
|
|
type = -1;
|
|
|
|
|
2014-12-30 15:38:20 +00:00
|
|
|
QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : 0;
|
2015-01-02 14:07:35 +00:00
|
|
|
QVariant v = scope.engine->toVariant(value, callType);
|
2011-05-11 07:20:40 +00:00
|
|
|
|
|
|
|
if (v.userType() == callType) {
|
2011-07-29 02:28:03 +00:00
|
|
|
*qvariantPtr = v;
|
2012-05-18 15:30:03 +00:00
|
|
|
} else if (v.canConvert(callType)) {
|
2011-07-29 02:28:03 +00:00
|
|
|
*qvariantPtr = v;
|
2012-05-18 15:30:03 +00:00
|
|
|
qvariantPtr->convert(callType);
|
2011-05-11 07:20:40 +00:00
|
|
|
} else {
|
2012-05-11 11:01:41 +00:00
|
|
|
QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
|
|
|
|
if (!mo.isNull()) {
|
|
|
|
QObject *obj = ep->toQObject(v);
|
|
|
|
|
|
|
|
if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo))
|
|
|
|
obj = 0;
|
|
|
|
|
|
|
|
*qvariantPtr = QVariant(callType, &obj);
|
|
|
|
} else {
|
|
|
|
*qvariantPtr = QVariant(callType, (void *)0);
|
|
|
|
}
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-30 15:38:20 +00:00
|
|
|
QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
|
2011-05-11 07:20:40 +00:00
|
|
|
{
|
2014-12-30 15:38:20 +00:00
|
|
|
QV4::Scope scope(engine);
|
2013-09-12 20:37:41 +00:00
|
|
|
|
2011-07-29 08:25:44 +00:00
|
|
|
if (type == qMetaTypeId<QJSValue>()) {
|
2015-01-14 15:22:33 +00:00
|
|
|
return QJSValuePrivate::convertedToValue(scope.engine, *qjsValuePtr);
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == QMetaType::Int) {
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(int(intValue));
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == QMetaType::UInt) {
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode((uint)intValue);
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == QMetaType::Bool) {
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(boolValue);
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == QMetaType::Double) {
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(doubleValue);
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == QMetaType::Float) {
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode(floatValue);
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == QMetaType::QString) {
|
2014-12-30 17:29:21 +00:00
|
|
|
return QV4::Encode(engine->newString(*qstringPtr));
|
2015-12-28 12:22:29 +00:00
|
|
|
} else if (type == QMetaType::QByteArray) {
|
|
|
|
return QV4::Encode(engine->newArrayBuffer(*qbyteArrayPtr));
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == QMetaType::QObjectStar) {
|
2011-07-29 02:28:03 +00:00
|
|
|
QObject *object = qobjectPtr;
|
2011-05-11 07:20:40 +00:00
|
|
|
if (object)
|
2012-02-16 04:43:03 +00:00
|
|
|
QQmlData::get(object, true)->setImplicitDestructible();
|
2014-12-30 15:38:20 +00:00
|
|
|
return QV4::QObjectWrapper::wrap(scope.engine, object);
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == qMetaTypeId<QList<QObject *> >()) {
|
2011-06-09 04:40:44 +00:00
|
|
|
// XXX Can this be made more by using Array as a prototype and implementing
|
|
|
|
// directly against QList<QObject*>?
|
2011-07-29 02:28:03 +00:00
|
|
|
QList<QObject *> &list = *qlistPtr;
|
2014-12-30 15:38:20 +00:00
|
|
|
QV4::ScopedArrayObject array(scope, scope.engine->newArrayObject());
|
2013-05-21 19:31:45 +00:00
|
|
|
array->arrayReserve(list.count());
|
2013-12-16 08:16:57 +00:00
|
|
|
QV4::ScopedValue v(scope);
|
2014-01-13 08:09:14 +00:00
|
|
|
for (int ii = 0; ii < list.count(); ++ii)
|
2014-12-30 15:38:20 +00:00
|
|
|
array->arrayPut(ii, (v = QV4::QObjectWrapper::wrap(scope.engine, list.at(ii))));
|
2013-05-21 19:31:45 +00:00
|
|
|
array->setArrayLengthUnchecked(list.count());
|
2013-09-14 09:25:02 +00:00
|
|
|
return array.asReturnedValue();
|
2013-04-19 08:59:41 +00:00
|
|
|
} else if (type == qMetaTypeId<QQmlV4Handle>()) {
|
2013-09-24 10:13:42 +00:00
|
|
|
return *handlePtr;
|
2012-04-24 16:22:06 +00:00
|
|
|
} else if (type == QMetaType::QJsonArray) {
|
2014-12-30 15:38:20 +00:00
|
|
|
return QV4::JsonObject::fromJsonArray(scope.engine, *jsonArrayPtr);
|
2012-04-24 16:22:06 +00:00
|
|
|
} else if (type == QMetaType::QJsonObject) {
|
2014-12-30 15:38:20 +00:00
|
|
|
return QV4::JsonObject::fromJsonObject(scope.engine, *jsonObjectPtr);
|
2012-04-24 16:22:06 +00:00
|
|
|
} else if (type == QMetaType::QJsonValue) {
|
2014-12-30 15:38:20 +00:00
|
|
|
return QV4::JsonObject::fromJsonValue(scope.engine, *jsonValuePtr);
|
2011-05-11 07:20:40 +00:00
|
|
|
} else if (type == -1 || type == qMetaTypeId<QVariant>()) {
|
2011-07-29 02:28:03 +00:00
|
|
|
QVariant value = *qvariantPtr;
|
2015-01-02 14:07:35 +00:00
|
|
|
QV4::ScopedValue rv(scope, scope.engine->fromVariant(value));
|
2014-05-07 13:36:38 +00:00
|
|
|
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, rv);
|
2014-01-27 13:58:52 +00:00
|
|
|
if (!!qobjectWrapper) {
|
2013-06-03 13:51:45 +00:00
|
|
|
if (QObject *object = qobjectWrapper->object())
|
|
|
|
QQmlData::get(object, true)->setImplicitDestructible();
|
|
|
|
}
|
2015-01-15 20:28:01 +00:00
|
|
|
return rv->asReturnedValue();
|
2011-05-11 07:20:40 +00:00
|
|
|
} else {
|
2013-09-12 20:37:41 +00:00
|
|
|
return QV4::Encode::undefined();
|
2011-05-11 07:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
2011-09-28 07:57:04 +00:00
|
|
|
|
2015-08-12 10:15:47 +00:00
|
|
|
ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
|
2013-06-03 14:41:14 +00:00
|
|
|
{
|
2014-11-14 20:53:20 +00:00
|
|
|
Scope valueScope(scope);
|
2015-08-28 10:58:08 +00:00
|
|
|
Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->allocObject<QObjectMethod>(scope));
|
2016-09-07 11:31:14 +00:00
|
|
|
method->d()->setObject(object);
|
2014-11-14 20:53:20 +00:00
|
|
|
|
|
|
|
if (QQmlData *ddata = QQmlData::get(object))
|
2016-09-09 13:37:57 +00:00
|
|
|
method->d()->setPropertyCache(ddata->propertyCache);
|
2014-11-14 20:53:20 +00:00
|
|
|
|
|
|
|
method->d()->index = index;
|
|
|
|
return method.asReturnedValue();
|
2013-06-03 14:41:14 +00:00
|
|
|
}
|
|
|
|
|
2015-08-12 10:15:47 +00:00
|
|
|
ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index)
|
2014-11-14 20:53:20 +00:00
|
|
|
{
|
|
|
|
Scope valueScope(scope);
|
2016-06-22 08:12:13 +00:00
|
|
|
Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
|
2016-09-09 13:37:57 +00:00
|
|
|
method->d()->setPropertyCache(valueType->d()->propertyCache());
|
2014-11-14 20:53:20 +00:00
|
|
|
method->d()->index = index;
|
2015-09-09 12:36:07 +00:00
|
|
|
method->d()->valueTypeWrapper = valueType->d();
|
2014-11-14 20:53:20 +00:00
|
|
|
return method.asReturnedValue();
|
|
|
|
}
|
|
|
|
|
2016-09-09 13:37:57 +00:00
|
|
|
void Heap::QObjectMethod::init(QV4::ExecutionContext *scope)
|
2013-05-24 15:02:48 +00:00
|
|
|
{
|
2016-09-09 13:37:57 +00:00
|
|
|
Heap::FunctionObject::init(scope);
|
2013-05-24 15:02:48 +00:00
|
|
|
}
|
|
|
|
|
2014-11-14 20:53:20 +00:00
|
|
|
const QMetaObject *Heap::QObjectMethod::metaObject()
|
|
|
|
{
|
2016-09-09 13:37:57 +00:00
|
|
|
if (propertyCache())
|
|
|
|
return propertyCache()->createMetaObject();
|
2016-09-07 11:31:14 +00:00
|
|
|
return object()->metaObject();
|
2014-11-14 20:53:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-13 12:39:20 +00:00
|
|
|
QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) const
|
2013-05-27 18:23:13 +00:00
|
|
|
{
|
|
|
|
QString result;
|
2014-11-14 20:53:20 +00:00
|
|
|
if (const QMetaObject *metaObject = d()->metaObject()) {
|
2013-05-27 18:23:13 +00:00
|
|
|
|
2016-05-06 19:02:43 +00:00
|
|
|
result += QString::fromUtf8(metaObject->className()) +
|
2016-09-07 11:31:14 +00:00
|
|
|
QLatin1String("(0x") + QString::number((quintptr)d()->object(),16);
|
2013-05-27 18:23:13 +00:00
|
|
|
|
2016-09-07 11:31:14 +00:00
|
|
|
if (d()->object()) {
|
|
|
|
QString objectName = d()->object()->objectName();
|
2016-05-06 19:02:43 +00:00
|
|
|
if (!objectName.isEmpty())
|
|
|
|
result += QLatin1String(", \"") + objectName + QLatin1Char('\"');
|
2013-05-27 18:23:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result += QLatin1Char(')');
|
|
|
|
} else {
|
|
|
|
result = QLatin1String("null");
|
|
|
|
}
|
|
|
|
|
2014-05-06 07:23:59 +00:00
|
|
|
return ctx->d()->engine->newString(result)->asReturnedValue();
|
2013-05-27 18:23:13 +00:00
|
|
|
}
|
|
|
|
|
2015-02-13 12:39:20 +00:00
|
|
|
QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const
|
2013-05-27 18:23:13 +00:00
|
|
|
{
|
2016-09-07 11:31:14 +00:00
|
|
|
if (!d()->object())
|
2013-09-12 09:13:03 +00:00
|
|
|
return Encode::undefined();
|
2016-09-07 11:31:14 +00:00
|
|
|
if (QQmlData::keepAliveDuringGarbageCollection(d()->object()))
|
2014-07-28 08:07:57 +00:00
|
|
|
return ctx->engine()->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object"));
|
2013-05-27 18:23:13 +00:00
|
|
|
|
|
|
|
int delay = 0;
|
|
|
|
if (argc > 0)
|
|
|
|
delay = args[0].toUInt32();
|
|
|
|
|
|
|
|
if (delay > 0)
|
2016-09-07 11:31:14 +00:00
|
|
|
QTimer::singleShot(delay, d()->object(), SLOT(deleteLater()));
|
2013-05-27 18:23:13 +00:00
|
|
|
else
|
2016-09-07 11:31:14 +00:00
|
|
|
d()->object()->deleteLater();
|
2013-05-27 18:23:13 +00:00
|
|
|
|
2013-09-12 09:13:03 +00:00
|
|
|
return Encode::undefined();
|
2013-05-27 18:23:13 +00:00
|
|
|
}
|
|
|
|
|
2016-06-22 08:12:13 +00:00
|
|
|
void QObjectMethod::call(const Managed *m, Scope &scope, CallData *callData)
|
2013-05-24 15:02:48 +00:00
|
|
|
{
|
2015-02-13 12:39:20 +00:00
|
|
|
const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
|
2016-06-22 08:12:13 +00:00
|
|
|
This->callInternal(callData, scope);
|
2013-05-24 15:02:48 +00:00
|
|
|
}
|
|
|
|
|
2016-06-22 08:12:13 +00:00
|
|
|
void QObjectMethod::callInternal(CallData *callData, Scope &scope) const
|
2013-05-24 15:02:48 +00:00
|
|
|
{
|
2015-09-09 12:36:07 +00:00
|
|
|
ExecutionEngine *v4 = engine();
|
|
|
|
ExecutionContext *context = v4->currentContext;
|
2016-06-22 08:12:13 +00:00
|
|
|
if (d()->index == DestroyMethod) {
|
|
|
|
scope.result = method_destroy(context, callData->args, callData->argc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (d()->index == ToStringMethod) {
|
|
|
|
scope.result = method_toString(context);
|
|
|
|
return;
|
|
|
|
}
|
2013-05-27 18:23:13 +00:00
|
|
|
|
2016-09-07 11:31:14 +00:00
|
|
|
QQmlObjectOrGadget object(d()->object());
|
|
|
|
if (!d()->object()) {
|
2016-06-22 08:12:13 +00:00
|
|
|
if (!d()->valueTypeWrapper) {
|
|
|
|
scope.result = Encode::undefined();
|
|
|
|
return;
|
|
|
|
}
|
2013-05-24 15:02:48 +00:00
|
|
|
|
2016-09-09 13:37:57 +00:00
|
|
|
object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr);
|
2013-05-24 15:02:48 +00:00
|
|
|
}
|
|
|
|
|
2014-11-14 20:53:20 +00:00
|
|
|
QQmlPropertyData method;
|
|
|
|
|
2016-09-09 13:37:57 +00:00
|
|
|
if (d()->propertyCache()) {
|
|
|
|
QQmlPropertyData *data = d()->propertyCache()->method(d()->index);
|
2016-06-22 08:12:13 +00:00
|
|
|
if (!data) {
|
|
|
|
scope.result = QV4::Encode::undefined();
|
|
|
|
return;
|
|
|
|
}
|
2014-11-14 20:53:20 +00:00
|
|
|
method = *data;
|
|
|
|
} else {
|
2016-09-07 11:31:14 +00:00
|
|
|
const QMetaObject *mo = d()->object()->metaObject();
|
2014-04-29 09:17:04 +00:00
|
|
|
const QMetaMethod moMethod = mo->method(d()->index);
|
2014-02-28 15:01:53 +00:00
|
|
|
method.load(moMethod);
|
2013-05-24 15:02:48 +00:00
|
|
|
|
2016-08-04 10:38:43 +00:00
|
|
|
if (method.coreIndex() == -1) {
|
2016-06-22 08:12:13 +00:00
|
|
|
scope.result = QV4::Encode::undefined();
|
|
|
|
return;
|
|
|
|
}
|
2014-02-28 15:01:53 +00:00
|
|
|
|
|
|
|
// Look for overloaded methods
|
|
|
|
QByteArray methodName = moMethod.name();
|
|
|
|
const int methodOffset = mo->methodOffset();
|
2014-04-29 09:17:04 +00:00
|
|
|
for (int ii = d()->index - 1; ii >= methodOffset; --ii) {
|
2014-02-28 15:01:53 +00:00
|
|
|
if (methodName == mo->method(ii).name()) {
|
2016-08-04 10:27:02 +00:00
|
|
|
method.setOverload(true);
|
2016-08-04 10:38:43 +00:00
|
|
|
method.setOverrideIndexIsProperty(0);
|
|
|
|
method.setOverrideIndex(ii);
|
2014-02-28 15:01:53 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-05-24 15:02:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (method.isV4Function()) {
|
2016-06-22 08:12:13 +00:00
|
|
|
scope.result = QV4::Encode::undefined();
|
|
|
|
QQmlV4Function func(callData, &scope.result, v4);
|
2013-05-24 15:02:48 +00:00
|
|
|
QQmlV4Function *funcptr = &func;
|
|
|
|
|
|
|
|
void *args[] = { 0, &funcptr };
|
2016-08-04 10:38:43 +00:00
|
|
|
object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex(), args);
|
2013-05-24 15:02:48 +00:00
|
|
|
|
2016-06-22 08:12:13 +00:00
|
|
|
return;
|
2013-05-24 15:02:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!method.isOverload()) {
|
2016-06-22 08:12:13 +00:00
|
|
|
scope.result = CallPrecise(object, method, v4, callData);
|
2013-05-24 15:02:48 +00:00
|
|
|
} else {
|
2016-09-09 13:37:57 +00:00
|
|
|
scope.result = CallOverloaded(object, method, v4, callData, d()->propertyCache());
|
2013-05-24 15:02:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-19 14:07:30 +00:00
|
|
|
void QObjectMethod::markObjects(Heap::Base *that, ExecutionEngine *e)
|
|
|
|
{
|
2014-11-14 20:53:20 +00:00
|
|
|
QObjectMethod::Data *This = static_cast<QObjectMethod::Data*>(that);
|
2015-09-09 12:36:07 +00:00
|
|
|
if (This->valueTypeWrapper)
|
|
|
|
This->valueTypeWrapper->mark(e);
|
2015-01-23 15:03:02 +00:00
|
|
|
|
|
|
|
FunctionObject::markObjects(that, e);
|
2014-11-19 14:07:30 +00:00
|
|
|
}
|
|
|
|
|
2014-01-20 12:51:00 +00:00
|
|
|
DEFINE_OBJECT_VTABLE(QObjectMethod);
|
2013-05-24 15:02:48 +00:00
|
|
|
|
2016-05-29 19:33:59 +00:00
|
|
|
|
2016-09-09 13:37:57 +00:00
|
|
|
void Heap::QMetaObjectWrapper::init(const QMetaObject *metaObject)
|
|
|
|
{
|
|
|
|
FunctionObject::init();
|
|
|
|
this->metaObject = metaObject;
|
|
|
|
constructors = nullptr;
|
|
|
|
constructorCount = 0;
|
|
|
|
}
|
2016-05-29 19:33:59 +00:00
|
|
|
|
2016-09-09 08:06:31 +00:00
|
|
|
void Heap::QMetaObjectWrapper::destroy()
|
2016-09-07 11:06:40 +00:00
|
|
|
{
|
|
|
|
delete[] constructors;
|
2016-05-29 19:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
|
|
|
|
|
|
|
|
const int count = metaObject->constructorCount();
|
2016-09-07 11:06:40 +00:00
|
|
|
if (constructorCount != count) {
|
|
|
|
delete[] constructors;
|
|
|
|
constructorCount = count;
|
|
|
|
if (count == 0) {
|
|
|
|
constructors = nullptr;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
constructors = new QQmlPropertyData[count];
|
2016-05-29 19:33:59 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
QMetaMethod method = metaObject->constructor(i);
|
2016-09-07 11:06:40 +00:00
|
|
|
QQmlPropertyData &d = constructors[i];
|
2016-05-29 19:33:59 +00:00
|
|
|
d.load(method);
|
2016-08-04 10:38:43 +00:00
|
|
|
d.setCoreIndex(i);
|
2016-05-29 19:33:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
|
|
|
|
|
|
|
|
QV4::Scope scope(engine);
|
|
|
|
Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocObject<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue());
|
|
|
|
mo->init(engine);
|
|
|
|
return mo->asReturnedValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMetaObjectWrapper::init(ExecutionEngine *) {
|
|
|
|
const QMetaObject & mo = *d()->metaObject;
|
|
|
|
|
|
|
|
for (int i = 0; i < mo.enumeratorCount(); i++) {
|
|
|
|
QMetaEnum Enum = mo.enumerator(i);
|
|
|
|
for (int k = 0; k < Enum.keyCount(); k++) {
|
|
|
|
const char* key = Enum.key(k);
|
|
|
|
const int value = Enum.value(k);
|
|
|
|
defineReadonlyProperty(QLatin1String(key), Primitive::fromInt32(value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-22 08:12:13 +00:00
|
|
|
void QMetaObjectWrapper::construct(const Managed *m, Scope &scope, CallData *callData)
|
2016-05-29 19:33:59 +00:00
|
|
|
{
|
|
|
|
const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(m);
|
2016-06-22 08:12:13 +00:00
|
|
|
scope.result = This->constructInternal(callData);
|
2016-05-29 19:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const {
|
|
|
|
|
|
|
|
d()->ensureConstructorsCache();
|
|
|
|
|
|
|
|
ExecutionEngine *v4 = engine();
|
|
|
|
const QMetaObject* mo = d()->metaObject;
|
2016-09-07 11:06:40 +00:00
|
|
|
if (d()->constructorCount == 0) {
|
2016-05-29 19:33:59 +00:00
|
|
|
return v4->throwTypeError(QStringLiteral("%1 has no invokable constructor")
|
|
|
|
.arg(QLatin1String(mo->className())));
|
|
|
|
}
|
|
|
|
|
|
|
|
Scope scope(v4);
|
|
|
|
Scoped<QObjectWrapper> object(scope);
|
|
|
|
|
2016-09-07 11:06:40 +00:00
|
|
|
if (d()->constructorCount == 1) {
|
|
|
|
object = callConstructor(d()->constructors[0], v4, callData);
|
2016-05-29 19:33:59 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
object = callOverloadedConstructor(v4, callData);
|
|
|
|
}
|
|
|
|
Scoped<QMetaObjectWrapper> metaObject(scope, this);
|
|
|
|
object->defineDefaultProperty(v4->id_constructor(), metaObject);
|
|
|
|
object->setPrototype(const_cast<QMetaObjectWrapper*>(this));
|
|
|
|
return object.asReturnedValue();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
|
|
|
|
|
|
|
|
const QMetaObject* mo = d()->metaObject;
|
|
|
|
const QQmlStaticMetaObject object(mo);
|
2016-09-02 06:27:18 +00:00
|
|
|
return CallPrecise(object, data, engine, callArgs, QMetaObject::CreateInstance);
|
2016-05-29 19:33:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const {
|
2016-09-07 11:06:40 +00:00
|
|
|
const int numberOfConstructors = d()->constructorCount;
|
2016-05-29 19:33:59 +00:00
|
|
|
const int argumentCount = callArgs->argc;
|
|
|
|
const QQmlStaticMetaObject object(d()->metaObject);
|
|
|
|
|
|
|
|
QQmlPropertyData best;
|
|
|
|
int bestParameterScore = INT_MAX;
|
|
|
|
int bestMatchScore = INT_MAX;
|
|
|
|
|
|
|
|
QV4::Scope scope(engine);
|
|
|
|
QV4::ScopedValue v(scope);
|
|
|
|
|
|
|
|
for (int i = 0; i < numberOfConstructors; i++) {
|
2016-09-07 11:06:40 +00:00
|
|
|
const QQmlPropertyData & attempt = d()->constructors[i];
|
2016-05-29 19:33:59 +00:00
|
|
|
int methodArgumentCount = 0;
|
|
|
|
int *methodArgTypes = 0;
|
|
|
|
if (attempt.hasArguments()) {
|
2016-07-05 08:35:56 +00:00
|
|
|
QQmlMetaObject::ArgTypeStorage storage;
|
2016-08-04 10:38:43 +00:00
|
|
|
int *args = object.constructorParameterTypes(attempt.coreIndex(), &storage, 0);
|
2016-05-29 19:33:59 +00:00
|
|
|
if (!args) // Must be an unknown argument
|
|
|
|
continue;
|
|
|
|
|
|
|
|
methodArgumentCount = args[0];
|
|
|
|
methodArgTypes = args + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (methodArgumentCount > argumentCount)
|
|
|
|
continue; // We don't have sufficient arguments to call this method
|
|
|
|
|
|
|
|
int methodParameterScore = argumentCount - methodArgumentCount;
|
|
|
|
if (methodParameterScore > bestParameterScore)
|
|
|
|
continue; // We already have a better option
|
|
|
|
|
|
|
|
int methodMatchScore = 0;
|
|
|
|
for (int ii = 0; ii < methodArgumentCount; ++ii)
|
|
|
|
methodMatchScore += MatchScore((v = callArgs->args[ii]), methodArgTypes[ii]);
|
|
|
|
|
|
|
|
if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
|
|
|
|
best = attempt;
|
|
|
|
bestParameterScore = methodParameterScore;
|
|
|
|
bestMatchScore = methodMatchScore;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bestParameterScore == 0 && bestMatchScore == 0)
|
|
|
|
break; // We can't get better than that
|
|
|
|
};
|
|
|
|
|
|
|
|
if (best.isValid()) {
|
|
|
|
return CallPrecise(object, best, engine, callArgs, QMetaObject::CreateInstance);
|
|
|
|
} else {
|
|
|
|
QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
|
|
|
|
for (int i = 0; i < numberOfConstructors; i++) {
|
2016-09-07 11:06:40 +00:00
|
|
|
const QQmlPropertyData & candidate = d()->constructors[i];
|
2016-05-29 19:33:59 +00:00
|
|
|
error += QLatin1String("\n ") +
|
2016-08-04 10:38:43 +00:00
|
|
|
QString::fromUtf8(d()->metaObject->constructor(candidate.coreIndex())
|
2016-05-29 19:33:59 +00:00
|
|
|
.methodSignature());
|
|
|
|
}
|
|
|
|
|
|
|
|
return engine->throwError(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QMetaObjectWrapper::isEqualTo(Managed *a, Managed *b)
|
|
|
|
{
|
|
|
|
Q_ASSERT(a->as<QMetaObjectWrapper>());
|
|
|
|
QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
|
|
|
|
QMetaObjectWrapper *bMetaObject = b->as<QMetaObjectWrapper>();
|
|
|
|
if (!bMetaObject)
|
|
|
|
return true;
|
|
|
|
return aMetaObject->metaObject() == bMetaObject->metaObject();
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_OBJECT_VTABLE(QMetaObjectWrapper);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-09-09 13:37:57 +00:00
|
|
|
void Heap::QmlSignalHandler::init(QObject *object, int signalIndex)
|
2013-06-03 12:41:17 +00:00
|
|
|
{
|
2016-09-09 13:37:57 +00:00
|
|
|
Object::init();
|
|
|
|
this->signalIndex = signalIndex;
|
2016-09-07 11:31:14 +00:00
|
|
|
setObject(object);
|
2013-06-03 12:41:17 +00:00
|
|
|
}
|
|
|
|
|
2014-01-20 12:51:00 +00:00
|
|
|
DEFINE_OBJECT_VTABLE(QmlSignalHandler);
|
2013-06-03 12:41:17 +00:00
|
|
|
|
2015-09-09 13:23:18 +00:00
|
|
|
void QmlSignalHandler::initProto(ExecutionEngine *engine)
|
|
|
|
{
|
2016-09-09 13:37:57 +00:00
|
|
|
if (engine->signalHandlerPrototype()->d_unchecked())
|
2015-09-09 13:23:18 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
Scope scope(engine);
|
|
|
|
ScopedObject o(scope, engine->newObject());
|
|
|
|
QV4::ScopedString connect(scope, engine->newIdentifier(QStringLiteral("connect")));
|
|
|
|
QV4::ScopedString disconnect(scope, engine->newIdentifier(QStringLiteral("disconnect")));
|
|
|
|
o->put(connect, QV4::ScopedValue(scope, engine->functionPrototype()->get(connect)));
|
|
|
|
o->put(disconnect, QV4::ScopedValue(scope, engine->functionPrototype()->get(disconnect)));
|
|
|
|
|
|
|
|
engine->jsObjects[QV4::ExecutionEngine::SignalHandlerProto] = o->d();
|
|
|
|
}
|
|
|
|
|
2015-04-28 13:38:09 +00:00
|
|
|
void MultiplyWrappedQObjectMap::insert(QObject *key, Heap::Object *value)
|
2013-06-04 12:28:13 +00:00
|
|
|
{
|
2015-04-28 13:38:09 +00:00
|
|
|
QV4::WeakValue v;
|
|
|
|
v.set(value->internalClass->engine, value);
|
|
|
|
QHash<QObject*, QV4::WeakValue>::insert(key, v);
|
2013-06-04 12:28:13 +00:00
|
|
|
connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
|
|
|
|
}
|
|
|
|
|
2015-04-28 13:38:09 +00:00
|
|
|
|
|
|
|
|
2013-06-04 12:28:13 +00:00
|
|
|
MultiplyWrappedQObjectMap::Iterator MultiplyWrappedQObjectMap::erase(MultiplyWrappedQObjectMap::Iterator it)
|
|
|
|
{
|
|
|
|
disconnect(it.key(), SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*)));
|
2015-04-28 13:38:09 +00:00
|
|
|
return QHash<QObject*, QV4::WeakValue>::erase(it);
|
2013-06-04 12:28:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MultiplyWrappedQObjectMap::remove(QObject *key)
|
|
|
|
{
|
|
|
|
Iterator it = find(key);
|
|
|
|
if (it == end())
|
|
|
|
return;
|
|
|
|
erase(it);
|
|
|
|
}
|
|
|
|
|
2015-05-07 14:22:24 +00:00
|
|
|
void MultiplyWrappedQObjectMap::mark(QObject *key, ExecutionEngine *engine)
|
|
|
|
{
|
|
|
|
Iterator it = find(key);
|
|
|
|
if (it == end())
|
|
|
|
return;
|
|
|
|
it->markOnce(engine);
|
|
|
|
}
|
|
|
|
|
2013-06-04 12:28:13 +00:00
|
|
|
void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object)
|
|
|
|
{
|
2015-04-28 13:38:09 +00:00
|
|
|
QHash<QObject*, QV4::WeakValue>::remove(object);
|
2013-06-04 12:28:13 +00:00
|
|
|
}
|
|
|
|
|
2011-09-28 07:57:04 +00:00
|
|
|
QT_END_NAMESPACE
|
|
|
|
|