2015-12-10 05:06:13 +02:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
2019-06-03 14:21:30 +00:00
|
|
|
** Copyright (C) 2016-2019 Ivailo Monev
|
2015-12-10 05:06:13 +02:00
|
|
|
**
|
2019-06-03 13:38:02 +00:00
|
|
|
** This file is part of the QtDeclarative module of the Katie Toolkit.
|
2015-12-10 05:06:13 +02:00
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3.0 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qdeclarativeobjectscriptclass_p.h"
|
|
|
|
|
|
|
|
#include "qdeclarativeengine_p.h"
|
|
|
|
#include "qdeclarativecontext_p.h"
|
|
|
|
#include "qdeclarativedata_p.h"
|
|
|
|
#include "qdeclarativetypenamescriptclass_p.h"
|
|
|
|
#include "qdeclarativelistscriptclass_p.h"
|
|
|
|
#include "qdeclarativebinding_p.h"
|
|
|
|
#include "qdeclarativeguard_p.h"
|
|
|
|
#include "qdeclarativevmemetaobject_p.h"
|
2016-08-28 01:33:18 +00:00
|
|
|
#include "qdeclarativecommon_p.h"
|
2015-12-10 05:06:13 +02:00
|
|
|
|
|
|
|
#include <QtCore/qtimer.h>
|
|
|
|
#include <QtCore/qvarlengtharray.h>
|
|
|
|
#include <QtScript/qscriptcontextinfo.h>
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
|
|
struct ObjectData : public QScriptDeclarativeClass::Object {
|
|
|
|
ObjectData(QObject *o, int t) : object(o), type(t) {
|
|
|
|
if (o) {
|
|
|
|
QDeclarativeData *ddata = QDeclarativeData::get(object, true);
|
|
|
|
if (ddata) ddata->objectDataRefCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~ObjectData() {
|
|
|
|
if (object && !object->parent()) {
|
|
|
|
QDeclarativeData *ddata = QDeclarativeData::get(object, false);
|
|
|
|
if (ddata && !ddata->indestructible && 0 == --ddata->objectDataRefCount)
|
|
|
|
object->deleteLater();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeGuard<QObject> object;
|
|
|
|
int type;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
The QDeclarativeObjectScriptClass handles property access for QObjects
|
|
|
|
via QtScript. It is also used to provide a more useful API in
|
|
|
|
QtScript for QML.
|
|
|
|
*/
|
|
|
|
QDeclarativeObjectScriptClass::QDeclarativeObjectScriptClass(QDeclarativeEngine *bindEngine)
|
|
|
|
: QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
|
|
|
|
methods(bindEngine), lastData(0), engine(bindEngine)
|
|
|
|
{
|
|
|
|
QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
|
|
|
|
|
|
|
|
m_destroy = scriptEngine->newFunction(destroy);
|
|
|
|
m_destroyId = createPersistentIdentifier(QLatin1String("destroy"));
|
|
|
|
m_toString = scriptEngine->newFunction(tostring);
|
|
|
|
m_toStringId = createPersistentIdentifier(QLatin1String("toString"));
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeObjectScriptClass::~QDeclarativeObjectScriptClass()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptValue QDeclarativeObjectScriptClass::newQObject(QObject *object, int type)
|
|
|
|
{
|
|
|
|
QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
|
|
|
|
|
|
|
|
if (!object)
|
|
|
|
return scriptEngine->nullValue();
|
|
|
|
// return newObject(scriptEngine, this, new ObjectData(object, type));
|
|
|
|
|
|
|
|
if (QObjectPrivate::get(object)->wasDeleted)
|
|
|
|
return scriptEngine->undefinedValue();
|
|
|
|
|
|
|
|
QDeclarativeData *ddata = QDeclarativeData::get(object, true);
|
|
|
|
|
|
|
|
if (!ddata) {
|
|
|
|
return scriptEngine->undefinedValue();
|
|
|
|
} else if (!ddata->indestructible && !object->parent()) {
|
|
|
|
return newObject(scriptEngine, this, new ObjectData(object, type));
|
|
|
|
} else if (!ddata->scriptValue) {
|
|
|
|
ddata->scriptValue = new QScriptValue(newObject(scriptEngine, this, new ObjectData(object, type)));
|
|
|
|
return *ddata->scriptValue;
|
|
|
|
} else if (ddata->scriptValue->engine() == QDeclarativeEnginePrivate::getScriptEngine(engine)) {
|
|
|
|
return *ddata->scriptValue;
|
|
|
|
} else {
|
|
|
|
return newObject(scriptEngine, this, new ObjectData(object, type));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QObject *QDeclarativeObjectScriptClass::toQObject(const QScriptValue &value) const
|
|
|
|
{
|
|
|
|
return value.toQObject();
|
|
|
|
}
|
|
|
|
|
|
|
|
int QDeclarativeObjectScriptClass::objectType(const QScriptValue &value) const
|
|
|
|
{
|
|
|
|
if (scriptClass(value) != this)
|
|
|
|
return QVariant::Invalid;
|
|
|
|
|
|
|
|
Object *o = object(value);
|
|
|
|
return ((ObjectData*)(o))->type;
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptClass::QueryFlags
|
|
|
|
QDeclarativeObjectScriptClass::queryProperty(Object *object, const Identifier &name,
|
|
|
|
QScriptClass::QueryFlags flags)
|
|
|
|
{
|
|
|
|
return queryProperty(toQObject(object), name, flags, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptClass::QueryFlags
|
|
|
|
QDeclarativeObjectScriptClass::queryProperty(QObject *obj, const Identifier &name,
|
|
|
|
QScriptClass::QueryFlags flags, QDeclarativeContextData *evalContext,
|
|
|
|
QueryHints hints)
|
|
|
|
{
|
|
|
|
Q_UNUSED(flags);
|
|
|
|
lastData = 0;
|
|
|
|
lastTNData = 0;
|
|
|
|
|
|
|
|
if (name == m_destroyId.identifier ||
|
|
|
|
name == m_toStringId.identifier)
|
|
|
|
return QScriptClass::HandlesReadAccess;
|
|
|
|
|
|
|
|
if (!obj)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
|
|
|
|
lastData = QDeclarativePropertyCache::property(engine, obj, name, local);
|
|
|
|
if ((hints & ImplicitObject) && lastData && lastData->revision != 0) {
|
|
|
|
|
|
|
|
QDeclarativeData *ddata = QDeclarativeData::get(obj);
|
|
|
|
if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(lastData))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastData)
|
|
|
|
return QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess;
|
|
|
|
|
|
|
|
if (!(hints & SkipAttachedProperties)) {
|
|
|
|
if (!evalContext && context()) {
|
|
|
|
// Global object, QScriptContext activation object, QDeclarativeContext object
|
|
|
|
QScriptValue scopeNode = scopeChainValue(context(), -3);
|
|
|
|
if (scopeNode.isValid()) {
|
|
|
|
Q_ASSERT(scriptClass(scopeNode) == enginePrivate->contextClass);
|
|
|
|
|
|
|
|
evalContext = enginePrivate->contextClass->contextFromValue(scopeNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (evalContext && evalContext->imports) {
|
|
|
|
QDeclarativeTypeNameCache::Data *data = evalContext->imports->data(name);
|
|
|
|
if (data) {
|
|
|
|
lastTNData = data;
|
|
|
|
return QScriptClass::HandlesReadAccess;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(hints & ImplicitObject)) {
|
|
|
|
local.coreIndex = -1;
|
|
|
|
lastData = &local;
|
|
|
|
return QScriptClass::HandlesWriteAccess;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeObjectScriptClass::Value
|
|
|
|
QDeclarativeObjectScriptClass::property(Object *object, const Identifier &name)
|
|
|
|
{
|
|
|
|
return property(toQObject(object), name);
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeObjectScriptClass::Value
|
|
|
|
QDeclarativeObjectScriptClass::property(QObject *obj, const Identifier &name)
|
|
|
|
{
|
|
|
|
QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
|
|
|
|
|
|
|
|
if (name == m_destroyId.identifier)
|
|
|
|
return Value(scriptEngine, m_destroy);
|
|
|
|
else if (name == m_toStringId.identifier)
|
|
|
|
return Value(scriptEngine, m_toString);
|
|
|
|
|
|
|
|
if (lastData && !lastData->isValid())
|
|
|
|
return Value();
|
|
|
|
|
|
|
|
Q_ASSERT(obj);
|
|
|
|
|
|
|
|
QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
|
|
|
|
|
|
|
|
if (lastTNData) {
|
|
|
|
|
|
|
|
if (lastTNData->type)
|
|
|
|
return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->type));
|
|
|
|
else
|
|
|
|
return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->typeNamespace));
|
|
|
|
|
|
|
|
} else if (lastData->flags & QDeclarativePropertyCache::Data::IsFunction) {
|
|
|
|
if (lastData->flags & QDeclarativePropertyCache::Data::IsVMEFunction) {
|
|
|
|
return Value(scriptEngine, ((QDeclarativeVMEMetaObject *)(obj->metaObject()))->vmeMethod(lastData->coreIndex));
|
|
|
|
} else {
|
|
|
|
// Uncomment to use QtScript method call logic
|
|
|
|
// QScriptValue sobj = scriptEngine->newQObject(obj);
|
|
|
|
// return Value(scriptEngine, sobj.property(toString(name)));
|
|
|
|
return Value(scriptEngine, methods.newMethod(obj, lastData));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (enginePriv->captureProperties && !(lastData->flags & QDeclarativePropertyCache::Data::IsConstant)) {
|
|
|
|
if (lastData->coreIndex == 0) {
|
|
|
|
enginePriv->capturedProperties <<
|
|
|
|
QDeclarativeEnginePrivate::CapturedProperty(QDeclarativeData::get(obj, true)->objectNameNotifier());
|
|
|
|
} else {
|
|
|
|
enginePriv->capturedProperties <<
|
|
|
|
QDeclarativeEnginePrivate::CapturedProperty(obj, lastData->coreIndex, lastData->notifyIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (QDeclarativeValueTypeFactory::isValueType((uint)lastData->propType)) {
|
|
|
|
QDeclarativeValueType *valueType = enginePriv->valueTypes[lastData->propType];
|
|
|
|
if (valueType)
|
|
|
|
return Value(scriptEngine, enginePriv->valueTypeClass->newObject(obj, lastData->coreIndex, valueType));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) {
|
|
|
|
return Value(scriptEngine, enginePriv->listClass->newList(obj, lastData->coreIndex, lastData->propType));
|
|
|
|
} else if (lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) {
|
|
|
|
QObject *rv = 0;
|
|
|
|
void *args[] = { &rv, 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
|
|
|
|
return Value(scriptEngine, newQObject(rv, lastData->propType));
|
|
|
|
} else if (lastData->flags & QDeclarativePropertyCache::Data::IsQScriptValue) {
|
|
|
|
QScriptValue rv = scriptEngine->nullValue();
|
|
|
|
void *args[] = { &rv, 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
|
|
|
|
return Value(scriptEngine, rv);
|
|
|
|
} else if (lastData->propType == QMetaType::QReal) {
|
|
|
|
qreal rv = 0;
|
|
|
|
void *args[] = { &rv, 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
|
|
|
|
return Value(scriptEngine, rv);
|
|
|
|
} else if (lastData->propType == QMetaType::Int || lastData->flags & QDeclarativePropertyCache::Data::IsEnumType) {
|
|
|
|
int rv = 0;
|
|
|
|
void *args[] = { &rv, 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
|
|
|
|
return Value(scriptEngine, rv);
|
|
|
|
} else if (lastData->propType == QMetaType::Bool) {
|
|
|
|
bool rv = false;
|
|
|
|
void *args[] = { &rv, 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
|
|
|
|
return Value(scriptEngine, rv);
|
|
|
|
} else if (lastData->propType == QMetaType::QString) {
|
|
|
|
QString rv;
|
|
|
|
void *args[] = { &rv, 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
|
|
|
|
return Value(scriptEngine, rv);
|
|
|
|
} else if (lastData->propType == QMetaType::UInt) {
|
|
|
|
uint rv = 0;
|
|
|
|
void *args[] = { &rv, 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
|
|
|
|
return Value(scriptEngine, rv);
|
|
|
|
} else if (lastData->propType == QMetaType::Float) {
|
|
|
|
float rv = 0;
|
|
|
|
void *args[] = { &rv, 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
|
|
|
|
return Value(scriptEngine, rv);
|
|
|
|
} else if (lastData->propType == QMetaType::Double) {
|
|
|
|
double rv = 0;
|
|
|
|
void *args[] = { &rv, 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args);
|
|
|
|
return Value(scriptEngine, rv);
|
|
|
|
} else {
|
|
|
|
QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj);
|
|
|
|
return Value(scriptEngine, enginePriv->scriptValueFromVariant(var));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QDeclarativeObjectScriptClass::setProperty(Object *object,
|
|
|
|
const Identifier &name,
|
|
|
|
const QScriptValue &value)
|
|
|
|
{
|
|
|
|
return setProperty(toQObject(object), name, value, context());
|
|
|
|
}
|
2016-08-28 01:33:18 +00:00
|
|
|
|
2015-12-10 05:06:13 +02:00
|
|
|
void QDeclarativeObjectScriptClass::setProperty(QObject *obj,
|
|
|
|
const Identifier &name,
|
|
|
|
const QScriptValue &value,
|
|
|
|
QScriptContext *context,
|
|
|
|
QDeclarativeContextData *evalContext)
|
|
|
|
{
|
|
|
|
Q_UNUSED(name);
|
|
|
|
|
|
|
|
Q_ASSERT(obj);
|
|
|
|
Q_ASSERT(lastData);
|
|
|
|
Q_ASSERT(context);
|
|
|
|
|
|
|
|
if (!lastData->isValid()) {
|
|
|
|
QString error = QLatin1String("Cannot assign to non-existent property \"") +
|
|
|
|
toString(name) + QLatin1Char('\"');
|
|
|
|
context->throwError(error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(lastData->flags & QDeclarativePropertyCache::Data::IsWritable) &&
|
|
|
|
!(lastData->flags & QDeclarativePropertyCache::Data::IsQList)) {
|
|
|
|
QString error = QLatin1String("Cannot assign to read-only property \"") +
|
|
|
|
toString(name) + QLatin1Char('\"');
|
|
|
|
context->throwError(error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine);
|
|
|
|
|
|
|
|
if (!evalContext) {
|
|
|
|
// Global object, QScriptContext activation object, QDeclarativeContext object
|
|
|
|
QScriptValue scopeNode = scopeChainValue(context, -3);
|
|
|
|
if (scopeNode.isValid()) {
|
|
|
|
Q_ASSERT(scriptClass(scopeNode) == enginePriv->contextClass);
|
|
|
|
|
|
|
|
evalContext = enginePriv->contextClass->contextFromValue(scopeNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeBinding *newBinding = 0;
|
|
|
|
if (value.isFunction() && !value.isRegExp()) {
|
|
|
|
QScriptContextInfo ctxtInfo(context);
|
|
|
|
QDeclarativePropertyCache::ValueTypeData valueTypeData;
|
|
|
|
|
|
|
|
newBinding = new QDeclarativeBinding(value, obj, evalContext);
|
|
|
|
newBinding->setSourceLocation(ctxtInfo.fileName(), ctxtInfo.functionStartLineNumber());
|
|
|
|
newBinding->setTarget(QDeclarativePropertyPrivate::restore(*lastData, valueTypeData, obj, evalContext));
|
|
|
|
if (newBinding->expression().contains(QLatin1String("this")))
|
|
|
|
newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeAbstractBinding *delBinding =
|
|
|
|
QDeclarativePropertyPrivate::setBinding(obj, lastData->coreIndex, -1, newBinding);
|
|
|
|
if (delBinding)
|
|
|
|
delBinding->destroy();
|
|
|
|
|
|
|
|
if (value.isNull() && lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) {
|
|
|
|
QObject *o = 0;
|
|
|
|
int status = -1;
|
|
|
|
int flags = 0;
|
|
|
|
void *argv[] = { &o, 0, &status, &flags };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::WriteProperty, lastData->coreIndex, argv);
|
|
|
|
} else if (value.isUndefined() && lastData->flags & QDeclarativePropertyCache::Data::IsResettable) {
|
|
|
|
void *a[] = { 0 };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::ResetProperty, lastData->coreIndex, a);
|
|
|
|
} else if (value.isUndefined() && lastData->propType == qMetaTypeId<QVariant>()) {
|
|
|
|
QDeclarativePropertyPrivate::write(obj, *lastData, QVariant(), evalContext);
|
|
|
|
} else if (value.isUndefined()) {
|
|
|
|
QString error = QLatin1String("Cannot assign [undefined] to ") +
|
|
|
|
QLatin1String(QMetaType::typeName(lastData->propType));
|
|
|
|
context->throwError(error);
|
|
|
|
} else if (value.isFunction() && !value.isRegExp()) {
|
|
|
|
// this is handled by the binding creation above
|
|
|
|
} else {
|
|
|
|
//### expand optimization for other known types
|
|
|
|
if (lastData->propType == QMetaType::Int && value.isNumber()) {
|
|
|
|
int rawValue = qRoundDouble(value.toNumber());
|
|
|
|
int status = -1;
|
|
|
|
int flags = 0;
|
|
|
|
void *a[] = { (void *)&rawValue, 0, &status, &flags };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::WriteProperty,
|
|
|
|
lastData->coreIndex, a);
|
|
|
|
return;
|
|
|
|
} else if (lastData->propType == QMetaType::QReal && value.isNumber()) {
|
|
|
|
qreal rawValue = qreal(value.toNumber());
|
|
|
|
int status = -1;
|
|
|
|
int flags = 0;
|
|
|
|
void *a[] = { (void *)&rawValue, 0, &status, &flags };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::WriteProperty,
|
|
|
|
lastData->coreIndex, a);
|
|
|
|
return;
|
|
|
|
} else if (lastData->propType == QMetaType::QString && value.isString()) {
|
|
|
|
const QString &rawValue = value.toString();
|
|
|
|
int status = -1;
|
|
|
|
int flags = 0;
|
|
|
|
void *a[] = { (void *)&rawValue, 0, &status, &flags };
|
|
|
|
QMetaObject::metacall(obj, QMetaObject::WriteProperty,
|
|
|
|
lastData->coreIndex, a);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariant v;
|
|
|
|
if (lastData->flags & QDeclarativePropertyCache::Data::IsQList)
|
|
|
|
v = enginePriv->scriptValueToVariant(value, qMetaTypeId<QList<QObject *> >());
|
|
|
|
else
|
|
|
|
v = enginePriv->scriptValueToVariant(value, lastData->propType);
|
|
|
|
|
|
|
|
if (!QDeclarativePropertyPrivate::write(obj, *lastData, v, evalContext)) {
|
|
|
|
const char *valueType = 0;
|
|
|
|
if (v.userType() == QVariant::Invalid) valueType = "null";
|
|
|
|
else valueType = QMetaType::typeName(v.userType());
|
|
|
|
|
|
|
|
QString error = QLatin1String("Cannot assign ") +
|
|
|
|
QLatin1String(valueType) +
|
|
|
|
QLatin1String(" to ") +
|
|
|
|
QLatin1String(QMetaType::typeName(lastData->propType));
|
|
|
|
context->throwError(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QDeclarativeObjectScriptClass::isQObject() const
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
QObject *QDeclarativeObjectScriptClass::toQObject(Object *object, bool *ok)
|
|
|
|
{
|
|
|
|
if (ok) *ok = true;
|
|
|
|
|
|
|
|
ObjectData *data = (ObjectData*)object;
|
|
|
|
return data->object.data();
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptValue QDeclarativeObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *)
|
|
|
|
{
|
|
|
|
QObject* obj = context->thisObject().toQObject();
|
|
|
|
|
|
|
|
QString ret;
|
|
|
|
if(obj){
|
|
|
|
QString objectName = obj->objectName();
|
|
|
|
|
|
|
|
ret += QString::fromUtf8(obj->metaObject()->className());
|
|
|
|
ret += QLatin1String("(0x");
|
|
|
|
ret += QString::number((quintptr)obj,16);
|
|
|
|
|
|
|
|
if (!objectName.isEmpty()) {
|
|
|
|
ret += QLatin1String(", \"");
|
|
|
|
ret += objectName;
|
|
|
|
ret += QLatin1Char('\"');
|
|
|
|
}
|
|
|
|
|
|
|
|
ret += QLatin1Char(')');
|
|
|
|
}else{
|
|
|
|
ret += QLatin1String("null");
|
|
|
|
}
|
|
|
|
return QScriptValue(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptValue QDeclarativeObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine)
|
|
|
|
{
|
|
|
|
QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
|
|
|
|
QScriptValue that = context->thisObject();
|
|
|
|
|
|
|
|
if (scriptClass(that) != p->objectClass)
|
|
|
|
return engine->undefinedValue();
|
|
|
|
|
|
|
|
ObjectData *data = (ObjectData *)p->objectClass->object(that);
|
|
|
|
if (!data->object)
|
|
|
|
return engine->undefinedValue();
|
|
|
|
|
|
|
|
QDeclarativeData *ddata = QDeclarativeData::get(data->object, false);
|
|
|
|
if (!ddata || ddata->indestructible)
|
|
|
|
return engine->currentContext()->throwError(QLatin1String("Invalid attempt to destroy() an indestructible object"));
|
|
|
|
|
|
|
|
QObject *obj = data->object;
|
|
|
|
int delay = 0;
|
|
|
|
if (context->argumentCount() > 0)
|
|
|
|
delay = context->argument(0).toInt32();
|
|
|
|
if (delay > 0)
|
|
|
|
QTimer::singleShot(delay, obj, SLOT(deleteLater()));
|
|
|
|
else
|
|
|
|
obj->deleteLater();
|
|
|
|
|
|
|
|
return engine->undefinedValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList QDeclarativeObjectScriptClass::propertyNames(Object *object)
|
|
|
|
{
|
|
|
|
QObject *obj = toQObject(object);
|
|
|
|
if (!obj)
|
|
|
|
return QStringList();
|
|
|
|
|
|
|
|
QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine);
|
|
|
|
|
|
|
|
QDeclarativePropertyCache *cache = 0;
|
|
|
|
QDeclarativeData *ddata = QDeclarativeData::get(obj);
|
|
|
|
if (ddata)
|
|
|
|
cache = ddata->propertyCache;
|
|
|
|
if (!cache) {
|
|
|
|
cache = enginePrivate->cache(obj);
|
|
|
|
if (cache) {
|
|
|
|
if (ddata) { cache->addref(); ddata->propertyCache = cache; }
|
|
|
|
} else {
|
|
|
|
// Not cachable - fall back to QMetaObject (eg. dynamic meta object)
|
|
|
|
// XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal.
|
|
|
|
// XXX This is a workaround for QTBUG-9420.
|
|
|
|
const QMetaObject *mo = obj->metaObject();
|
|
|
|
QStringList r;
|
|
|
|
int pc = mo->propertyCount();
|
|
|
|
int po = mo->propertyOffset();
|
|
|
|
for (int i=po; i<pc; ++i)
|
|
|
|
r += QString::fromUtf8(mo->property(i).name());
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cache->propertyNames();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QDeclarativeObjectScriptClass::compare(Object *o1, Object *o2)
|
|
|
|
{
|
|
|
|
ObjectData *d1 = (ObjectData *)o1;
|
|
|
|
ObjectData *d2 = (ObjectData *)o2;
|
|
|
|
|
|
|
|
return d1 == d2 || d1->object == d2->object;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MethodData : public QScriptDeclarativeClass::Object {
|
|
|
|
MethodData(QObject *o, const QDeclarativePropertyCache::Data &d) : object(o), data(d) {}
|
|
|
|
|
|
|
|
QDeclarativeGuard<QObject> object;
|
|
|
|
QDeclarativePropertyCache::Data data;
|
|
|
|
};
|
|
|
|
|
|
|
|
QDeclarativeObjectMethodScriptClass::QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *bindEngine)
|
|
|
|
: QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)),
|
|
|
|
engine(bindEngine)
|
|
|
|
{
|
|
|
|
qRegisterMetaType<QList<QObject *> >("QList<QObject *>");
|
|
|
|
|
|
|
|
setSupportsCall(true);
|
|
|
|
|
|
|
|
QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
|
|
|
|
|
|
|
|
m_connect = scriptEngine->newFunction(connect);
|
|
|
|
m_connectId = createPersistentIdentifier(QLatin1String("connect"));
|
|
|
|
m_disconnect = scriptEngine->newFunction(disconnect);
|
|
|
|
m_disconnectId = createPersistentIdentifier(QLatin1String("disconnect"));
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeObjectMethodScriptClass::~QDeclarativeObjectMethodScriptClass()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptValue QDeclarativeObjectMethodScriptClass::newMethod(QObject *object, const QDeclarativePropertyCache::Data *method)
|
|
|
|
{
|
|
|
|
QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
|
|
|
|
|
|
|
|
return newObject(scriptEngine, this, new MethodData(object, *method));
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptValue QDeclarativeObjectMethodScriptClass::connect(QScriptContext *context, QScriptEngine *engine)
|
|
|
|
{
|
|
|
|
QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
|
|
|
|
|
|
|
|
QScriptValue that = context->thisObject();
|
|
|
|
if (&p->objectClass->methods != scriptClass(that))
|
|
|
|
return engine->undefinedValue();
|
|
|
|
|
|
|
|
MethodData *data = (MethodData *)object(that);
|
|
|
|
|
|
|
|
if (!data->object || context->argumentCount() == 0)
|
|
|
|
return engine->undefinedValue();
|
|
|
|
|
|
|
|
QByteArray signal("2");
|
|
|
|
signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
|
|
|
|
|
|
|
|
if (context->argumentCount() == 1) {
|
|
|
|
qScriptConnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
|
|
|
|
} else {
|
|
|
|
qScriptConnect(data->object, signal.constData(), context->argument(0), context->argument(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
return engine->undefinedValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptValue QDeclarativeObjectMethodScriptClass::disconnect(QScriptContext *context, QScriptEngine *engine)
|
|
|
|
{
|
|
|
|
QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine);
|
|
|
|
|
|
|
|
QScriptValue that = context->thisObject();
|
|
|
|
if (&p->objectClass->methods != scriptClass(that))
|
|
|
|
return engine->undefinedValue();
|
|
|
|
|
|
|
|
MethodData *data = (MethodData *)object(that);
|
|
|
|
|
|
|
|
if (!data->object || context->argumentCount() == 0)
|
|
|
|
return engine->undefinedValue();
|
|
|
|
|
|
|
|
QByteArray signal("2");
|
|
|
|
signal.append(data->object->metaObject()->method(data->data.coreIndex).signature());
|
|
|
|
|
|
|
|
if (context->argumentCount() == 1) {
|
|
|
|
qScriptDisconnect(data->object, signal.constData(), QScriptValue(), context->argument(0));
|
|
|
|
} else {
|
|
|
|
qScriptDisconnect(data->object, signal.constData(), context->argument(0), context->argument(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
return engine->undefinedValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptClass::QueryFlags
|
|
|
|
QDeclarativeObjectMethodScriptClass::queryProperty(Object *, const Identifier &name,
|
|
|
|
QScriptClass::QueryFlags flags)
|
|
|
|
{
|
|
|
|
Q_UNUSED(flags);
|
|
|
|
if (name == m_connectId.identifier || name == m_disconnectId.identifier)
|
|
|
|
return QScriptClass::HandlesReadAccess;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeObjectMethodScriptClass::Value
|
|
|
|
QDeclarativeObjectMethodScriptClass::property(Object *, const Identifier &name)
|
|
|
|
{
|
|
|
|
QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
|
|
|
|
|
|
|
|
if (name == m_connectId.identifier)
|
|
|
|
return Value(scriptEngine, m_connect);
|
|
|
|
else if (name == m_disconnectId.identifier)
|
|
|
|
return Value(scriptEngine, m_disconnect);
|
|
|
|
else
|
|
|
|
return Value();
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
struct MetaCallArgument {
|
|
|
|
inline MetaCallArgument();
|
|
|
|
inline ~MetaCallArgument();
|
|
|
|
inline void *dataPtr();
|
|
|
|
|
|
|
|
inline void initAsType(int type, QDeclarativeEngine *);
|
|
|
|
void fromScriptValue(int type, QDeclarativeEngine *, const QScriptValue &);
|
|
|
|
inline QScriptDeclarativeClass::Value toValue(QDeclarativeEngine *);
|
|
|
|
|
|
|
|
private:
|
|
|
|
MetaCallArgument(const MetaCallArgument &);
|
|
|
|
|
|
|
|
inline void cleanup();
|
|
|
|
|
|
|
|
union {
|
|
|
|
float floatValue;
|
|
|
|
double doubleValue;
|
|
|
|
quint32 intValue;
|
|
|
|
bool boolValue;
|
|
|
|
QObject *qobjectPtr;
|
|
|
|
|
|
|
|
char allocData[sizeof(QVariant)];
|
|
|
|
};
|
|
|
|
|
|
|
|
// Pointers to allocData
|
|
|
|
union {
|
|
|
|
QString *qstringPtr;
|
|
|
|
QVariant *qvariantPtr;
|
|
|
|
QList<QObject *> *qlistPtr;
|
|
|
|
QScriptValue *qscriptValuePtr;
|
|
|
|
};
|
|
|
|
|
|
|
|
int type;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
MetaCallArgument::MetaCallArgument()
|
|
|
|
: type(QVariant::Invalid)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
MetaCallArgument::~MetaCallArgument()
|
|
|
|
{
|
|
|
|
cleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MetaCallArgument::cleanup()
|
|
|
|
{
|
|
|
|
if (type == QMetaType::QString) {
|
|
|
|
qstringPtr->~QString();
|
|
|
|
} else if (type == -1 || type == QMetaType::QVariant) {
|
|
|
|
qvariantPtr->~QVariant();
|
|
|
|
} else if (type == qMetaTypeId<QScriptValue>()) {
|
|
|
|
qscriptValuePtr->~QScriptValue();
|
|
|
|
} else if (type == qMetaTypeId<QList<QObject *> >()) {
|
|
|
|
qlistPtr->~QList<QObject *>();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void *MetaCallArgument::dataPtr()
|
|
|
|
{
|
|
|
|
if (type == -1)
|
|
|
|
return qvariantPtr->data();
|
|
|
|
else
|
|
|
|
return (void *)&allocData;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MetaCallArgument::initAsType(int callType, QDeclarativeEngine *e)
|
|
|
|
{
|
|
|
|
if (type != 0) { cleanup(); type = 0; }
|
|
|
|
if (callType == 0) return;
|
|
|
|
|
|
|
|
QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
|
|
|
|
|
|
|
|
if (callType == qMetaTypeId<QScriptValue>()) {
|
|
|
|
qscriptValuePtr = new (&allocData) QScriptValue(engine->undefinedValue());
|
|
|
|
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) {
|
|
|
|
qobjectPtr = 0;
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::QString) {
|
|
|
|
qstringPtr = new (&allocData) QString();
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == qMetaTypeId<QVariant>()) {
|
|
|
|
type = callType;
|
|
|
|
qvariantPtr = new (&allocData) QVariant();
|
|
|
|
} else if (callType == qMetaTypeId<QList<QObject *> >()) {
|
|
|
|
type = callType;
|
|
|
|
qlistPtr = new (&allocData) QList<QObject *>();
|
|
|
|
} else {
|
|
|
|
type = -1;
|
|
|
|
qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MetaCallArgument::fromScriptValue(int callType, QDeclarativeEngine *engine, const QScriptValue &value)
|
|
|
|
{
|
|
|
|
if (type != 0) { cleanup(); type = 0; }
|
|
|
|
|
|
|
|
if (callType == qMetaTypeId<QScriptValue>()) {
|
|
|
|
qscriptValuePtr = new (&allocData) QScriptValue(value);
|
|
|
|
type = qMetaTypeId<QScriptValue>();
|
|
|
|
} else if (callType == QMetaType::Int) {
|
|
|
|
intValue = quint32(value.toInt32());
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::UInt) {
|
|
|
|
intValue = quint32(value.toUInt32());
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::Bool) {
|
|
|
|
boolValue = value.toBool();
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::Double) {
|
|
|
|
doubleValue = double(value.toNumber());
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::Float) {
|
|
|
|
floatValue = float(value.toNumber());
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::QString) {
|
|
|
|
if (value.isNull() || value.isUndefined())
|
|
|
|
qstringPtr = new (&allocData) QString();
|
|
|
|
else
|
|
|
|
qstringPtr = new (&allocData) QString(value.toString());
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == QMetaType::QObjectStar) {
|
|
|
|
qobjectPtr = value.toQObject();
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == qMetaTypeId<QVariant>()) {
|
|
|
|
QVariant other = QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value);
|
|
|
|
qvariantPtr = new (&allocData) QVariant(other);
|
|
|
|
type = callType;
|
|
|
|
} else if (callType == qMetaTypeId<QList<QObject*> >()) {
|
|
|
|
qlistPtr = new (&allocData) QList<QObject *>();
|
|
|
|
if (value.isArray()) {
|
|
|
|
int length = value.property(QLatin1String("length")).toInt32();
|
|
|
|
for (int ii = 0; ii < length; ++ii) {
|
|
|
|
QScriptValue arrayItem = value.property(ii);
|
|
|
|
QObject *d = arrayItem.toQObject();
|
|
|
|
qlistPtr->append(d);
|
|
|
|
}
|
|
|
|
} else if (QObject *d = value.toQObject()) {
|
|
|
|
qlistPtr->append(d);
|
|
|
|
}
|
|
|
|
type = callType;
|
|
|
|
} else {
|
|
|
|
qvariantPtr = new (&allocData) QVariant();
|
|
|
|
type = -1;
|
|
|
|
|
|
|
|
QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine);
|
|
|
|
QVariant v = priv->scriptValueToVariant(value);
|
|
|
|
if (v.userType() == callType) {
|
|
|
|
*qvariantPtr = v;
|
|
|
|
} else if (v.canConvert((QVariant::Type)callType)) {
|
|
|
|
*qvariantPtr = v;
|
|
|
|
qvariantPtr->convert((QVariant::Type)callType);
|
|
|
|
} else if (const QMetaObject *mo = priv->rawMetaObjectForType(callType)) {
|
|
|
|
QObject *obj = priv->toQObject(v);
|
|
|
|
|
|
|
|
if (obj) {
|
|
|
|
const QMetaObject *objMo = obj->metaObject();
|
|
|
|
while (objMo && objMo != mo) objMo = objMo->superClass();
|
|
|
|
if (!objMo) obj = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*qvariantPtr = QVariant(callType, &obj);
|
|
|
|
} else {
|
|
|
|
*qvariantPtr = QVariant(callType, (void *)0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QScriptDeclarativeClass::Value MetaCallArgument::toValue(QDeclarativeEngine *e)
|
|
|
|
{
|
|
|
|
QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e);
|
|
|
|
|
|
|
|
if (type == qMetaTypeId<QScriptValue>()) {
|
|
|
|
return QScriptDeclarativeClass::Value(engine, *qscriptValuePtr);
|
|
|
|
} else if (type == QMetaType::Int) {
|
|
|
|
return QScriptDeclarativeClass::Value(engine, int(intValue));
|
|
|
|
} else if (type == QMetaType::UInt) {
|
|
|
|
return QScriptDeclarativeClass::Value(engine, uint(intValue));
|
|
|
|
} else if (type == QMetaType::Bool) {
|
|
|
|
return QScriptDeclarativeClass::Value(engine, boolValue);
|
|
|
|
} else if (type == QMetaType::Double) {
|
|
|
|
return QScriptDeclarativeClass::Value(engine, doubleValue);
|
|
|
|
} else if (type == QMetaType::Float) {
|
|
|
|
return QScriptDeclarativeClass::Value(engine, floatValue);
|
|
|
|
} else if (type == QMetaType::QString) {
|
|
|
|
return QScriptDeclarativeClass::Value(engine, *qstringPtr);
|
|
|
|
} else if (type == QMetaType::QObjectStar) {
|
|
|
|
if (qobjectPtr)
|
|
|
|
QDeclarativeData::get(qobjectPtr, true)->setImplicitDestructible();
|
|
|
|
QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
|
|
|
|
return QScriptDeclarativeClass::Value(engine, priv->objectClass->newQObject(qobjectPtr));
|
|
|
|
} else if (type == qMetaTypeId<QList<QObject *> >()) {
|
|
|
|
QList<QObject *> &list = *qlistPtr;
|
|
|
|
QScriptValue rv = engine->newArray(list.count());
|
|
|
|
QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e);
|
|
|
|
for (int ii = 0; ii < list.count(); ++ii) {
|
|
|
|
QObject *object = list.at(ii);
|
|
|
|
QDeclarativeData::get(object, true)->setImplicitDestructible();
|
|
|
|
rv.setProperty(ii, priv->objectClass->newQObject(object));
|
|
|
|
}
|
|
|
|
return QScriptDeclarativeClass::Value(engine, rv);
|
|
|
|
} else if (type == -1 || type == qMetaTypeId<QVariant>()) {
|
|
|
|
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(e);
|
|
|
|
QScriptValue rv = ep->scriptValueFromVariant(*qvariantPtr);
|
|
|
|
if (rv.isQObject()) {
|
|
|
|
QObject *object = rv.toQObject();
|
|
|
|
if (object)
|
|
|
|
QDeclarativeData::get(object, true)->setImplicitDestructible();
|
|
|
|
}
|
|
|
|
return QScriptDeclarativeClass::Value(engine, rv);
|
|
|
|
} else {
|
|
|
|
return QScriptDeclarativeClass::Value();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int QDeclarativeObjectMethodScriptClass::enumType(const QMetaObject *meta, const QString &strname)
|
|
|
|
{
|
|
|
|
QByteArray str = strname.toUtf8();
|
|
|
|
QByteArray scope;
|
|
|
|
QByteArray name;
|
|
|
|
int scopeIdx = str.lastIndexOf("::");
|
|
|
|
if (scopeIdx != -1) {
|
|
|
|
scope = str.left(scopeIdx);
|
|
|
|
name = str.mid(scopeIdx + 2);
|
|
|
|
} else {
|
|
|
|
name = str;
|
|
|
|
}
|
|
|
|
for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
|
|
|
|
QMetaEnum m = meta->enumerator(i);
|
|
|
|
if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
|
|
|
|
return QVariant::Int;
|
|
|
|
}
|
|
|
|
return QVariant::Invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeObjectMethodScriptClass::Value QDeclarativeObjectMethodScriptClass::call(Object *o, QScriptContext *ctxt)
|
|
|
|
{
|
|
|
|
MethodData *method = static_cast<MethodData *>(o);
|
|
|
|
|
|
|
|
if (method->data.relatedIndex == -1)
|
|
|
|
return callPrecise(method->object, method->data, ctxt);
|
|
|
|
else
|
|
|
|
return callOverloaded(method, ctxt);
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeObjectMethodScriptClass::Value
|
|
|
|
QDeclarativeObjectMethodScriptClass::callPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
|
|
|
|
QScriptContext *ctxt)
|
|
|
|
{
|
|
|
|
if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
|
|
|
|
|
|
|
|
QMetaMethod m = object->metaObject()->method(data.coreIndex);
|
|
|
|
QList<QByteArray> argTypeNames = m.parameterTypes();
|
|
|
|
QVarLengthArray<int, 9> argTypes(argTypeNames.count());
|
|
|
|
|
|
|
|
// ### Cache
|
|
|
|
for (int ii = 0; ii < argTypeNames.count(); ++ii) {
|
|
|
|
argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
|
|
|
|
if (argTypes[ii] == QVariant::Invalid)
|
|
|
|
argTypes[ii] = enumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
|
|
|
|
if (argTypes[ii] == QVariant::Invalid)
|
|
|
|
return Value(ctxt, ctxt->throwError(QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)))));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argTypes.count() > ctxt->argumentCount())
|
|
|
|
return Value(ctxt, ctxt->throwError(QLatin1String("Insufficient arguments")));
|
|
|
|
|
|
|
|
return callMethod(object, data.coreIndex, data.propType, argTypes.count(), argTypes.data(), ctxt);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
return callMethod(object, data.coreIndex, data.propType, 0, 0, ctxt);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QDeclarativeObjectMethodScriptClass::Value
|
|
|
|
QDeclarativeObjectMethodScriptClass::callMethod(QObject *object, int index,
|
|
|
|
int returnType, int argCount, int *argTypes,
|
|
|
|
QScriptContext *ctxt)
|
|
|
|
{
|
|
|
|
if (argCount > 0) {
|
|
|
|
|
|
|
|
QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
|
|
|
|
args[0].initAsType(returnType, engine);
|
|
|
|
|
|
|
|
for (int ii = 0; ii < argCount; ++ii)
|
|
|
|
args[ii + 1].fromScriptValue(argTypes[ii], engine, ctxt->argument(ii));
|
|
|
|
|
|
|
|
QVarLengthArray<void *, 9> argData(args.count());
|
|
|
|
for (int ii = 0; ii < args.count(); ++ii)
|
|
|
|
argData[ii] = args[ii].dataPtr();
|
|
|
|
|
|
|
|
QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
|
|
|
|
|
|
|
|
return args[0].toValue(engine);
|
|
|
|
|
|
|
|
} else if (returnType != 0) {
|
|
|
|
|
|
|
|
MetaCallArgument arg;
|
|
|
|
arg.initAsType(returnType, engine);
|
|
|
|
|
|
|
|
void *args[] = { arg.dataPtr() };
|
|
|
|
|
|
|
|
QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
|
|
|
|
|
|
|
|
return arg.toValue(engine);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
void *args[] = { 0 };
|
|
|
|
QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
|
|
|
|
return Value();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Resolve the overloaded method to call. The algorithm works conceptually like this:
|
|
|
|
1. Resolve the set of overloads it is *possible* to call.
|
|
|
|
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
|
|
|
|
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.
|
|
|
|
3. Find the best remaining overload based on its match score.
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
QDeclarativeObjectMethodScriptClass::Value
|
|
|
|
QDeclarativeObjectMethodScriptClass::callOverloaded(MethodData *method, QScriptContext *ctxt)
|
|
|
|
{
|
|
|
|
int argumentCount = ctxt->argumentCount();
|
|
|
|
|
|
|
|
QDeclarativePropertyCache::Data *best = 0;
|
|
|
|
int bestParameterScore = INT_MAX;
|
|
|
|
int bestMatchScore = INT_MAX;
|
|
|
|
|
|
|
|
QDeclarativePropertyCache::Data dummy;
|
|
|
|
QDeclarativePropertyCache::Data *attempt = &method->data;
|
|
|
|
|
|
|
|
do {
|
|
|
|
QList<QByteArray> methodArgTypeNames;
|
|
|
|
|
|
|
|
if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
|
|
|
|
methodArgTypeNames = method->object->metaObject()->method(attempt->coreIndex).parameterTypes();
|
|
|
|
|
|
|
|
int methodArgumentCount = methodArgTypeNames.count();
|
|
|
|
|
|
|
|
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;
|
|
|
|
QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
|
|
|
|
|
|
|
|
bool unknownArgument = false;
|
|
|
|
for (int ii = 0; ii < methodArgumentCount; ++ii) {
|
|
|
|
methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
|
|
|
|
if (methodArgTypes[ii] == QVariant::Invalid)
|
|
|
|
methodArgTypes[ii] = enumType(method->object->metaObject(),
|
|
|
|
QString::fromLatin1(methodArgTypeNames.at(ii)));
|
|
|
|
if (methodArgTypes[ii] == QVariant::Invalid) {
|
|
|
|
unknownArgument = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
methodMatchScore += matchScore(ctxt->argument(ii), methodArgTypes[ii], methodArgTypeNames.at(ii));
|
|
|
|
}
|
|
|
|
if (unknownArgument)
|
|
|
|
continue; // We don't understand all the parameters
|
|
|
|
|
|
|
|
if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
|
|
|
|
best = attempt;
|
|
|
|
bestParameterScore = methodParameterScore;
|
|
|
|
bestMatchScore = methodMatchScore;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bestParameterScore == 0 && bestMatchScore == 0)
|
|
|
|
break; // We can't get better than that
|
|
|
|
|
|
|
|
} while((attempt = relatedMethod(method->object, attempt, dummy)) != 0);
|
|
|
|
|
|
|
|
if (best) {
|
|
|
|
return callPrecise(method->object, *best, ctxt);
|
|
|
|
} else {
|
|
|
|
QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
|
|
|
|
QDeclarativePropertyCache::Data *candidate = &method->data;
|
|
|
|
while (candidate) {
|
|
|
|
error += QLatin1String("\n ") + QString::fromUtf8(method->object->metaObject()->method(candidate->coreIndex).signature());
|
|
|
|
candidate = relatedMethod(method->object, candidate, dummy);
|
|
|
|
}
|
|
|
|
return Value(ctxt, ctxt->throwError(error));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Returns the match score for converting \a actual to be of type \a conversionType. A
|
|
|
|
zero score means "perfect match" whereas a higher score is worse.
|
|
|
|
|
|
|
|
The conversion table is copied out of the QtScript callQtMethod() function.
|
|
|
|
*/
|
|
|
|
int QDeclarativeObjectMethodScriptClass::matchScore(const QScriptValue &actual, int conversionType,
|
|
|
|
const QByteArray &conversionTypeName)
|
|
|
|
{
|
|
|
|
if (actual.isNumber()) {
|
|
|
|
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;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
} else if (actual.isString()) {
|
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QString:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
} else if (actual.isBoolean()) {
|
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::Bool:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
} else if (actual.isDate()) {
|
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QDateTime:
|
|
|
|
return 0;
|
|
|
|
case QMetaType::QDate:
|
|
|
|
return 1;
|
|
|
|
case QMetaType::QTime:
|
|
|
|
return 2;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
} else if (actual.isRegExp()) {
|
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QRegExp:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
} else if (actual.isVariant()) {
|
|
|
|
if (conversionType == qMetaTypeId<QVariant>())
|
|
|
|
return 0;
|
|
|
|
else if (actual.toVariant().userType() == conversionType)
|
|
|
|
return 0;
|
|
|
|
else
|
|
|
|
return 10;
|
|
|
|
} else if (actual.isArray()) {
|
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QStringList:
|
|
|
|
case QMetaType::QVariantList:
|
|
|
|
return 5;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
} else if (actual.isQObject()) {
|
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::QObjectStar:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
} else if (actual.isNull()) {
|
|
|
|
switch (conversionType) {
|
|
|
|
case QMetaType::VoidStar:
|
|
|
|
case QMetaType::QObjectStar:
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
if (!conversionTypeName.endsWith('*'))
|
|
|
|
return 10;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static QByteArray QMetaMethod_name(const QMetaMethod &m)
|
|
|
|
{
|
|
|
|
QByteArray sig = m.signature();
|
|
|
|
int paren = sig.indexOf('(');
|
|
|
|
if (paren == -1)
|
|
|
|
return sig;
|
|
|
|
else
|
|
|
|
return sig.left(paren);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Returns the next related method, if one, or 0.
|
|
|
|
*/
|
|
|
|
QDeclarativePropertyCache::Data *
|
|
|
|
QDeclarativeObjectMethodScriptClass::relatedMethod(QObject *object, QDeclarativePropertyCache::Data *current,
|
|
|
|
QDeclarativePropertyCache::Data &dummy)
|
|
|
|
{
|
|
|
|
QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
|
|
|
|
if (current->relatedIndex == -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (cache) {
|
|
|
|
return cache->method(current->relatedIndex);
|
|
|
|
} else {
|
|
|
|
const QMetaObject *mo = object->metaObject();
|
|
|
|
int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
|
|
|
|
|
|
|
|
while (methodOffset > current->relatedIndex) {
|
|
|
|
mo = mo->superClass();
|
|
|
|
methodOffset -= QMetaObject_methods(mo);
|
|
|
|
}
|
|
|
|
|
|
|
|
QMetaMethod method = mo->method(current->relatedIndex);
|
|
|
|
dummy.load(method);
|
|
|
|
|
|
|
|
// Look for overloaded methods
|
|
|
|
QByteArray methodName = QMetaMethod_name(method);
|
|
|
|
for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
|
|
|
|
if (methodName == QMetaMethod_name(mo->method(ii))) {
|
|
|
|
dummy.relatedIndex = ii;
|
|
|
|
return &dummy;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &dummy;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|