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