Declarative: Fix gcc 4.6 warnings about assigned/unused variables.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / v8 / qv8qobjectwrapper.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qv8qobjectwrapper_p.h"
43 #include "qv8contextwrapper_p.h"
44 #include "qv8engine_p.h"
45
46 #include <private/qdeclarativeguard_p.h>
47 #include <private/qdeclarativepropertycache_p.h>
48 #include <private/qdeclarativeengine_p.h>
49 #include <private/qdeclarativevmemetaobject_p.h>
50 #include <private/qdeclarativebinding_p.h>
51 #include <private/qjsvalue_p.h>
52 #include <private/qscript_impl_p.h>
53
54 #include <QtDeclarative/qjsvalue.h>
55 #include <QtCore/qvarlengtharray.h>
56 #include <QtCore/qtimer.h>
57 #include <QtCore/qatomic.h>
58
59 Q_DECLARE_METATYPE(QJSValue);
60 Q_DECLARE_METATYPE(QDeclarativeV8Handle);
61
62 QT_BEGIN_NAMESPACE
63
64 #if defined(__GNUC__)
65 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
66 // The code in this file does not violate strict aliasing, but GCC thinks it does
67 // so turn off the warnings for us to have a clean build
68 #  pragma GCC diagnostic ignored "-Wstrict-aliasing"
69 # endif
70 #endif
71
72 #define QOBJECT_TOSTRING_INDEX -2
73 #define QOBJECT_DESTROY_INDEX -3
74
75 // XXX TODO: Need to review all calls to QDeclarativeEngine *engine() to confirm QObjects work
76 // correctly in a worker thread
77
78 class QV8QObjectResource : public QV8ObjectResource
79 {
80     V8_RESOURCE_TYPE(QObjectType);
81
82 public:
83     QV8QObjectResource(QV8Engine *engine, QObject *object);
84
85     QDeclarativeGuard<QObject> object;
86 };
87
88 class QV8QObjectInstance : public QDeclarativeGuard<QObject>
89 {
90 public:
91     QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
92     : QDeclarativeGuard<QObject>(o), wrapper(w)
93     {
94     }
95
96     ~QV8QObjectInstance()
97     {
98         qPersistentDispose(v8object);
99     }
100
101     virtual void objectDestroyed(QObject *o)
102     {
103         if (wrapper)
104             wrapper->m_taintedObjects.remove(o);
105         delete this;
106     }
107
108     v8::Persistent<v8::Object> v8object;
109     QV8QObjectWrapper *wrapper;
110 };
111
112 class QV8SignalHandlerResource : public QV8ObjectResource
113 {
114     V8_RESOURCE_TYPE(SignalHandlerType)
115 public:
116     QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index);
117
118     QDeclarativeGuard<QObject> object;
119     int index;
120 };
121
122 namespace {
123
124 template<typename A, typename B, typename C, typename D, typename E>
125 class MaxSizeOf5 {
126     template<typename Z, typename X>
127     struct SMax {
128         static const size_t Size = sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X);
129     };
130 public:
131     static const size_t Size = SMax<A, SMax<B, SMax<C, SMax<D, E> > > >::Size;
132 };
133
134 struct MetaCallArgument {
135     inline MetaCallArgument();
136     inline ~MetaCallArgument();
137     inline void *dataPtr();
138
139     inline void initAsType(int type);
140     inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
141     inline v8::Handle<v8::Value> toValue(QV8Engine *);
142
143 private:
144     MetaCallArgument(const MetaCallArgument &);
145
146     inline void cleanup();
147
148     union {
149         float floatValue;
150         double doubleValue;
151         quint32 intValue;
152         bool boolValue;
153         QObject *qobjectPtr;
154
155         char allocData[MaxSizeOf5<QVariant,
156                                 QString,
157                                 QList<QObject *>,
158                                 QJSValue,
159                                 QDeclarativeV8Handle>::Size];
160         qint64 q_for_alignment;
161     };
162
163     // Pointers to allocData
164     union {
165         QString *qstringPtr;
166         QVariant *qvariantPtr;
167         QList<QObject *> *qlistPtr;
168         QJSValue *qjsValuePtr;
169         QDeclarativeV8Handle *handlePtr;
170     };
171
172     int type;
173 };
174 }
175
176 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object) 
177 : QV8ObjectResource(engine), object(object) 
178 {
179 }
180
181 QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index)
182 : QV8ObjectResource(engine), object(object), index(index)
183 {
184 }
185
186 static QAtomicInt objectIdCounter(1);
187
188 QV8QObjectWrapper::QV8QObjectWrapper()
189 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
190 {
191 }
192
193 QV8QObjectWrapper::~QV8QObjectWrapper()
194 {
195     for (TaintedHash::Iterator iter = m_taintedObjects.begin(); 
196          iter != m_taintedObjects.end();
197          ++iter) {
198         (*iter)->wrapper = 0;
199     }
200     m_taintedObjects.clear();
201 }
202
203 void QV8QObjectWrapper::destroy()
204 {
205     qDeleteAll(m_connections);
206     m_connections.clear();
207
208     qPersistentDispose(m_hiddenObject);
209     qPersistentDispose(m_destroySymbol);
210     qPersistentDispose(m_toStringSymbol);
211     qPersistentDispose(m_signalHandlerConstructor);
212     qPersistentDispose(m_methodConstructor);
213     qPersistentDispose(m_constructor);
214 }
215
216 #define FAST_VALUE_GETTER(name, cpptype, defaultvalue, constructor) \
217 static v8::Handle<v8::Value> name ## ValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) \
218 { \
219     v8::Handle<v8::Object> This = info.This(); \
220     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
221  \
222     if (resource->object.isNull()) return v8::Undefined(); \
223  \
224     QObject *object = resource->object; \
225  \
226     uint32_t data = info.Data()->Uint32Value(); \
227     int index = data & 0x7FFF; \
228     int notify = (data & 0x0FFF0000) >> 16; \
229     if (notify == 0x0FFF) notify = -1; \
230  \
231     QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
232     if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
233         typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
234         ep->capturedProperties << CapturedProperty(object, index, notify); \
235     } \
236  \
237     cpptype value = defaultvalue; \
238     void *args[] = { &value, 0 }; \
239     QMetaObject::metacall(object, QMetaObject::ReadProperty, index, args); \
240  \
241     return constructor(value); \
242 } \
243 static v8::Handle<v8::Value> name ## ValueGetterDirect(v8::Local<v8::String>, const v8::AccessorInfo &info) \
244 { \
245     v8::Handle<v8::Object> This = info.This(); \
246     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
247  \
248     if (resource->object.isNull()) return v8::Undefined(); \
249  \
250     QObject *object = resource->object; \
251  \
252     uint32_t data = info.Data()->Uint32Value(); \
253     int index = data & 0x7FFF; \
254     int notify = (data & 0x0FFF0000) >> 16; \
255     if (notify == 0x0FFF) notify = -1; \
256  \
257     QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
258     if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
259         typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
260         ep->capturedProperties << CapturedProperty(object, index, notify); \
261     } \
262  \
263     cpptype value = defaultvalue; \
264     void *args[] = { &value, 0 }; \
265     object->qt_metacall(QMetaObject::ReadProperty, index, args); \
266  \
267     return constructor(value); \
268
269
270 #define CREATE_FUNCTION \
271     "(function(method) { "\
272         "return (function(object, data, qmlglobal) { "\
273             "return (function() { "\
274                 "return method(object, data, qmlglobal, arguments.length, arguments); "\
275             "});"\
276         "});"\
277     "})"
278
279
280 static quint32 toStringHash = -1;
281 static quint32 destroyHash = -1;
282
283 void QV8QObjectWrapper::init(QV8Engine *engine)
284 {
285     m_engine = engine;
286
287     m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
288     m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
289     m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
290
291     m_toStringString = QHashedV8String(m_toStringSymbol);
292     m_destroyString = QHashedV8String(m_destroySymbol);
293
294     toStringHash = m_toStringString.hash();
295     destroyHash = m_destroyString.hash();
296
297     {
298     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
299     ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
300     ft->InstanceTemplate()->SetHasExternalResource(true);
301     m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
302     }
303     {
304     v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
305     v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin);
306     v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
307     v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
308     v8::Handle<v8::Value> args[] = { invokeFn };
309     v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
310     m_methodConstructor = qPersistentNew<v8::Function>(createFn);
311     }
312
313     v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine);
314     v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine);
315
316     {
317     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
318     ft->InstanceTemplate()->SetHasExternalResource(true);
319     ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum);
320     ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
321     m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
322     }
323
324     {
325     v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
326     prototype->Set(v8::String::New("connect"), connect, v8::DontEnum);
327     prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
328     }
329 }
330
331 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
332 {
333     return v8_resource_cast<QV8QObjectResource>(obj) != 0;
334 }
335
336 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
337 {
338     QV8QObjectResource *r =  v8_resource_cast<QV8QObjectResource>(obj);
339     return r?r->object:0;
340 }
341
342 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
343 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
344 {
345     Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
346     return static_cast<QV8QObjectResource *>(r)->object;
347 }
348
349 // Load value properties
350 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object, 
351                                           const QDeclarativePropertyData &property)
352 {
353     Q_ASSERT(!property.isFunction());
354
355 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
356     if (property.propType == QMetaType:: metatype) { \
357         cpptype type = cpptype(); \
358         void *args[] = { &type, 0 }; \
359         QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \
360         return constructor(type); \
361     }
362
363     if (property.isQObject()) {
364         QObject *rv = 0;
365         void *args[] = { &rv, 0 };
366         QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
367         return engine->newQObject(rv);
368     } else if (property.isQList()) {
369         return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
370     } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
371     else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
372     else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
373     else PROPERTY_LOAD(QString, QString, engine->toString)
374     else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
375     else PROPERTY_LOAD(Float, float, v8::Number::New)
376     else PROPERTY_LOAD(Double, double, v8::Number::New)
377     else if(property.isV8Handle()) {
378         QDeclarativeV8Handle handle;
379         void *args[] = { &handle, 0 };
380         QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); 
381         return handle.toHandle();
382     } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)
383                && engine->engine()) {
384         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
385         QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
386         if (valueType)
387             return engine->newValueType(object, property.coreIndex, valueType);
388     } 
389
390     QVariant var = object->metaObject()->property(property.coreIndex).read(object);
391     return engine->fromVariant(var);
392
393 #undef PROPERTY_LOAD
394 }
395
396 static v8::Handle<v8::Value> LoadPropertyDirect(QV8Engine *engine, QObject *object, 
397                                                 const QDeclarativePropertyData &property)
398 {
399     Q_ASSERT(!property.isFunction());
400
401 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
402     if (property.propType == QMetaType:: metatype) { \
403         cpptype type = cpptype(); \
404         void *args[] = { &type, 0 }; \
405         object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); \
406         return constructor(type); \
407     }
408
409     if (property.isQObject()) {
410         QObject *rv = 0;
411         void *args[] = { &rv, 0 };
412         QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
413         return engine->newQObject(rv);
414     } else if (property.isQList()) {
415         return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
416     } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
417     else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
418     else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
419     else PROPERTY_LOAD(QString, QString, engine->toString)
420     else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
421     else PROPERTY_LOAD(Float, float, v8::Number::New)
422     else PROPERTY_LOAD(Double, double, v8::Number::New)
423     else if(property.isV8Handle()) {
424         QDeclarativeV8Handle handle;
425         void *args[] = { &handle, 0 };
426         object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); 
427         return handle.toHandle();
428     } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)
429                && engine->engine()) {
430         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
431         QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
432         if (valueType)
433             return engine->newValueType(object, property.coreIndex, valueType);
434     } 
435
436     QVariant var = object->metaObject()->property(property.coreIndex).read(object);
437     return engine->fromVariant(var);
438
439 #undef PROPERTY_LOAD
440 }
441
442 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object, 
443                                                      v8::Handle<v8::Value> *objectHandle, 
444                                                      const QHashedV8String &property,
445                                                      QV8QObjectWrapper::RevisionMode revisionMode)
446 {
447     // XXX More recent versions of V8 introduced "Callable" objects.  It is possible that these
448     // will be a faster way of creating QObject method objects.
449     struct MethodClosure {
450        static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object, 
451                                            v8::Handle<v8::Value> *objectHandle, 
452                                            int index) { 
453            v8::Handle<v8::Value> argv[] = { 
454                objectHandle?*objectHandle:engine->newQObject(object),
455                v8::Integer::New(index)
456            };
457            return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
458        }
459        static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object, 
460                                                      v8::Handle<v8::Value> *objectHandle, 
461                                                      int index) { 
462            v8::Handle<v8::Value> argv[] = { 
463                objectHandle?*objectHandle:engine->newQObject(object),
464                v8::Integer::New(index),
465                v8::Context::GetCallingQmlGlobal()
466            };
467            return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
468        }
469     };
470
471     {
472         // Comparing the hash first actually makes a measurable difference here, at least on x86
473         quint32 hash = property.hash();
474         if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
475             return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
476         } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
477             return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
478         }
479     }
480
481     QDeclarativePropertyData local;
482     QDeclarativePropertyData *result = 0;
483     {
484         QDeclarativeData *ddata = QDeclarativeData::get(object, false);
485         if (ddata && ddata->propertyCache)
486             result = ddata->propertyCache->property(property);
487         else
488             result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
489     }
490
491     if (!result)
492         return v8::Handle<v8::Value>();
493
494     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
495         QDeclarativeData *ddata = QDeclarativeData::get(object);
496         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
497             return v8::Handle<v8::Value>();
498     }
499
500     typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
501
502     if (result->isFunction()) {
503         if (result->isVMEFunction()) {
504             return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
505         } else if (result->isV8Function()) {
506             return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
507         } else if (result->isSignalHandler()) {
508             v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
509             QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
510             handler->SetExternalResource(r);
511             return handler;
512         } else {
513             return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
514         }
515     }
516
517     QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
518     if (ep && ep->captureProperties && !result->isConstant()) {
519         if (result->coreIndex == 0)
520             ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
521         else
522             ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
523     }
524
525     if (result->isVMEProperty())
526         return static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject*>(object->metaObject()))->vmeProperty(result->coreIndex);
527
528     if (result->isDirect())  {
529         return LoadPropertyDirect(engine, object, *result);
530     } else {
531         return LoadProperty(engine, object, *result);
532     }
533 }
534
535 // Setter for writable properties.  Shared between the interceptor and fast property accessor
536 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyData *property,
537                                  v8::Handle<v8::Value> value)
538 {
539     QDeclarativeBinding *newBinding = 0;
540
541     if (value->IsFunction()) {
542         QDeclarativeContextData *context = engine->callingContext();
543         v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
544
545         v8::Local<v8::StackTrace> trace = 
546             v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | 
547                                                                                      v8::StackTrace::kScriptName));
548         v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
549         int lineNumber = frame->GetLineNumber();
550         QString url = engine->toString(frame->GetScriptName());
551
552         newBinding = new QDeclarativeBinding(&function, object, context);
553         newBinding->setSourceLocation(url, lineNumber);
554         newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, object, context));
555         newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
556     }
557
558     QDeclarativeAbstractBinding *oldBinding = 
559         QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
560     if (oldBinding)
561         oldBinding->destroy();
562
563 #define PROPERTY_STORE(cpptype, value) \
564     cpptype o = value; \
565     int status = -1; \
566     int flags = 0; \
567     void *argv[] = { &o, 0, &status, &flags }; \
568     QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
569
570
571     if (value->IsNull() && property->isQObject()) {
572         PROPERTY_STORE(QObject*, 0);
573     } else if (value->IsUndefined() && property->isResettable()) {
574         void *a[] = { 0 };
575         QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
576     } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
577         PROPERTY_STORE(QVariant, QVariant());
578     } else if (value->IsUndefined()) {
579         QString error = QLatin1String("Cannot assign [undefined] to ") +
580                         QLatin1String(QMetaType::typeName(property->propType));
581         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
582     } else if (value->IsFunction()) {
583         // this is handled by the binding creation above
584     } else if (property->propType == QMetaType::Int && value->IsNumber()) {
585         PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
586     } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
587         PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
588     } else if (property->propType == QMetaType::Float && value->IsNumber()) {
589         PROPERTY_STORE(float, float(value->ToNumber()->Value()));
590     } else if (property->propType == QMetaType::Double && value->IsNumber()) {
591         PROPERTY_STORE(double, double(value->ToNumber()->Value()));
592     } else if (property->propType == QMetaType::QString && value->IsString()) {
593         PROPERTY_STORE(QString, engine->toString(value->ToString()));
594     } else if (property->isVMEProperty()) {
595         static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
596     } else {
597         QVariant v;
598         if (property->isQList()) 
599             v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
600         else
601             v = engine->toVariant(value, property->propType);
602
603         QDeclarativeContextData *context = engine->callingContext();
604
605         if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
606             const char *valueType = 0;
607             if (v.userType() == QVariant::Invalid) valueType = "null";
608             else valueType = QMetaType::typeName(v.userType());
609
610             QString error = QLatin1String("Cannot assign ") +
611                             QLatin1String(valueType) +
612                             QLatin1String(" to ") +
613                             QLatin1String(QMetaType::typeName(property->propType));
614             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
615         }
616     }
617 }
618
619 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
620                                     v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
621 {
622     if (engine->qobjectWrapper()->m_toStringString == property ||
623         engine->qobjectWrapper()->m_destroyString == property)
624         return true;
625
626     QDeclarativePropertyData local;
627     QDeclarativePropertyData *result = 0;
628     result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
629
630     if (!result)
631         return false;
632
633     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
634         QDeclarativeData *ddata = QDeclarativeData::get(object);
635         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
636             return false;
637     }
638
639     if (!result->isWritable() && !result->isQList()) {
640         QString error = QLatin1String("Cannot assign to read-only property \"") +
641                         engine->toString(property.string()) + QLatin1Char('\"');
642         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
643         return true;
644     }
645
646     StoreProperty(engine, object, result, value);
647
648     return true;
649 }
650
651 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property, 
652                                                 const v8::AccessorInfo &info)
653 {
654     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
655
656     if (resource->object.isNull()) 
657         return v8::Handle<v8::Value>();
658
659     QObject *object = resource->object;
660
661     QHashedV8String propertystring(property);
662
663     QV8Engine *v8engine = resource->engine;
664     v8::Handle<v8::Value> This = info.This();
665     v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring, 
666                                                QV8QObjectWrapper::IgnoreRevision);
667     if (!result.IsEmpty())
668         return result;
669
670     if (QV8Engine::startsWithUpper(property)) {
671         // Check for attached properties
672         QDeclarativeContextData *context = v8engine->callingContext();
673
674         if (context && context->imports) {
675             QDeclarativeTypeNameCache::Result r = context->imports->query(propertystring);
676
677             if (r.isValid()) {
678                 if (r.scriptIndex != -1) {
679                     return v8::Undefined();
680                 } else if (r.type) {
681                     return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
682                 } else if (r.importNamespace) {
683                     return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace, 
684                                                               QV8TypeWrapper::ExcludeEnums);
685                 }
686                 Q_ASSERT(!"Unreachable");
687             }
688         }
689     } 
690
691     return v8::Handle<v8::Value>();
692 }
693
694 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property, 
695                                                 v8::Local<v8::Value> value,
696                                                 const v8::AccessorInfo &info)
697 {
698     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
699
700     if (resource->object.isNull()) 
701         return value;
702
703     QObject *object = resource->object;
704
705     QHashedV8String propertystring(property);
706
707     QV8Engine *v8engine = resource->engine;
708     bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
709
710     if (!result) {
711         QString error = QLatin1String("Cannot assign to non-existent property \"") +
712                         v8engine->toString(property) + QLatin1Char('\"');
713         v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
714         return value;
715     }
716
717     return value;
718 }
719
720 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
721                                                  const v8::AccessorInfo &info)
722 {
723     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
724
725     if (resource->object.isNull()) 
726         return v8::Handle<v8::Integer>();
727
728     QV8Engine *engine = resource->engine;
729     QObject *object = resource->object;
730
731     QHashedV8String propertystring(property);
732
733     QDeclarativePropertyData local;
734     QDeclarativePropertyData *result = 0;
735     result = QDeclarativePropertyCache::property(engine->engine(), object, propertystring, local);
736
737     if (!result)
738         return v8::Handle<v8::Integer>();
739     else if (!result->isWritable() && !result->isQList())
740         return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
741     else
742         return v8::Integer::New(v8::DontDelete);
743 }
744
745 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
746 {
747     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
748
749     if (resource->object.isNull()) 
750         return v8::Array::New();
751
752     QObject *object = resource->object;
753
754     QStringList result;
755
756     QDeclarativeEnginePrivate *ep = resource->engine->engine()
757             ? QDeclarativeEnginePrivate::get(resource->engine->engine())
758             : 0;
759
760     QDeclarativePropertyCache *cache = 0;
761     QDeclarativeData *ddata = QDeclarativeData::get(object);
762     if (ddata)
763         cache = ddata->propertyCache;
764
765     if (!cache) {
766         cache = ep ? ep->cache(object) : 0;
767         if (cache) {
768             if (ddata) { cache->addref(); ddata->propertyCache = cache; }
769         } else {
770             // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
771             const QMetaObject *mo = object->metaObject();
772             int pc = mo->propertyCount();
773             int po = mo->propertyOffset();
774             for (int i=po; i<pc; ++i)
775                 result << QString::fromUtf8(mo->property(i).name());
776         }
777     } else {
778         result = cache->propertyNames();
779     }
780
781     v8::Local<v8::Array> rv = v8::Array::New(result.count());
782
783     for (int ii = 0; ii < result.count(); ++ii) 
784         rv->Set(ii, resource->engine->toString(result.at(ii)));
785
786     return rv;
787 }
788
789 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
790 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
791 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
792 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
793 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
794 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
795 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
796
797 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
798                             const v8::AccessorInfo& info)
799 {
800     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
801
802     if (resource->object.isNull()) 
803         return; 
804
805     QObject *object = resource->object;
806
807     uint32_t data = info.Data()->Uint32Value(); 
808     int index = data & 0x7FFF;  // So that we can use the same data for Setter and Getter
809
810     QDeclarativeData *ddata = QDeclarativeData::get(object, false);
811     Q_ASSERT(ddata);
812     Q_ASSERT(ddata->propertyCache);
813
814     QDeclarativePropertyData *pdata = ddata->propertyCache->property(index);
815     Q_ASSERT(pdata);
816
817     Q_ASSERT(pdata->isWritable() || pdata->isQList());
818
819     StoreProperty(resource->engine, object, pdata, value);
820 }
821
822 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
823                                     const v8::AccessorInfo& info)
824 {
825     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
826
827     if (resource->object.isNull()) 
828         return; 
829
830     QV8Engine *v8engine = resource->engine;
831
832     QString error = QLatin1String("Cannot assign to read-only property \"") +
833                     v8engine->toString(property) + QLatin1Char('\"');
834     v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
835 }
836
837 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
838 {
839     Q_ASSERT(handle->IsObject());
840     
841     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
842
843     Q_ASSERT(resource);
844
845     QObject *object = resource->object;
846     if (object) {
847         QDeclarativeData *ddata = QDeclarativeData::get(object, false);
848         if (ddata) {
849             ddata->v8object.Clear();
850             if (!object->parent() && !ddata->indestructible)
851                 object->deleteLater();
852         }
853     }
854
855     qPersistentDispose(handle);
856 }
857
858 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
859 {
860     QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
861     instance->v8object.Clear();
862     qPersistentDispose(handle);
863 }
864
865 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
866 {
867     Q_ASSERT(object);
868     Q_ASSERT(this->engine);
869
870     Q_ASSERT(QDeclarativeData::get(object, false));
871     Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
872
873     // Setup constructor
874     if (constructor.IsEmpty()) {
875         v8::Local<v8::FunctionTemplate> ft;
876
877         QString toString = QLatin1String("toString");
878         QString destroy = QLatin1String("destroy");
879
880         // XXX TODO: Enables fast property accessors.  These more than double the property access 
881         // performance, but the  cost of setting up this structure hasn't been measured so 
882         // its not guarenteed that this is a win overall.  We need to try and measure the cost.
883         for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
884             QDeclarativePropertyData *property = *iter;
885             if (property->isFunction() || 
886                 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x0FFF || 
887                 property->coreIndex == 0)
888                 continue;
889
890             v8::AccessorGetter fastgetter = 0;
891             v8::AccessorSetter fastsetter = FastValueSetter;
892             if (!property->isWritable())
893                 fastsetter = FastValueSetterReadOnly;
894
895             if (property->isQObject()) 
896                 fastgetter = property->isDirect()?QObjectValueGetterDirect:QObjectValueGetter;
897             else if (property->propType == QMetaType::Int || property->isEnum()) 
898                 fastgetter = property->isDirect()?IntValueGetterDirect:IntValueGetter;
899             else if (property->propType == QMetaType::Bool)
900                 fastgetter = property->isDirect()?BoolValueGetterDirect:BoolValueGetter;
901             else if (property->propType == QMetaType::QString)
902                 fastgetter = property->isDirect()?QStringValueGetterDirect:QStringValueGetter;
903             else if (property->propType == QMetaType::UInt)
904                 fastgetter = property->isDirect()?UIntValueGetterDirect:UIntValueGetter;
905             else if (property->propType == QMetaType::Float) 
906                 fastgetter = property->isDirect()?FloatValueGetterDirect:FloatValueGetter;
907             else if (property->propType == QMetaType::Double) 
908                 fastgetter = property->isDirect()?DoubleValueGetterDirect:DoubleValueGetter;
909
910             if (fastgetter) {
911                 int notifyIndex = property->notifyIndex;
912                 if (property->isConstant()) notifyIndex = 0;
913                 else if (notifyIndex == -1) notifyIndex = 0x0FFF;
914                 uint32_t data = (notifyIndex & 0x0FFF) << 16 | property->coreIndex;
915
916                 QString name = iter.key();
917                 if (name == toString || name == destroy)
918                     continue;
919
920                 if (ft.IsEmpty()) {
921                     ft = v8::FunctionTemplate::New();
922                     ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
923                                                                        QV8QObjectWrapper::Setter,
924                                                                        QV8QObjectWrapper::Query, 
925                                                                        0,
926                                                                        QV8QObjectWrapper::Enumerator);
927                     ft->InstanceTemplate()->SetHasExternalResource(true);
928                 }
929
930                 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
931                                                     v8::Integer::NewFromUnsigned(data));
932             }
933         }
934
935         if (ft.IsEmpty()) {
936             constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
937         } else {
938             ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
939                                                                QV8QObjectWrapper::Setter,
940                                                                QV8QObjectWrapper::Query, 
941                                                                0,
942                                                                QV8QObjectWrapper::Enumerator);
943             ft->InstanceTemplate()->SetHasExternalResource(true);
944             constructor = qPersistentNew<v8::Function>(ft->GetFunction());
945         }
946
947         QDeclarativeCleanup::addToEngine(this->engine);
948     }
949
950     v8::Local<v8::Object> result = constructor->NewInstance();
951     QV8QObjectResource *r = new QV8QObjectResource(engine, object);
952     result->SetExternalResource(r);
953     return result;
954 }
955
956 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
957 {
958     v8::Local<v8::Object> rv;
959
960     if (!ddata->propertyCache && engine->engine()) {
961         ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
962         if (ddata->propertyCache) ddata->propertyCache->addref();
963     }
964
965     if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
966         rv = ddata->propertyCache->newQObject(object, engine);
967     } else {
968         // XXX NewInstance() should be optimized
969         rv = m_constructor->NewInstance(); 
970         QV8QObjectResource *r = new QV8QObjectResource(engine, object);
971         rv->SetExternalResource(r);
972     }
973
974     return rv;
975 }
976
977 /*
978 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
979 V8 handle for subsequent calls to newQObject for the same QObject.  To do this we have a two
980 pronged strategy:
981    1. If there is no current outstanding V8 handle to the QObject, we create one and store a 
982       persistent handle in QDeclarativeData::v8object.  We mark the QV8QObjectWrapper that 
983       "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this 
984       QV8QObjectWrapper.
985    2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create 
986       an entry in the m_taintedObject hash where we store the handle and mark the object as 
987       "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
988 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
989 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has 
990 released the handle.
991 */
992 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
993 {
994     if (!object)
995         return v8::Null();
996
997     if (QObjectPrivate::get(object)->wasDeleted)
998        return v8::Undefined();
999     
1000     QDeclarativeData *ddata = QDeclarativeData::get(object, true);
1001
1002     if (!ddata) 
1003         return v8::Undefined();
1004
1005     if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1006         // We own the v8object 
1007         return v8::Local<v8::Object>::New(ddata->v8object);
1008     } else if (ddata->v8object.IsEmpty() && 
1009                (ddata->v8objectid == m_id || // We own the QObject
1010                 ddata->v8objectid == 0 ||    // No one owns the QObject
1011                 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1012
1013         v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1014         ddata->v8object = qPersistentNew<v8::Object>(rv);
1015         ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1016         ddata->v8objectid = m_id;
1017         return rv;
1018
1019     } else {
1020         // If this object is tainted, we have to check to see if it is in our
1021         // tainted object list
1022         TaintedHash::Iterator iter =
1023             ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1024         bool found = iter != m_taintedObjects.end();
1025
1026         // If our tainted handle doesn't exist or has been collected, and there isn't
1027         // a handle in the ddata, we can assume ownership of the ddata->v8object
1028         if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1029             v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1030             ddata->v8object = qPersistentNew<v8::Object>(rv);
1031             ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1032             ddata->v8objectid = m_id;
1033
1034             if (found) {
1035                 delete (*iter);
1036                 m_taintedObjects.erase(iter);
1037             }
1038
1039             return rv;
1040         } else if (!found) {
1041             QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1042             iter = m_taintedObjects.insert(object, instance);
1043             ddata->hasTaintedV8Object = true;
1044         }
1045
1046         if ((*iter)->v8object.IsEmpty()) {
1047             v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1048             (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1049             (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1050         }
1051
1052         return v8::Local<v8::Object>::New((*iter)->v8object);
1053     }
1054 }
1055
1056 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1057 {
1058     if (object->IsFunction())
1059         return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1060
1061     if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1062         return qMakePair(resource->object.data(), resource->index);
1063
1064     return qMakePair((QObject *)0, -1);
1065 }
1066
1067 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1068 {
1069     v8::ScriptOrigin origin = function->GetScriptOrigin();
1070     if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1071
1072         // This is one of our special QObject method wrappers
1073         v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1074         v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1075
1076         if (data->IsArray()) {
1077             v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1078             return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1079         } 
1080
1081         // In theory this can't fall through, but I suppose V8 might run out of memory or something
1082     }
1083
1084     return qMakePair((QObject *)0, -1);
1085 }
1086
1087 class QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
1088 {
1089 public:
1090     QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1091     ~QV8QObjectConnectionList();
1092
1093     struct Connection {
1094         Connection() 
1095         : needsDestroy(false) {}
1096         Connection(const Connection &other) 
1097         : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1098         Connection &operator=(const Connection &other) {
1099             thisObject = other.thisObject;
1100             function = other.function;
1101             needsDestroy = other.needsDestroy;
1102             return *this;
1103         }
1104
1105         v8::Persistent<v8::Object> thisObject;
1106         v8::Persistent<v8::Function> function;
1107
1108         void dispose() {
1109             qPersistentDispose(thisObject);
1110             qPersistentDispose(function);
1111         }
1112
1113         bool needsDestroy;
1114     };
1115
1116     struct ConnectionList : public QList<Connection> {
1117         ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1118         int connectionsInUse;
1119         bool connectionsNeedClean;
1120     };
1121
1122     QV8Engine *engine;
1123
1124     typedef QHash<int, ConnectionList> SlotHash;
1125     SlotHash slotHash;
1126     bool needsDestroy;
1127     int inUse;
1128
1129     virtual void objectDestroyed(QObject *);
1130     virtual int qt_metacall(QMetaObject::Call, int, void **);
1131 };
1132
1133 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1134 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1135 {
1136 }
1137
1138 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1139 {
1140     for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1141         QList<Connection> &connections = *iter;
1142         for (int ii = 0; ii < connections.count(); ++ii) {
1143             qPersistentDispose(connections[ii].thisObject);
1144             qPersistentDispose(connections[ii].function);
1145         }
1146     }
1147     slotHash.clear();
1148 }
1149
1150 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1151 {
1152     engine->qobjectWrapper()->m_connections.remove(object);
1153
1154     if (inUse)
1155         needsDestroy = true;
1156     else
1157         delete this;
1158 }
1159
1160 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1161 {
1162     if (method == QMetaObject::InvokeMetaMethod) {
1163         SlotHash::Iterator iter = slotHash.find(index);
1164         if (iter == slotHash.end())
1165             return -1;
1166         ConnectionList &connectionList = *iter;
1167         if (connectionList.isEmpty())
1168             return -1;
1169
1170         inUse++;
1171
1172         connectionList.connectionsInUse++;
1173
1174         QList<Connection> connections = connectionList;
1175
1176         QVarLengthArray<int, 9> dummy;
1177         int *argsTypes = QDeclarativePropertyCache::methodParameterTypes(data(), index, dummy, 0);
1178
1179         v8::HandleScope handle_scope;
1180         v8::Context::Scope scope(engine->context());
1181
1182         int argCount = argsTypes?argsTypes[0]:0;
1183         QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1184
1185         for (int ii = 0; ii < argCount; ++ii) {
1186             int type = argsTypes[ii + 1];
1187             if (type == qMetaTypeId<QVariant>()) {
1188                 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1189             } else {
1190                 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1191             }
1192         }
1193
1194         for (int ii = 0; ii < connections.count(); ++ii) {
1195             Connection &connection = connections[ii];
1196             if (connection.needsDestroy)
1197                 continue;
1198             if (connection.thisObject.IsEmpty()) {
1199                 connection.function->Call(engine->global(), argCount, args.data());
1200             } else {
1201                 connection.function->Call(connection.thisObject, argCount, args.data());
1202             }
1203         }
1204
1205         connectionList.connectionsInUse--;
1206         if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1207             for (QList<Connection>::Iterator iter = connectionList.begin(); 
1208                  iter != connectionList.end(); ) {
1209                 if (iter->needsDestroy) {
1210                     iter->dispose();
1211                     iter = connectionList.erase(iter);
1212                 } else {
1213                     ++iter;
1214                 }
1215             }
1216         }
1217
1218         inUse--;
1219         if (inUse == 0 && needsDestroy)
1220             delete this;
1221     } 
1222
1223     return -1;
1224 }
1225
1226 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1227 {
1228     if (args.Length() == 0)
1229         V8THROW_ERROR("Function.prototype.connect: no arguments given");
1230
1231     QV8Engine *engine = V8ENGINE();
1232
1233     QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1234     QObject *signalObject = signalInfo.first;
1235     int signalIndex = signalInfo.second;
1236
1237     if (signalIndex == -1)
1238         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1239
1240     if (!signalObject)
1241         V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1242
1243     if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1244         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1245
1246     v8::Local<v8::Value> functionValue;
1247     v8::Local<v8::Value> functionThisValue;
1248
1249     if (args.Length() == 1) {
1250         functionValue = args[0];
1251     } else {
1252         functionThisValue = args[0];
1253         functionValue = args[1];
1254     }
1255
1256     if (!functionValue->IsFunction())
1257         V8THROW_ERROR("Function.prototype.connect: target is not a function");
1258
1259     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1260         V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1261
1262     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1263     QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1264     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1265     if (iter == connections.end()) 
1266         iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1267
1268     QV8QObjectConnectionList *connectionList = *iter;
1269     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1270     if (slotIter == connectionList->slotHash.end()) {
1271         slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1272         QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1273     }
1274
1275     QV8QObjectConnectionList::Connection connection;
1276     if (!functionThisValue.IsEmpty()) 
1277         connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1278     connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1279
1280     slotIter->append(connection);
1281
1282     return v8::Undefined();
1283 }
1284
1285 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1286 {
1287     if (args.Length() == 0)
1288         V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1289
1290     QV8Engine *engine = V8ENGINE();
1291
1292     QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1293     QObject *signalObject = signalInfo.first;
1294     int signalIndex = signalInfo.second;
1295
1296     if (signalIndex == -1)
1297         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1298
1299     if (!signalObject)
1300         V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1301
1302     if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1303         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1304
1305     v8::Local<v8::Value> functionValue;
1306     v8::Local<v8::Value> functionThisValue;
1307
1308     if (args.Length() == 1) {
1309         functionValue = args[0];
1310     } else {
1311         functionThisValue = args[0];
1312         functionValue = args[1];
1313     }
1314
1315     if (!functionValue->IsFunction())
1316         V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1317
1318     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1319         V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1320
1321     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1322     QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1323     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1324     if (iter == connectionsList.end()) 
1325         return v8::Undefined(); // Nothing to disconnect from
1326
1327     QV8QObjectConnectionList *connectionList = *iter;
1328     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1329     if (slotIter == connectionList->slotHash.end()) 
1330         return v8::Undefined(); // Nothing to disconnect from
1331
1332     QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1333
1334     v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1335     QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1336
1337     if (functionData.second != -1) {
1338         // This is a QObject function wrapper
1339         for (int ii = 0; ii < connections.count(); ++ii) {
1340             QV8QObjectConnectionList::Connection &connection = connections[ii];
1341
1342             if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1343                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1344
1345                 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1346                 if (connectedFunctionData == functionData) {
1347                     // Match!
1348                     if (connections.connectionsInUse) {
1349                         connection.needsDestroy = true;
1350                     } else {
1351                         connection.dispose();
1352                         connections.removeAt(ii);
1353                     }
1354                     return v8::Undefined();
1355                 }
1356             }
1357         }
1358
1359     } else {
1360         // This is a normal JS function
1361         for (int ii = 0; ii < connections.count(); ++ii) {
1362             QV8QObjectConnectionList::Connection &connection = connections[ii];
1363             if (connection.function->StrictEquals(function) &&
1364                 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1365                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1366                 // Match!
1367                 if (connections.connectionsInUse) {
1368                     connection.needsDestroy = true;
1369                 } else {
1370                     connection.dispose();
1371                     connections.removeAt(ii);
1372                 }
1373                 return v8::Undefined();
1374             }
1375         }
1376     }
1377
1378     return v8::Undefined();
1379 }
1380
1381 /*!
1382     \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1383
1384     Get the \a property of \a object.  Returns an empty handle if the property doesn't exist.
1385
1386     Only searches for real properties of \a object (including methods), not attached properties etc.
1387 */
1388
1389 /*
1390     \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1391
1392     Set the \a property of \a object to \a value.
1393
1394     Returns true if the property was "set" - even if this results in an exception being thrown -
1395     and false if the object has no such property.
1396
1397     Only searches for real properties of \a object (including methods), not attached properties etc.
1398 */
1399
1400 namespace {
1401 struct CallArgs
1402 {
1403     CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1404     int Length() const { return _length; }
1405     v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1406
1407 private:
1408     int _length;
1409     v8::Handle<v8::Object> *_args;
1410 };
1411 }
1412
1413 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount, 
1414                                         int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1415 {
1416     if (argCount > 0) {
1417
1418         QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1419         args[0].initAsType(returnType);
1420
1421         for (int ii = 0; ii < argCount; ++ii)
1422             args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1423
1424         QVarLengthArray<void *, 9> argData(args.count());
1425         for (int ii = 0; ii < args.count(); ++ii)
1426             argData[ii] = args[ii].dataPtr();
1427
1428         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1429
1430         return args[0].toValue(engine);
1431
1432     } else if (returnType != 0) {
1433         
1434         MetaCallArgument arg;
1435         arg.initAsType(returnType);
1436
1437         void *args[] = { arg.dataPtr() };
1438
1439         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1440
1441         return arg.toValue(engine);
1442
1443     } else {
1444
1445         void *args[] = { 0 };
1446         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1447         return v8::Undefined();
1448
1449     }
1450 }
1451
1452 /*!
1453     Returns the match score for converting \a actual to be of type \a conversionType.  A 
1454     zero score means "perfect match" whereas a higher score is worse.
1455
1456     The conversion table is copied out of the QtScript callQtMethod() function.
1457 */
1458 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1459 {
1460     if (actual->IsNumber()) {
1461         switch (conversionType) {
1462         case QMetaType::Double:
1463             return 0;
1464         case QMetaType::Float:
1465             return 1;
1466         case QMetaType::LongLong:
1467         case QMetaType::ULongLong:
1468             return 2;
1469         case QMetaType::Long:
1470         case QMetaType::ULong:
1471             return 3;
1472         case QMetaType::Int:
1473         case QMetaType::UInt:
1474             return 4;
1475         case QMetaType::Short:
1476         case QMetaType::UShort:
1477             return 5;
1478             break;
1479         case QMetaType::Char:
1480         case QMetaType::UChar:
1481             return 6;
1482         default:
1483             return 10;
1484         }
1485     } else if (actual->IsString()) {
1486         switch (conversionType) {
1487         case QMetaType::QString:
1488             return 0;
1489         default:
1490             return 10;
1491         }
1492     } else if (actual->IsBoolean()) {
1493         switch (conversionType) {
1494         case QMetaType::Bool:
1495             return 0;
1496         default:
1497             return 10;
1498         }
1499     } else if (actual->IsDate()) {
1500         switch (conversionType) {
1501         case QMetaType::QDateTime:
1502             return 0;
1503         case QMetaType::QDate:
1504             return 1;
1505         case QMetaType::QTime:
1506             return 2;
1507         default:
1508             return 10;
1509         }
1510     } else if (actual->IsRegExp()) {
1511         switch (conversionType) {
1512         case QMetaType::QRegExp:
1513             return 0;
1514         default:
1515             return 10;
1516         }
1517     } else if (actual->IsArray()) {
1518         switch (conversionType) {
1519         case QMetaType::QStringList:
1520         case QMetaType::QVariantList:
1521             return 5;
1522         default:
1523             return 10;
1524         }
1525     } else if (actual->IsNull()) {
1526         switch (conversionType) {
1527         case QMetaType::VoidStar:
1528         case QMetaType::QObjectStar:
1529             return 0;
1530         default: {
1531             const char *typeName = QMetaType::typeName(conversionType);
1532             if (typeName && typeName[strlen(typeName) - 1] == '*')
1533                 return 0;
1534             else
1535                 return 10;
1536         }
1537         }
1538     } else if (actual->IsObject()) {
1539         v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1540
1541         QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1542         if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1543             switch (conversionType) {
1544             case QMetaType::QObjectStar:
1545                 return 0;
1546             default:
1547                 return 10;
1548             }
1549         } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1550             if (conversionType == qMetaTypeId<QVariant>())
1551                 return 0;
1552             else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1553                 return 0;
1554             else
1555                 return 10;
1556         } else {
1557             return 10;
1558         }
1559
1560     } else {
1561         return 10;
1562     }
1563 }
1564
1565 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1566 {
1567     struct Private
1568     {
1569         int revision;
1570         int className;
1571         int classInfoCount, classInfoData;
1572         int methodCount, methodData;
1573     };
1574
1575     return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1576 }
1577
1578 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1579 {
1580     QByteArray sig = m.signature();
1581     int paren = sig.indexOf('(');
1582     if (paren == -1)
1583         return sig;
1584     else
1585         return sig.left(paren);
1586 }
1587
1588 /*!
1589 Returns the next related method, if one, or 0.
1590 */
1591 static const QDeclarativePropertyData * RelatedMethod(QObject *object,
1592                                                       const QDeclarativePropertyData *current,
1593                                                       QDeclarativePropertyData &dummy)
1594 {
1595     QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1596     if (!current->isOverload())
1597         return 0;
1598
1599     Q_ASSERT(!current->overrideIndexIsProperty);
1600
1601     if (cache) {
1602         return cache->method(current->overrideIndex);
1603     } else {
1604         const QMetaObject *mo = object->metaObject();
1605         int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1606
1607         while (methodOffset > current->overrideIndex) {
1608             mo = mo->superClass();
1609             methodOffset -= QMetaObject_methods(mo);
1610         }
1611
1612         QMetaMethod method = mo->method(current->overrideIndex);
1613         dummy.load(method);
1614         
1615         // Look for overloaded methods
1616         QByteArray methodName = QMetaMethod_name(method);
1617         for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1618             if (methodName == QMetaMethod_name(mo->method(ii))) {
1619                 dummy.setFlags(dummy.getFlags() | QDeclarativePropertyData::IsOverload);
1620                 dummy.overrideIndexIsProperty = 0;
1621                 dummy.overrideIndex = ii;
1622                 return &dummy;
1623             }
1624         }
1625
1626         return &dummy;
1627     }
1628 }
1629
1630 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyData &data,
1631                                          QV8Engine *engine, CallArgs &callArgs)
1632 {
1633     if (data.hasArguments()) {
1634
1635         int *args = 0;
1636         QVarLengthArray<int, 9> dummy;
1637         QByteArray unknownTypeError;
1638
1639         args = QDeclarativePropertyCache::methodParameterTypes(object, data.coreIndex, dummy, 
1640                                                                &unknownTypeError);
1641
1642         if (!args) {
1643             QString typeName = QString::fromLatin1(unknownTypeError);
1644             QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1645             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1646             return v8::Handle<v8::Value>();
1647         }
1648
1649         if (args[0] > callArgs.Length()) {
1650             QString error = QLatin1String("Insufficient arguments");
1651             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1652             return v8::Handle<v8::Value>();
1653         }
1654
1655         return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1656
1657     } else {
1658
1659         return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1660
1661     }
1662 }
1663
1664 /*!
1665 Resolve the overloaded method to call.  The algorithm works conceptually like this:
1666     1.  Resolve the set of overloads it is *possible* to call.
1667         Impossible overloads include those that have too many parameters or have parameters 
1668         of unknown type.  
1669     2.  Filter the set of overloads to only contain those with the closest number of 
1670         parameters.
1671         For example, if we are called with 3 parameters and there are 2 overloads that
1672         take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1673     3.  Find the best remaining overload based on its match score.  
1674         If two or more overloads have the same match score, call the last one.  The match
1675         score is constructed by adding the matchScore() result for each of the parameters.
1676 */
1677 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyData &data,
1678                                             QV8Engine *engine, CallArgs &callArgs)
1679 {
1680     int argumentCount = callArgs.Length();
1681
1682     const QDeclarativePropertyData *best = 0;
1683     int bestParameterScore = INT_MAX;
1684     int bestMatchScore = INT_MAX;
1685
1686     QDeclarativePropertyData dummy;
1687     const QDeclarativePropertyData *attempt = &data;
1688
1689     do {
1690         QVarLengthArray<int, 9> dummy;
1691         int methodArgumentCount = 0;
1692         int *methodArgTypes = 0;
1693         if (attempt->hasArguments()) {
1694             typedef QDeclarativePropertyCache PC;
1695             int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1696             if (!args) // Must be an unknown argument
1697                 continue;
1698
1699             methodArgumentCount = args[0];
1700             methodArgTypes = args + 1;
1701         }
1702
1703         if (methodArgumentCount > argumentCount)
1704             continue; // We don't have sufficient arguments to call this method
1705
1706         int methodParameterScore = argumentCount - methodArgumentCount;
1707         if (methodParameterScore > bestParameterScore)
1708             continue; // We already have a better option
1709
1710         int methodMatchScore = 0;
1711         for (int ii = 0; ii < methodArgumentCount; ++ii) 
1712             methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1713
1714         if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1715             best = attempt;
1716             bestParameterScore = methodParameterScore;
1717             bestMatchScore = methodMatchScore;
1718         }
1719
1720         if (bestParameterScore == 0 && bestMatchScore == 0)
1721             break; // We can't get better than that
1722
1723     } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1724
1725     if (best) {
1726         return CallPrecise(object, *best, engine, callArgs);
1727     } else {
1728         QString error = QLatin1String("Unable to determine callable overload.  Candidates are:");
1729         const QDeclarativePropertyData *candidate = &data;
1730         while (candidate) {
1731             error += QLatin1String("\n    ") + 
1732                      QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1733             candidate = RelatedMethod(object, candidate, dummy);
1734         }
1735
1736         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1737         return v8::Handle<v8::Value>();
1738     }
1739 }
1740
1741 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1742 {
1743     QString result;
1744     if (object) {
1745         QString objectName = object->objectName();
1746
1747         result += QString::fromUtf8(object->metaObject()->className());
1748         result += QLatin1String("(0x");
1749         result += QString::number((quintptr)object,16);
1750
1751         if (!objectName.isEmpty()) {
1752             result += QLatin1String(", \"");
1753             result += objectName;
1754             result += QLatin1Char('\"');
1755         }
1756
1757         result += QLatin1Char(')');
1758     } else {
1759         result = QLatin1String("null");
1760     }
1761
1762     return engine->toString(result);
1763 }
1764
1765 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1766 {
1767     QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1768     if (!ddata || ddata->indestructible) {
1769         const char *error = "Invalid attempt to destroy() an indestructible object";
1770         v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1771         return v8::Undefined();
1772     }
1773
1774     int delay = 0;
1775     if (argCount > 0)
1776         delay = args->Get(0)->Uint32Value();
1777
1778     if (delay > 0)
1779         QTimer::singleShot(delay, object, SLOT(deleteLater()));
1780     else
1781         object->deleteLater();
1782
1783     return v8::Undefined();
1784 }
1785
1786 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1787 {
1788     // object, index, qmlglobal, argCount, args
1789     Q_ASSERT(args.Length() == 5);
1790     Q_ASSERT(args[0]->IsObject());
1791
1792     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1793
1794     if (!resource)
1795         return v8::Undefined();
1796
1797     int argCount = args[3]->Int32Value();
1798     v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1799
1800     // Special hack to return info about this closure.
1801     if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1802         v8::Local<v8::Array> data = v8::Array::New(2);
1803         data->Set(0, args[0]);
1804         data->Set(1, args[1]);
1805         return data;
1806     }
1807
1808     QObject *object = resource->object;
1809     int index = args[1]->Int32Value();
1810
1811     if (!object)
1812         return v8::Undefined();
1813
1814     if (index < 0) {
1815         // Builtin functions
1816         if (index == QOBJECT_TOSTRING_INDEX) {
1817             return ToString(resource->engine, object, argCount, arguments);
1818         } else if (index == QOBJECT_DESTROY_INDEX) {
1819             return Destroy(resource->engine, object, argCount, arguments);
1820         } else {
1821             return v8::Undefined();
1822         }
1823     }
1824
1825     QDeclarativePropertyData method;
1826
1827     if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1828         if (ddata->propertyCache) {
1829             QDeclarativePropertyData *d = ddata->propertyCache->method(index);
1830             if (!d) 
1831                 return v8::Undefined();
1832             method = *d;
1833         } 
1834     }
1835
1836     if (method.coreIndex == -1) {
1837         method.load(object->metaObject()->method(index));
1838
1839         if (method.coreIndex == -1)
1840             return v8::Undefined();
1841     }
1842
1843     if (method.isV8Function()) {
1844         v8::Handle<v8::Value> rv;
1845         v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1846
1847         QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal, 
1848                                     resource->engine->contextWrapper()->context(qmlglobal),
1849                                     resource->engine);
1850         QDeclarativeV8Function *funcptr = &func;
1851
1852         void *args[] = { 0, &funcptr };
1853         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1854
1855         if (rv.IsEmpty()) return v8::Undefined();
1856         return rv;
1857     }
1858
1859     CallArgs callArgs(argCount, &arguments);
1860     if (!method.isOverload()) {
1861         return CallPrecise(object, method, resource->engine, callArgs);
1862     } else {
1863         return CallOverloaded(object, method, resource->engine, callArgs);
1864     }
1865 }
1866
1867 MetaCallArgument::MetaCallArgument()
1868 : type(QVariant::Invalid)
1869 {
1870 }
1871
1872 MetaCallArgument::~MetaCallArgument()
1873 {
1874     cleanup();
1875 }
1876
1877 void MetaCallArgument::cleanup()
1878 {
1879     if (type == QMetaType::QString) {
1880         qstringPtr->~QString();
1881     } else if (type == -1 || type == QMetaType::QVariant) {
1882         qvariantPtr->~QVariant();
1883     } else if (type == qMetaTypeId<QJSValue>()) {
1884         qjsValuePtr->~QJSValue();
1885     } else if (type == qMetaTypeId<QList<QObject *> >()) {
1886         qlistPtr->~QList<QObject *>();
1887     } 
1888 }
1889
1890 void *MetaCallArgument::dataPtr()
1891 {
1892     if (type == -1)
1893         return qvariantPtr->data();
1894     else
1895         return (void *)&allocData;
1896 }
1897
1898 void MetaCallArgument::initAsType(int callType)
1899 {
1900     if (type != 0) { cleanup(); type = 0; }
1901     if (callType == 0) return;
1902
1903     if (callType == qMetaTypeId<QJSValue>()) {
1904         qjsValuePtr = new (&allocData) QJSValue();
1905         type = callType;
1906     } else if (callType == QMetaType::Int ||
1907                callType == QMetaType::UInt ||
1908                callType == QMetaType::Bool ||
1909                callType == QMetaType::Double ||
1910                callType == QMetaType::Float) {
1911         type = callType;
1912     } else if (callType == QMetaType::QObjectStar) {
1913         qobjectPtr = 0;
1914         type = callType;
1915     } else if (callType == QMetaType::QString) {
1916         qstringPtr = new (&allocData) QString();
1917         type = callType;
1918     } else if (callType == QMetaType::QVariant) {
1919         type = callType;
1920         qvariantPtr = new (&allocData) QVariant();
1921     } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1922         type = callType;
1923         qlistPtr = new (&allocData) QList<QObject *>();
1924     } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1925         type = callType;
1926         handlePtr = new (&allocData) QDeclarativeV8Handle;
1927     } else {
1928         type = -1;
1929         qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
1930     }
1931 }
1932
1933 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1934 {
1935     if (type != 0) { cleanup(); type = 0; }
1936
1937     if (callType == qMetaTypeId<QJSValue>()) {
1938         qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
1939         type = qMetaTypeId<QJSValue>();
1940     } else if (callType == QMetaType::Int) {
1941         intValue = quint32(value->Int32Value());
1942         type = callType;
1943     } else if (callType == QMetaType::UInt) {
1944         intValue = quint32(value->Uint32Value());
1945         type = callType;
1946     } else if (callType == QMetaType::Bool) {
1947         boolValue = value->BooleanValue();
1948         type = callType;
1949     } else if (callType == QMetaType::Double) {
1950         doubleValue = double(value->NumberValue());
1951         type = callType;
1952     } else if (callType == QMetaType::Float) {
1953         floatValue = float(value->NumberValue());
1954         type = callType;
1955     } else if (callType == QMetaType::QString) {
1956         if (value->IsNull() || value->IsUndefined())
1957             qstringPtr = new (&allocData) QString();
1958         else
1959             qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
1960         type = callType;
1961     } else if (callType == QMetaType::QObjectStar) {
1962         qobjectPtr = engine->toQObject(value);
1963         type = callType;
1964     } else if (callType == qMetaTypeId<QVariant>()) {
1965         qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
1966         type = callType;
1967     } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1968         qlistPtr = new (&allocData) QList<QObject *>();
1969         if (value->IsArray()) {
1970             v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1971             uint32_t length = array->Length();
1972             for (uint32_t ii = 0; ii < length; ++ii) 
1973                 qlistPtr->append(engine->toQObject(array->Get(ii)));
1974         } else {
1975             qlistPtr->append(engine->toQObject(value));
1976         }
1977         type = callType;
1978     } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1979         handlePtr = new (&allocData) QDeclarativeV8Handle(QDeclarativeV8Handle::fromHandle(value));
1980         type = callType;
1981     } else {
1982         qvariantPtr = new (&allocData) QVariant();
1983         type = -1;
1984
1985         QDeclarativeEnginePrivate *ep = engine->engine() ? QDeclarativeEnginePrivate::get(engine->engine()) : 0;
1986         QVariant v = engine->toVariant(value, -1);
1987
1988         if (v.userType() == callType) {
1989             *qvariantPtr = v;
1990         } else if (v.canConvert((QVariant::Type)callType)) {
1991             *qvariantPtr = v;
1992             qvariantPtr->convert((QVariant::Type)callType);
1993         } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
1994             QObject *obj = ep->toQObject(v);
1995             
1996             if (obj) {
1997                 const QMetaObject *objMo = obj->metaObject();
1998                 while (objMo && objMo != mo) objMo = objMo->superClass();
1999                 if (!objMo) obj = 0;
2000             }
2001
2002             *qvariantPtr = QVariant(callType, &obj);
2003         } else {
2004             *qvariantPtr = QVariant(callType, (void *)0);
2005         }
2006     }
2007 }
2008
2009 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
2010 {
2011     if (type == qMetaTypeId<QJSValue>()) {
2012         return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2013     } else if (type == QMetaType::Int) {
2014         return v8::Integer::New(int(intValue));
2015     } else if (type == QMetaType::UInt) {
2016         return v8::Integer::NewFromUnsigned(intValue);
2017     } else if (type == QMetaType::Bool) {
2018         return v8::Boolean::New(boolValue);
2019     } else if (type == QMetaType::Double) {
2020         return v8::Number::New(doubleValue);
2021     } else if (type == QMetaType::Float) {
2022         return v8::Number::New(floatValue);
2023     } else if (type == QMetaType::QString) {
2024         return engine->toString(*qstringPtr);
2025     } else if (type == QMetaType::QObjectStar) {
2026         QObject *object = qobjectPtr;
2027         if (object)
2028             QDeclarativeData::get(object, true)->setImplicitDestructible();
2029         return engine->newQObject(object);
2030     } else if (type == qMetaTypeId<QList<QObject *> >()) {
2031         // XXX Can this be made more by using Array as a prototype and implementing
2032         // directly against QList<QObject*>?
2033         QList<QObject *> &list = *qlistPtr;
2034         v8::Local<v8::Array> array = v8::Array::New(list.count());
2035         for (int ii = 0; ii < list.count(); ++ii) 
2036             array->Set(ii, engine->newQObject(list.at(ii)));
2037         return array;
2038     } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
2039         return handlePtr->toHandle();
2040     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2041         QVariant value = *qvariantPtr;
2042         v8::Handle<v8::Value> rv = engine->fromVariant(value);
2043         if (QObject *object = engine->toQObject(rv)) 
2044             QDeclarativeData::get(object, true)->setImplicitDestructible();
2045         return rv;
2046     } else {
2047         return v8::Undefined();
2048     }
2049 }
2050
2051 QT_END_NAMESPACE
2052