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