aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAaron Kennedy <aaron.kennedy@nokia.com>2011-10-25 15:41:33 +0100
committerQt by Nokia <qt-info@nokia.com>2011-10-25 16:55:57 +0200
commit2413cc1e87c051760210028979b4db8e4c13eca0 (patch)
tree57142fc5e75d6f87f6c8d458d647c50ede05b934 /src
parent70c53a2d5b703abafb4d6d891e492bae437a00c5 (diff)
Cache QObject method arguments
This more than doubles the performance of invoking simple QObject methods with parameters - such as myFunction(int,int) - multiple times. Change-Id: I4bf21fb3980b09aedf0f440a246682c418933a65 Reviewed-by: Aaron Kennedy <aaron.kennedy@nokia.com>
Diffstat (limited to 'src')
-rw-r--r--src/declarative/qml/qdeclarativepropertycache.cpp119
-rw-r--r--src/declarative/qml/qdeclarativepropertycache_p.h14
-rw-r--r--src/declarative/qml/v8/qv8qobjectwrapper.cpp129
3 files changed, 174 insertions, 88 deletions
diff --git a/src/declarative/qml/qdeclarativepropertycache.cpp b/src/declarative/qml/qdeclarativepropertycache.cpp
index 134c588538..6cb952e5bf 100644
--- a/src/declarative/qml/qdeclarativepropertycache.cpp
+++ b/src/declarative/qml/qdeclarativepropertycache.cpp
@@ -54,6 +54,13 @@ Q_DECLARE_METATYPE(QDeclarativeV8Handle);
QT_BEGIN_NAMESPACE
+class QDeclarativePropertyCacheMethodArguments
+{
+public:
+ QDeclarativePropertyCacheMethodArguments *next;
+ int arguments[0];
+};
+
// Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick
// to load
static QDeclarativePropertyCache::Data::Flags fastFlagsForProperty(const QMetaProperty &p)
@@ -143,7 +150,7 @@ void QDeclarativePropertyCache::Data::load(const QMetaProperty &p, QDeclarativeE
void QDeclarativePropertyCache::Data::load(const QMetaMethod &m)
{
coreIndex = m.methodIndex();
- relatedIndex = -1;
+ arguments = 0;
flags |= Data::IsFunction;
if (m.methodType() == QMetaMethod::Signal)
flags |= Data::IsSignal;
@@ -170,7 +177,7 @@ void QDeclarativePropertyCache::Data::load(const QMetaMethod &m)
void QDeclarativePropertyCache::Data::lazyLoad(const QMetaMethod &m)
{
coreIndex = m.methodIndex();
- relatedIndex = -1;
+ arguments = 0;
flags |= Data::IsFunction;
if (m.methodType() == QMetaMethod::Signal)
flags |= Data::IsSignal;
@@ -200,7 +207,8 @@ void QDeclarativePropertyCache::Data::lazyLoad(const QMetaMethod &m)
Creates a new empty QDeclarativePropertyCache.
*/
QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e)
-: engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0)
+: engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), metaObject(0),
+ argumentsCache(0)
{
Q_ASSERT(engine);
}
@@ -209,7 +217,8 @@ QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e)
Creates a new QDeclarativePropertyCache of \a metaObject.
*/
QDeclarativePropertyCache::QDeclarativePropertyCache(QDeclarativeEngine *e, const QMetaObject *metaObject)
-: engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0)
+: engine(e), parent(0), propertyIndexCacheStart(0), methodIndexCacheStart(0), metaObject(0),
+ argumentsCache(0)
{
Q_ASSERT(engine);
Q_ASSERT(metaObject);
@@ -221,6 +230,13 @@ QDeclarativePropertyCache::~QDeclarativePropertyCache()
{
clear();
+ QDeclarativePropertyCacheMethodArguments *args = argumentsCache;
+ while (args) {
+ QDeclarativePropertyCacheMethodArguments *next = args->next;
+ qFree(args);
+ args = next;
+ }
+
if (parent) parent->release();
parent = 0;
engine = 0;
@@ -298,6 +314,7 @@ QDeclarativePropertyCache *QDeclarativePropertyCache::copy(int reserve)
cache->methodIndexCacheStart = methodIndexCache.count() + methodIndexCacheStart;
cache->stringCache.copyAndReserve(stringCache, reserve);
cache->allowedRevisionCache = allowedRevisionCache;
+ cache->metaObject = metaObject;
// We specifically do *NOT* copy the constructor
@@ -317,6 +334,8 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb
Q_UNUSED(revision);
Q_ASSERT(constructor.IsEmpty()); // We should not be appending to an in-use property cache
+ this->metaObject = metaObject;
+
bool dynamicMetaObject = isDynamicMetaObject(metaObject);
allowedRevisionCache.append(0);
@@ -404,8 +423,8 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb
if (old) {
// We only overload methods in the same class, exactly like C++
- if (old->flags & Data::IsFunction && old->coreIndex >= methodOffset)
- data->relatedIndex = old->coreIndex;
+ if (old->flags & Data::IsFunction && old->coreIndex >= methodOffset)
+ data->flags |= Data::IsOverload;
data->overrideIndexIsProperty = !bool(old->flags & Data::IsFunction);
data->overrideIndex = old->coreIndex;
}
@@ -588,6 +607,94 @@ QStringList QDeclarativePropertyCache::propertyNames() const
return keys;
}
+static int EnumType(const QMetaObject *meta, const QByteArray &str)
+{
+ 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;
+}
+
+// Returns an array of the arguments for method \a index. The first entry in the array
+// is the number of arguments.
+int *QDeclarativePropertyCache::methodParameterTypes(QObject *object, int index,
+ QVarLengthArray<int, 9> &dummy,
+ QByteArray *unknownTypeError)
+{
+ Q_ASSERT(object && index >= 0);
+
+ QDeclarativeData *ddata = QDeclarativeData::get(object, false);
+
+ if (ddata && ddata->propertyCache) {
+ typedef QDeclarativePropertyCacheMethodArguments A;
+
+ QDeclarativePropertyCache *c = ddata->propertyCache;
+ Q_ASSERT(index < c->methodIndexCacheStart + c->methodIndexCache.count());
+
+ while (index < c->methodIndexCacheStart)
+ c = c->parent;
+
+ Data *rv = const_cast<Data *>(&c->methodIndexCache.at(index - c->methodIndexCacheStart));
+
+ if (rv->arguments)
+ return static_cast<A *>(rv->arguments)->arguments;
+
+ const QMetaObject *metaObject = object->metaObject();
+ QMetaMethod m = metaObject->method(index);
+ QList<QByteArray> argTypeNames = m.parameterTypes();
+
+ A *args = static_cast<A *>(qMalloc(sizeof(A) + (argTypeNames.count() + 1) * sizeof(int)));
+ args->arguments[0] = argTypeNames.count();
+
+ for (int ii = 0; ii < argTypeNames.count(); ++ii) {
+ int type = QMetaType::type(argTypeNames.at(ii));
+ if (type == QVariant::Invalid)
+ type = EnumType(object->metaObject(), argTypeNames.at(ii));
+ if (type == QVariant::Invalid) {
+ if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
+ qFree(args);
+ return 0;
+ }
+ args->arguments[ii + 1] = type;
+ }
+
+ rv->arguments = args;
+ args->next = c->argumentsCache;
+ c->argumentsCache = args;
+ return static_cast<A *>(rv->arguments)->arguments;
+
+ } else {
+ QMetaMethod m = object->metaObject()->method(index);
+ QList<QByteArray> argTypeNames = m.parameterTypes();
+ dummy.resize(argTypeNames.count() + 1);
+ dummy[0] = argTypeNames.count();
+
+ for (int ii = 0; ii < argTypeNames.count(); ++ii) {
+ int type = QMetaType::type(argTypeNames.at(ii));
+ if (type == QVariant::Invalid)
+ type = EnumType(object->metaObject(), argTypeNames.at(ii));
+ if (type == QVariant::Invalid) {
+ if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii);
+ return 0;
+ }
+ dummy[ii + 1] = type;
+ }
+
+ return dummy.data();
+ }
+}
+
QDeclarativePropertyCache::Data *
QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj,
const QHashedV8String &name, Data &local)
diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h
index 068db589bd..176d6b34fe 100644
--- a/src/declarative/qml/qdeclarativepropertycache_p.h
+++ b/src/declarative/qml/qdeclarativepropertycache_p.h
@@ -58,6 +58,7 @@
#include "qdeclarativenotifier_p.h"
#include <private/qhashedstring_p.h>
+#include <QtCore/qvarlengtharray.h>
#include <QtCore/qvector.h>
QT_BEGIN_NAMESPACE
@@ -66,6 +67,7 @@ class QDeclarativeEngine;
class QMetaProperty;
class QV8Engine;
class QV8QObjectWrapper;
+class QDeclarativePropertyCacheMethodArguments;
class Q_DECLARATIVE_EXPORT QDeclarativePropertyCache : public QDeclarativeRefCount, public QDeclarativeCleanup
{
@@ -109,9 +111,10 @@ public:
IsVMESignal = 0x00040000, // Signal was added by QML
IsV8Function = 0x00080000, // Function takes QDeclarativeV8Function* args
IsSignalHandler = 0x00100000, // Function is a signal handler
+ IsOverload = 0x00200000, // Function is an overload of another function
// Internal QDeclarativePropertyCache flags
- NotFullyResolved = 0x00200000 // True if the type data is to be lazily resolved
+ NotFullyResolved = 0x00400000 // True if the type data is to be lazily resolved
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -141,6 +144,7 @@ public:
bool isVMESignal() const { return flags & IsVMESignal; }
bool isV8Function() const { return flags & IsV8Function; }
bool isSignalHandler() const { return flags & IsSignalHandler; }
+ bool isOverload() const { return flags & IsOverload; }
union {
int propType; // When !NotFullyResolved
@@ -149,7 +153,7 @@ public:
int coreIndex;
union {
int notifyIndex; // When !IsFunction
- int relatedIndex; // When IsFunction
+ void *arguments; // When IsFunction && HasArguments
};
union {
struct { // When !IsValueTypeVirtual
@@ -219,9 +223,10 @@ public:
inline QDeclarativeEngine *qmlEngine() const;
static Data *property(QDeclarativeEngine *, QObject *, const QString &, Data &);
static Data *property(QDeclarativeEngine *, QObject *, const QHashedV8String &, Data &);
+ static int *methodParameterTypes(QObject *, int index, QVarLengthArray<int, 9> &dummy,
+ QByteArray *unknownTypeError);
static bool isDynamicMetaObject(const QMetaObject *);
-
protected:
virtual void destroy();
virtual void clear();
@@ -252,6 +257,9 @@ private:
StringCache stringCache;
AllowedRevisionCache allowedRevisionCache;
v8::Persistent<v8::Function> constructor;
+
+ const QMetaObject *metaObject;
+ QDeclarativePropertyCacheMethodArguments *argumentsCache;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativePropertyCache::Data::Flags);
diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp
index 1dc3db7d29..031cf8386e 100644
--- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp
+++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp
@@ -1173,20 +1173,17 @@ int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, v
QList<Connection> connections = connectionList;
- QMetaMethod method = data()->metaObject()->method(index);
- Q_ASSERT(method.methodType() == QMetaMethod::Signal);
- // XXX TODO: We should figure out a way to cache the parameter types to avoid resolving
- // them each time.
- QList<QByteArray> params = method.parameterTypes();
+ QVarLengthArray<int, 9> dummy;
+ int *argsTypes = QDeclarativePropertyCache::methodParameterTypes(data(), index, dummy, 0);
v8::HandleScope handle_scope;
v8::Context::Scope scope(engine->context());
- QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
- int argCount = params.count();
+ int argCount = argsTypes?argsTypes[0]:0;
+ QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
for (int ii = 0; ii < argCount; ++ii) {
- int type = QMetaType::type(params.at(ii).constData());
+ int type = argsTypes[ii + 1];
if (type == qMetaTypeId<QVariant>()) {
args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
} else {
@@ -1452,34 +1449,13 @@ static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnTy
}
}
-static int 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;
-}
-
/*!
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.
*/
-static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
- const QByteArray &conversionTypeName)
+static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
{
if (actual->IsNumber()) {
switch (conversionType) {
@@ -1551,11 +1527,13 @@ static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
case QMetaType::VoidStar:
case QMetaType::QObjectStar:
return 0;
- default:
- if (!conversionTypeName.endsWith('*'))
- return 10;
- else
+ default: {
+ const char *typeName = QMetaType::typeName(conversionType);
+ if (typeName && typeName[strlen(typeName) - 1] == '*')
return 0;
+ else
+ return 10;
+ }
}
} else if (actual->IsObject()) {
v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
@@ -1615,28 +1593,32 @@ static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object,
QDeclarativePropertyCache::Data &dummy)
{
QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
- if (current->relatedIndex == -1)
+ if (!current->isOverload())
return 0;
+ Q_ASSERT(!current->overrideIndexIsProperty);
+
if (cache) {
- return cache->method(current->relatedIndex);
+ return cache->method(current->overrideIndex);
} else {
const QMetaObject *mo = object->metaObject();
int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
- while (methodOffset > current->relatedIndex) {
+ while (methodOffset > current->overrideIndex) {
mo = mo->superClass();
methodOffset -= QMetaObject_methods(mo);
}
- QMetaMethod method = mo->method(current->relatedIndex);
+ QMetaMethod method = mo->method(current->overrideIndex);
dummy.load(method);
// Look for overloaded methods
QByteArray methodName = QMetaMethod_name(method);
- for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
+ for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
if (methodName == QMetaMethod_name(mo->method(ii))) {
- dummy.relatedIndex = ii;
+ dummy.setFlags(dummy.getFlags() | QDeclarativePropertyCache::Data::IsOverload);
+ dummy.overrideIndexIsProperty = 0;
+ dummy.overrideIndex = ii;
return &dummy;
}
}
@@ -1650,30 +1632,27 @@ static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativeProp
{
if (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) {
- QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
- v8::ThrowException(v8::Exception::Error(engine->toString(error)));
- return v8::Handle<v8::Value>();
- }
+ int *args = 0;
+ QVarLengthArray<int, 9> dummy;
+ QByteArray unknownTypeError;
+
+ args = QDeclarativePropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
+ &unknownTypeError);
+
+ if (!args) {
+ QString typeName = QString::fromLatin1(unknownTypeError);
+ QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ return v8::Handle<v8::Value>();
}
- if (argTypes.count() > callArgs.Length()) {
+ if (args[0] > callArgs.Length()) {
QString error = QLatin1String("Insufficient arguments");
v8::ThrowException(v8::Exception::Error(engine->toString(error)));
return v8::Handle<v8::Value>();
}
- return CallMethod(object, data.coreIndex, data.propType, argTypes.count(),
- argTypes.data(), engine, callArgs);
+ return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
} else {
@@ -1708,12 +1687,18 @@ static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativeP
const QDeclarativePropertyCache::Data *attempt = &data;
do {
- QList<QByteArray> methodArgTypeNames;
-
- if (attempt->hasArguments())
- methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
+ QVarLengthArray<int, 9> dummy;
+ int methodArgumentCount = 0;
+ int *methodArgTypes = 0;
+ if (attempt->hasArguments()) {
+ typedef QDeclarativePropertyCache PC;
+ int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
+ if (!args) // Must be an unknown argument
+ continue;
- int methodArgumentCount = methodArgTypeNames.count();
+ methodArgumentCount = args[0];
+ methodArgTypes = args + 1;
+ }
if (methodArgumentCount > argumentCount)
continue; // We don't have sufficient arguments to call this method
@@ -1723,22 +1708,8 @@ static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativeP
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(object->metaObject(),
- QString::fromLatin1(methodArgTypeNames.at(ii)));
- if (methodArgTypes[ii] == QVariant::Invalid) {
- unknownArgument = true;
- break;
- }
- methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
- }
- if (unknownArgument)
- continue; // We don't understand all the parameters
+ for (int ii = 0; ii < methodArgumentCount; ++ii)
+ methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
best = attempt;
@@ -1887,7 +1858,7 @@ v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
}
CallArgs callArgs(argCount, &arguments);
- if (method.relatedIndex == -1) {
+ if (!method.isOverload()) {
return CallPrecise(object, method, resource->engine, callArgs);
} else {
return CallOverloaded(object, method, resource->engine, callArgs);