Allow function assignment to cause binding for non-var props
[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 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1677 {
1678     QByteArray sig = m.signature();
1679     int paren = sig.indexOf('(');
1680     if (paren == -1)
1681         return sig;
1682     else
1683         return sig.left(paren);
1684 }
1685
1686 /*!
1687 Returns the next related method, if one, or 0.
1688 */
1689 static const QQmlPropertyData * RelatedMethod(QObject *object,
1690                                                       const QQmlPropertyData *current,
1691                                                       QQmlPropertyData &dummy)
1692 {
1693     QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1694     if (!current->isOverload())
1695         return 0;
1696
1697     Q_ASSERT(!current->overrideIndexIsProperty);
1698
1699     if (cache) {
1700         return cache->method(current->overrideIndex);
1701     } else {
1702         const QMetaObject *mo = object->metaObject();
1703         int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1704
1705         while (methodOffset > current->overrideIndex) {
1706             mo = mo->superClass();
1707             methodOffset -= QMetaObject_methods(mo);
1708         }
1709
1710         QMetaMethod method = mo->method(current->overrideIndex);
1711         dummy.load(method);
1712         
1713         // Look for overloaded methods
1714         QByteArray methodName = QMetaMethod_name(method);
1715         for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1716             if (methodName == QMetaMethod_name(mo->method(ii))) {
1717                 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1718                 dummy.overrideIndexIsProperty = 0;
1719                 dummy.overrideIndex = ii;
1720                 return &dummy;
1721             }
1722         }
1723
1724         return &dummy;
1725     }
1726 }
1727
1728 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1729                                          QV8Engine *engine, CallArgs &callArgs)
1730 {
1731     if (data.hasArguments()) {
1732
1733         int *args = 0;
1734         QVarLengthArray<int, 9> dummy;
1735         QByteArray unknownTypeError;
1736
1737         args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy, 
1738                                                                &unknownTypeError);
1739
1740         if (!args) {
1741             QString typeName = QString::fromLatin1(unknownTypeError);
1742             QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1743             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1744             return v8::Handle<v8::Value>();
1745         }
1746
1747         if (args[0] > callArgs.Length()) {
1748             QString error = QLatin1String("Insufficient arguments");
1749             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1750             return v8::Handle<v8::Value>();
1751         }
1752
1753         return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1754
1755     } else {
1756
1757         return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1758
1759     }
1760 }
1761
1762 /*!
1763 Resolve the overloaded method to call.  The algorithm works conceptually like this:
1764     1.  Resolve the set of overloads it is *possible* to call.
1765         Impossible overloads include those that have too many parameters or have parameters 
1766         of unknown type.  
1767     2.  Filter the set of overloads to only contain those with the closest number of 
1768         parameters.
1769         For example, if we are called with 3 parameters and there are 2 overloads that
1770         take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1771     3.  Find the best remaining overload based on its match score.  
1772         If two or more overloads have the same match score, call the last one.  The match
1773         score is constructed by adding the matchScore() result for each of the parameters.
1774 */
1775 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1776                                             QV8Engine *engine, CallArgs &callArgs)
1777 {
1778     int argumentCount = callArgs.Length();
1779
1780     const QQmlPropertyData *best = 0;
1781     int bestParameterScore = INT_MAX;
1782     int bestMatchScore = INT_MAX;
1783
1784     QQmlPropertyData dummy;
1785     const QQmlPropertyData *attempt = &data;
1786
1787     do {
1788         QVarLengthArray<int, 9> dummy;
1789         int methodArgumentCount = 0;
1790         int *methodArgTypes = 0;
1791         if (attempt->hasArguments()) {
1792             typedef QQmlPropertyCache PC;
1793             int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1794             if (!args) // Must be an unknown argument
1795                 continue;
1796
1797             methodArgumentCount = args[0];
1798             methodArgTypes = args + 1;
1799         }
1800
1801         if (methodArgumentCount > argumentCount)
1802             continue; // We don't have sufficient arguments to call this method
1803
1804         int methodParameterScore = argumentCount - methodArgumentCount;
1805         if (methodParameterScore > bestParameterScore)
1806             continue; // We already have a better option
1807
1808         int methodMatchScore = 0;
1809         for (int ii = 0; ii < methodArgumentCount; ++ii) 
1810             methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1811
1812         if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1813             best = attempt;
1814             bestParameterScore = methodParameterScore;
1815             bestMatchScore = methodMatchScore;
1816         }
1817
1818         if (bestParameterScore == 0 && bestMatchScore == 0)
1819             break; // We can't get better than that
1820
1821     } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1822
1823     if (best) {
1824         return CallPrecise(object, *best, engine, callArgs);
1825     } else {
1826         QString error = QLatin1String("Unable to determine callable overload.  Candidates are:");
1827         const QQmlPropertyData *candidate = &data;
1828         while (candidate) {
1829             error += QLatin1String("\n    ") + 
1830                      QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1831             candidate = RelatedMethod(object, candidate, dummy);
1832         }
1833
1834         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1835         return v8::Handle<v8::Value>();
1836     }
1837 }
1838
1839 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1840 {
1841     QString result;
1842     if (object) {
1843         QString objectName = object->objectName();
1844
1845         result += QString::fromUtf8(object->metaObject()->className());
1846         result += QLatin1String("(0x");
1847         result += QString::number((quintptr)object,16);
1848
1849         if (!objectName.isEmpty()) {
1850             result += QLatin1String(", \"");
1851             result += objectName;
1852             result += QLatin1Char('\"');
1853         }
1854
1855         result += QLatin1Char(')');
1856     } else {
1857         result = QLatin1String("null");
1858     }
1859
1860     return engine->toString(result);
1861 }
1862
1863 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1864 {
1865     QQmlData *ddata = QQmlData::get(object, false);
1866     if (!ddata || ddata->indestructible) {
1867         const char *error = "Invalid attempt to destroy() an indestructible object";
1868         v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1869         return v8::Undefined();
1870     }
1871
1872     int delay = 0;
1873     if (argCount > 0)
1874         delay = args->Get(0)->Uint32Value();
1875
1876     if (delay > 0)
1877         QTimer::singleShot(delay, object, SLOT(deleteLater()));
1878     else
1879         object->deleteLater();
1880
1881     return v8::Undefined();
1882 }
1883
1884 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1885 {
1886     // object, index, qmlglobal, argCount, args
1887     Q_ASSERT(args.Length() == 5);
1888     Q_ASSERT(args[0]->IsObject());
1889
1890     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1891
1892     if (!resource)
1893         return v8::Undefined();
1894
1895     int argCount = args[3]->Int32Value();
1896     v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1897
1898     // Special hack to return info about this closure.
1899     if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1900         v8::Local<v8::Array> data = v8::Array::New(2);
1901         data->Set(0, args[0]);
1902         data->Set(1, args[1]);
1903         return data;
1904     }
1905
1906     QObject *object = resource->object;
1907     int index = args[1]->Int32Value();
1908
1909     if (!object)
1910         return v8::Undefined();
1911
1912     if (index < 0) {
1913         // Builtin functions
1914         if (index == QOBJECT_TOSTRING_INDEX) {
1915             return ToString(resource->engine, object, argCount, arguments);
1916         } else if (index == QOBJECT_DESTROY_INDEX) {
1917             return Destroy(resource->engine, object, argCount, arguments);
1918         } else {
1919             return v8::Undefined();
1920         }
1921     }
1922
1923     QQmlPropertyData method;
1924
1925     if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
1926         if (ddata->propertyCache) {
1927             QQmlPropertyData *d = ddata->propertyCache->method(index);
1928             if (!d) 
1929                 return v8::Undefined();
1930             method = *d;
1931         } 
1932     }
1933
1934     if (method.coreIndex == -1) {
1935         method.load(object->metaObject()->method(index));
1936
1937         if (method.coreIndex == -1)
1938             return v8::Undefined();
1939     }
1940
1941     if (method.isV8Function()) {
1942         v8::Handle<v8::Value> rv;
1943         v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1944
1945         QQmlV8Function func(argCount, arguments, rv, qmlglobal, 
1946                                     resource->engine->contextWrapper()->context(qmlglobal),
1947                                     resource->engine);
1948         QQmlV8Function *funcptr = &func;
1949
1950         void *args[] = { 0, &funcptr };
1951         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1952
1953         if (rv.IsEmpty()) return v8::Undefined();
1954         return rv;
1955     }
1956
1957     CallArgs callArgs(argCount, &arguments);
1958     if (!method.isOverload()) {
1959         return CallPrecise(object, method, resource->engine, callArgs);
1960     } else {
1961         return CallOverloaded(object, method, resource->engine, callArgs);
1962     }
1963 }
1964
1965 CallArgument::CallArgument()
1966 : type(QVariant::Invalid)
1967 {
1968 }
1969
1970 CallArgument::~CallArgument()
1971 {
1972     cleanup();
1973 }
1974
1975 void CallArgument::cleanup()
1976 {
1977     if (type == QMetaType::QString) {
1978         qstringPtr->~QString();
1979     } else if (type == -1 || type == QMetaType::QVariant) {
1980         qvariantPtr->~QVariant();
1981     } else if (type == qMetaTypeId<QJSValue>()) {
1982         qjsValuePtr->~QJSValue();
1983     } else if (type == qMetaTypeId<QList<QObject *> >()) {
1984         qlistPtr->~QList<QObject *>();
1985     } 
1986 }
1987
1988 void *CallArgument::dataPtr()
1989 {
1990     if (type == -1)
1991         return qvariantPtr->data();
1992     else
1993         return (void *)&allocData;
1994 }
1995
1996 void CallArgument::initAsType(int callType)
1997 {
1998     if (type != 0) { cleanup(); type = 0; }
1999     if (callType == 0) return;
2000
2001     if (callType == qMetaTypeId<QJSValue>()) {
2002         qjsValuePtr = new (&allocData) QJSValue();
2003         type = callType;
2004     } else if (callType == QMetaType::Int ||
2005                callType == QMetaType::UInt ||
2006                callType == QMetaType::Bool ||
2007                callType == QMetaType::Double ||
2008                callType == QMetaType::Float) {
2009         type = callType;
2010     } else if (callType == QMetaType::QObjectStar) {
2011         qobjectPtr = 0;
2012         type = callType;
2013     } else if (callType == QMetaType::QString) {
2014         qstringPtr = new (&allocData) QString();
2015         type = callType;
2016     } else if (callType == QMetaType::QVariant) {
2017         type = callType;
2018         qvariantPtr = new (&allocData) QVariant();
2019     } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2020         type = callType;
2021         qlistPtr = new (&allocData) QList<QObject *>();
2022     } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2023         type = callType;
2024         handlePtr = new (&allocData) QQmlV8Handle;
2025     } else {
2026         type = -1;
2027         qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2028     }
2029 }
2030
2031 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2032 {
2033     if (type != 0) { cleanup(); type = 0; }
2034
2035     if (callType == qMetaTypeId<QJSValue>()) {
2036         qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2037         type = qMetaTypeId<QJSValue>();
2038     } else if (callType == QMetaType::Int) {
2039         intValue = quint32(value->Int32Value());
2040         type = callType;
2041     } else if (callType == QMetaType::UInt) {
2042         intValue = quint32(value->Uint32Value());
2043         type = callType;
2044     } else if (callType == QMetaType::Bool) {
2045         boolValue = value->BooleanValue();
2046         type = callType;
2047     } else if (callType == QMetaType::Double) {
2048         doubleValue = double(value->NumberValue());
2049         type = callType;
2050     } else if (callType == QMetaType::Float) {
2051         floatValue = float(value->NumberValue());
2052         type = callType;
2053     } else if (callType == QMetaType::QString) {
2054         if (value->IsNull() || value->IsUndefined())
2055             qstringPtr = new (&allocData) QString();
2056         else
2057             qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2058         type = callType;
2059     } else if (callType == QMetaType::QObjectStar) {
2060         qobjectPtr = engine->toQObject(value);
2061         type = callType;
2062     } else if (callType == qMetaTypeId<QVariant>()) {
2063         qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2064         type = callType;
2065     } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2066         qlistPtr = new (&allocData) QList<QObject *>();
2067         if (value->IsArray()) {
2068             v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2069             uint32_t length = array->Length();
2070             for (uint32_t ii = 0; ii < length; ++ii) 
2071                 qlistPtr->append(engine->toQObject(array->Get(ii)));
2072         } else {
2073             qlistPtr->append(engine->toQObject(value));
2074         }
2075         type = callType;
2076     } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2077         handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2078         type = callType;
2079     } else {
2080         qvariantPtr = new (&allocData) QVariant();
2081         type = -1;
2082
2083         QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2084         QVariant v = engine->toVariant(value, -1);
2085
2086         if (v.userType() == callType) {
2087             *qvariantPtr = v;
2088         } else if (v.canConvert((QVariant::Type)callType)) {
2089             *qvariantPtr = v;
2090             qvariantPtr->convert((QVariant::Type)callType);
2091         } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
2092             QObject *obj = ep->toQObject(v);
2093             
2094             if (obj) {
2095                 const QMetaObject *objMo = obj->metaObject();
2096                 while (objMo && objMo != mo) objMo = objMo->superClass();
2097                 if (!objMo) obj = 0;
2098             }
2099
2100             *qvariantPtr = QVariant(callType, &obj);
2101         } else {
2102             *qvariantPtr = QVariant(callType, (void *)0);
2103         }
2104     }
2105 }
2106
2107 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2108 {
2109     if (type == qMetaTypeId<QJSValue>()) {
2110         return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2111     } else if (type == QMetaType::Int) {
2112         return v8::Integer::New(int(intValue));
2113     } else if (type == QMetaType::UInt) {
2114         return v8::Integer::NewFromUnsigned(intValue);
2115     } else if (type == QMetaType::Bool) {
2116         return v8::Boolean::New(boolValue);
2117     } else if (type == QMetaType::Double) {
2118         return v8::Number::New(doubleValue);
2119     } else if (type == QMetaType::Float) {
2120         return v8::Number::New(floatValue);
2121     } else if (type == QMetaType::QString) {
2122         return engine->toString(*qstringPtr);
2123     } else if (type == QMetaType::QObjectStar) {
2124         QObject *object = qobjectPtr;
2125         if (object)
2126             QQmlData::get(object, true)->setImplicitDestructible();
2127         return engine->newQObject(object);
2128     } else if (type == qMetaTypeId<QList<QObject *> >()) {
2129         // XXX Can this be made more by using Array as a prototype and implementing
2130         // directly against QList<QObject*>?
2131         QList<QObject *> &list = *qlistPtr;
2132         v8::Local<v8::Array> array = v8::Array::New(list.count());
2133         for (int ii = 0; ii < list.count(); ++ii) 
2134             array->Set(ii, engine->newQObject(list.at(ii)));
2135         return array;
2136     } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2137         return handlePtr->toHandle();
2138     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2139         QVariant value = *qvariantPtr;
2140         v8::Handle<v8::Value> rv = engine->fromVariant(value);
2141         if (QObject *object = engine->toQObject(rv)) 
2142             QQmlData::get(object, true)->setImplicitDestructible();
2143         return rv;
2144     } else {
2145         return v8::Undefined();
2146     }
2147 }
2148
2149 QT_END_NAMESPACE
2150