aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/declarative/qml/ftw/qmetaobjectbuilder.cpp10
-rw-r--r--src/declarative/qml/qdeclarativepropertycache.cpp33
-rw-r--r--src/declarative/qml/qdeclarativepropertycache_p.h5
-rw-r--r--src/declarative/qml/v8/qv8engine_p.h2
-rw-r--r--src/declarative/qml/v8/qv8qobjectwrapper.cpp57
-rw-r--r--src/declarative/qml/v8/qv8qobjectwrapper_p.h2
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/data/signalHandlers.qml60
-rw-r--r--tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp31
8 files changed, 185 insertions, 15 deletions
diff --git a/src/declarative/qml/ftw/qmetaobjectbuilder.cpp b/src/declarative/qml/ftw/qmetaobjectbuilder.cpp
index f5e8369ebc..e52b9e1172 100644
--- a/src/declarative/qml/ftw/qmetaobjectbuilder.cpp
+++ b/src/declarative/qml/ftw/qmetaobjectbuilder.cpp
@@ -155,6 +155,7 @@ struct QMetaObjectPrivate
int enumeratorCount, enumeratorData;
int constructorCount, constructorData;
int flags;
+ int signalCount;
};
static inline const QMetaObjectPrivate *priv(const uint* data)
@@ -1206,7 +1207,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
QMetaObjectPrivate *pmeta
= reinterpret_cast<QMetaObjectPrivate *>(buf + size);
int pmetaSize = size;
- dataIndex = 13; // Number of fields in the QMetaObjectPrivate.
+ dataIndex = 14; // Number of fields in the QMetaObjectPrivate.
for (index = 0; index < d->properties.size(); ++index) {
if (d->properties[index].notifySignal != -1) {
hasNotifySignals = true;
@@ -1214,9 +1215,10 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
}
}
if (buf) {
- pmeta->revision = 3;
+ pmeta->revision = 4;
pmeta->flags = d->flags;
pmeta->className = 0; // Class name is always the first string.
+ //pmeta->signalCount is handled in the "output method loop" as an optimization.
pmeta->classInfoCount = d->classInfoNames.size();
pmeta->classInfoData = dataIndex;
@@ -1274,7 +1276,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
}
// Reset the current data position to just past the QMetaObjectPrivate.
- dataIndex = 13;
+ dataIndex = 14;
// Add the class name to the string table.
int offset = 0;
@@ -1312,6 +1314,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
data[dataIndex + 2] = ret;
data[dataIndex + 3] = tag;
data[dataIndex + 4] = attrs;
+ if (method->methodType() == QMetaMethod::Signal)
+ pmeta->signalCount++;
}
dataIndex += 5;
}
diff --git a/src/declarative/qml/qdeclarativepropertycache.cpp b/src/declarative/qml/qdeclarativepropertycache.cpp
index 406e43fc21..05232d981a 100644
--- a/src/declarative/qml/qdeclarativepropertycache.cpp
+++ b/src/declarative/qml/qdeclarativepropertycache.cpp
@@ -313,10 +313,14 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb
allowedRevisionCache.append(0);
int methodCount = metaObject->methodCount();
+ Q_ASSERT(QMetaObjectPrivate::get(metaObject)->revision >= 4);
+ int signalCount = QMetaObjectPrivate::get(metaObject)->signalCount;
// 3 to block the destroyed signal and the deleteLater() slot
int methodOffset = qMax(3, metaObject->methodOffset());
methodIndexCache.resize(methodCount - methodIndexCacheStart);
+ signalHandlerIndexCache.resize(signalCount);
+ int signalHandlerIndex = 0;
for (int ii = methodOffset; ii < methodCount; ++ii) {
QMetaMethod m = metaObject->method(ii);
if (m.access() == QMetaMethod::Private)
@@ -329,6 +333,7 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb
while (*cptr != '(') { Q_ASSERT(*cptr != 0); utf8 |= *cptr & 0x80; ++cptr; }
Data *data = &methodIndexCache[ii - methodIndexCacheStart];
+ Data *sigdata = 0;
data->lazyLoad(m);
@@ -342,6 +347,12 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb
data->metaObjectOffset = allowedRevisionCache.count() - 1;
+ if (data->isSignal()) {
+ sigdata = &signalHandlerIndexCache[signalHandlerIndex];
+ *sigdata = *data;
+ sigdata->flags |= Data::IsSignalHandler;
+ }
+
Data *old = 0;
if (utf8) {
@@ -349,11 +360,33 @@ void QDeclarativePropertyCache::append(QDeclarativeEngine *engine, const QMetaOb
if (Data **it = stringCache.value(methodName))
old = *it;
stringCache.insert(methodName, data);
+
+ if (data->isSignal()) {
+ QHashedString on(QStringLiteral("on") % methodName.at(0).toUpper() % methodName.midRef(1));
+ stringCache.insert(on, sigdata);
+ ++signalHandlerIndex;
+ }
} else {
QHashedCStringRef methodName(signature, cptr - signature);
if (Data **it = stringCache.value(methodName))
old = *it;
stringCache.insert(methodName, data);
+
+ if (data->isSignal()) {
+ int length = methodName.length();
+
+ char str[length + 3];
+ str[0] = 'o';
+ str[1] = 'n';
+ str[2] = toupper(signature[0]);
+ if (length > 1)
+ memcpy(&str[3], &signature[1], length - 1);
+ str[length + 2] = '\0';
+
+ QHashedString on(QString::fromLatin1(str));
+ stringCache.insert(on, sigdata);
+ ++signalHandlerIndex;
+ }
}
if (old) {
diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h
index 8a1da1929c..33565c4d45 100644
--- a/src/declarative/qml/qdeclarativepropertycache_p.h
+++ b/src/declarative/qml/qdeclarativepropertycache_p.h
@@ -104,9 +104,10 @@ public:
IsSignal = 0x00008000, // Function is a signal
IsVMESignal = 0x00010000, // Signal was added by QML
IsV8Function = 0x00020000, // Function takes QDeclarativeV8Function* args
+ IsSignalHandler = 0x00040000, // Function is a signal handler
// Internal QDeclarativePropertyCache flags
- NotFullyResolved = 0x00040000 // True if the type data is to be lazily resolved
+ NotFullyResolved = 0x00080000 // True if the type data is to be lazily resolved
};
Q_DECLARE_FLAGS(Flags, Flag)
@@ -133,6 +134,7 @@ public:
bool isSignal() const { return flags & IsSignal; }
bool isVMESignal() const { return flags & IsVMESignal; }
bool isV8Function() const { return flags & IsV8Function; }
+ bool isSignalHandler() const { return flags & IsSignalHandler; }
union {
int propType; // When !NotFullyResolved
@@ -221,6 +223,7 @@ private:
IndexCache propertyIndexCache;
IndexCache methodIndexCache;
+ IndexCache signalHandlerIndexCache;
StringCache stringCache;
AllowedRevisionCache allowedRevisionCache;
v8::Persistent<v8::Function> constructor;
diff --git a/src/declarative/qml/v8/qv8engine_p.h b/src/declarative/qml/v8/qv8engine_p.h
index bd5f360748..ab4da423ee 100644
--- a/src/declarative/qml/v8/qv8engine_p.h
+++ b/src/declarative/qml/v8/qv8engine_p.h
@@ -135,7 +135,7 @@ public:
QV8ObjectResource(QV8Engine *engine) : engine(engine) { Q_ASSERT(engine); }
enum ResourceType { ContextType, QObjectType, TypeType, ListType, VariantType,
ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType,
- ListModelType, Context2DType, ParticleDataType };
+ ListModelType, Context2DType, ParticleDataType, SignalHandlerType };
virtual ResourceType resourceType() const = 0;
QV8Engine *engine;
diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp
index 52e106494c..bf8dcea51b 100644
--- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp
+++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp
@@ -109,6 +109,16 @@ public:
QV8QObjectWrapper *wrapper;
};
+class QV8SignalHandlerResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(SignalHandlerType)
+public:
+ QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index);
+
+ QDeclarativeGuard<QObject> object;
+ int index;
+};
+
namespace {
struct MetaCallArgument {
inline MetaCallArgument();
@@ -152,6 +162,11 @@ QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
{
}
+QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index)
+: QV8ObjectResource(engine), object(object), index(index)
+{
+}
+
static QAtomicInt objectIdCounter(1);
QV8QObjectWrapper::QV8QObjectWrapper()
@@ -177,6 +192,7 @@ void QV8QObjectWrapper::destroy()
qPersistentDispose(m_hiddenObject);
qPersistentDispose(m_destroySymbol);
qPersistentDispose(m_toStringSymbol);
+ qPersistentDispose(m_signalHandlerConstructor);
qPersistentDispose(m_methodConstructor);
qPersistentDispose(m_constructor);
}
@@ -278,10 +294,21 @@ void QV8QObjectWrapper::init(QV8Engine *engine)
m_methodConstructor = qPersistentNew<v8::Function>(createFn);
}
+ v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine);
+ v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine);
+
+ {
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum);
+ ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
+ m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
+ }
+
{
v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
- prototype->Set(v8::String::New("connect"), V8FUNCTION(Connect, engine), v8::DontEnum);
- prototype->Set(v8::String::New("disconnect"), V8FUNCTION(Disconnect, engine), v8::DontEnum);
+ prototype->Set(v8::String::New("connect"), connect, v8::DontEnum);
+ prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
}
}
@@ -461,6 +488,11 @@ v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject
return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
} else if (result->isV8Function()) {
return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
+ } else if (result->isSignalHandler()) {
+ v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
+ QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
+ handler->SetExternalResource(r);
+ return handler;
} else {
return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
}
@@ -998,6 +1030,17 @@ v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
}
}
+QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
+{
+ if (object->IsFunction())
+ return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
+
+ if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
+ return qMakePair(resource->object.data(), resource->index);
+
+ return qMakePair((QObject *)0, -1);
+}
+
QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
{
v8::ScriptOrigin origin = function->GetScriptOrigin();
@@ -1166,10 +1209,7 @@ v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
QV8Engine *engine = V8ENGINE();
- if (!args.This()->IsFunction())
- V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
-
- QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
+ QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
QObject *signalObject = signalInfo.first;
int signalIndex = signalInfo.second;
@@ -1228,10 +1268,7 @@ v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
QV8Engine *engine = V8ENGINE();
- if (!args.This()->IsFunction())
- V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
-
- QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
+ QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
QObject *signalObject = signalInfo.first;
int signalIndex = signalInfo.second;
diff --git a/src/declarative/qml/v8/qv8qobjectwrapper_p.h b/src/declarative/qml/v8/qv8qobjectwrapper_p.h
index d0a489bed8..be118a9c34 100644
--- a/src/declarative/qml/v8/qv8qobjectwrapper_p.h
+++ b/src/declarative/qml/v8/qv8qobjectwrapper_p.h
@@ -111,11 +111,13 @@ private:
static v8::Handle<v8::Value> Disconnect(const v8::Arguments &args);
static v8::Handle<v8::Value> Invoke(const v8::Arguments &args);
static QPair<QObject *, int> ExtractQtMethod(QV8Engine *, v8::Handle<v8::Function>);
+ static QPair<QObject *, int> ExtractQtSignal(QV8Engine *, v8::Handle<v8::Object>);
QV8Engine *m_engine;
quint32 m_id;
v8::Persistent<v8::Function> m_constructor;
v8::Persistent<v8::Function> m_methodConstructor;
+ v8::Persistent<v8::Function> m_signalHandlerConstructor;
v8::Persistent<v8::String> m_toStringSymbol;
v8::Persistent<v8::String> m_destroySymbol;
QHashedV8String m_toStringString;
diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/signalHandlers.qml b/tests/auto/declarative/qdeclarativeecmascript/data/signalHandlers.qml
new file mode 100644
index 0000000000..975be1b2ad
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativeecmascript/data/signalHandlers.qml
@@ -0,0 +1,60 @@
+import Qt.test 1.0
+import QtQuick 2.0
+
+QtObject {
+ id: root
+
+ property int count: 0
+ signal testSignal
+ onTestSignal: count++
+
+ property int funcCount: 0
+ function testFunction() {
+ funcCount++;
+ }
+
+ //should increment count
+ function testSignalCall() {
+ testSignal()
+ }
+
+ //should NOT increment count, and should throw an exception
+ property string errorString
+ function testSignalHandlerCall() {
+ try {
+ onTestSignal()
+ } catch (error) {
+ errorString = error.toString();
+ }
+ }
+
+ //should increment funcCount once
+ function testSignalConnection() {
+ testSignal.connect(testFunction)
+ testSignal();
+ testSignal.disconnect(testFunction)
+ testSignal();
+ }
+
+ //should increment funcCount once
+ function testSignalHandlerConnection() {
+ onTestSignal.connect(testFunction)
+ testSignal();
+ onTestSignal.disconnect(testFunction)
+ testSignal();
+ }
+
+ //should be defined
+ property bool definedResult: false
+ function testSignalDefined() {
+ if (testSignal !== undefined)
+ definedResult = true;
+ }
+
+ //should be defined
+ property bool definedHandlerResult: false
+ function testSignalHandlerDefined() {
+ if (onTestSignal !== undefined)
+ definedHandlerResult = true;
+ }
+}
diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
index f14db0a330..ba1aaca034 100644
--- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
+++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp
@@ -188,6 +188,7 @@ private slots:
void realToInt();
void dynamicString();
void include();
+ void signalHandlers();
void callQtInvokables();
void invokableObjectArg();
@@ -3597,6 +3598,36 @@ void tst_qdeclarativeecmascript::include()
}
}
+void tst_qdeclarativeecmascript::signalHandlers()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("signalHandlers.qml"));
+ QObject *o = component.create();
+ QVERIFY(o != 0);
+
+ QVERIFY(o->property("count").toInt() == 0);
+ QMetaObject::invokeMethod(o, "testSignalCall");
+ QCOMPARE(o->property("count").toInt(), 1);
+
+ QMetaObject::invokeMethod(o, "testSignalHandlerCall");
+ QCOMPARE(o->property("count").toInt(), 1);
+ QCOMPARE(o->property("errorString").toString(), QLatin1String("TypeError: Property 'onTestSignal' of object [object Object] is not a function"));
+
+ QVERIFY(o->property("funcCount").toInt() == 0);
+ QMetaObject::invokeMethod(o, "testSignalConnection");
+ QCOMPARE(o->property("funcCount").toInt(), 1);
+
+ QMetaObject::invokeMethod(o, "testSignalHandlerConnection");
+ QCOMPARE(o->property("funcCount").toInt(), 2);
+
+ QMetaObject::invokeMethod(o, "testSignalDefined");
+ QCOMPARE(o->property("definedResult").toBool(), true);
+
+ QMetaObject::invokeMethod(o, "testSignalHandlerDefined");
+ QCOMPARE(o->property("definedHandlerResult").toBool(), true);
+
+ delete o;
+}
+
void tst_qdeclarativeecmascript::qtbug_10696()
{
QDeclarativeComponent component(&engine, TEST_FILE("qtbug_10696.qml"));