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