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