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