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