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