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