Change copyrights from Nokia to Digia
[profile/ivi/qtdeclarative.git] / src / qml / qml / v8 / qv8qobjectwrapper.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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     QQmlData::flushPendingBinding(object, result->coreIndex);
538
539     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
540         QQmlData *ddata = QQmlData::get(object);
541         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
542             return v8::Handle<v8::Value>();
543     }
544
545     if (result->isFunction() && !result->isVarProperty()) {
546         if (result->isVMEFunction()) {
547             QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
548             Q_ASSERT(vmemo);
549             return vmemo->vmeMethod(result->coreIndex);
550         } else if (result->isV8Function()) {
551             return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
552         } else if (result->isSignalHandler()) {
553             v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
554             QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
555             handler->SetExternalResource(r);
556             return handler;
557         } else {
558             return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
559         }
560     }
561
562     QQmlEnginePrivate *ep =
563         engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
564
565     if (result->hasAccessors()) {
566         QQmlNotifier *n = 0;
567         QQmlNotifier **nptr = 0;
568
569         if (ep && ep->propertyCapture && result->accessors->notifier)
570             nptr = &n;
571
572         v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
573
574         if (result->accessors->notifier) {
575             if (n) ep->captureProperty(n);
576         } else {
577             ep->captureProperty(object, result->coreIndex, result->notifyIndex);
578         }
579
580         return rv;
581     }
582
583     if (ep && !result->isConstant())
584         ep->captureProperty(object, result->coreIndex, result->notifyIndex);
585
586     if (result->isVarProperty()) {
587         QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
588         Q_ASSERT(vmemo);
589         return vmemo->vmeProperty(result->coreIndex);
590     } else if (result->isDirect())  {
591         return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
592     } else {
593         return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
594     }
595 }
596
597 // Setter for writable properties.  Shared between the interceptor and fast property accessor
598 static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
599                                  v8::Handle<v8::Value> value)
600 {
601     QQmlBinding *newBinding = 0;
602     if (value->IsFunction()) {
603         if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
604             if (!property->isVarProperty() && property->propType != qMetaTypeId<QJSValue>()) {
605                 // assigning a JS function to a non var or QJSValue property or is not allowed.
606                 QString error = QLatin1String("Cannot assign JavaScript function to ");
607                 if (!QMetaType::typeName(property->propType))
608                     error += QLatin1String("[unknown property type]");
609                 else
610                     error += QLatin1String(QMetaType::typeName(property->propType));
611                 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
612                 return;
613             }
614         } else {
615             // binding assignment.
616             QQmlContextData *context = engine->callingContext();
617             v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
618
619             v8::Local<v8::StackTrace> trace =
620                 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
621                                                                                          v8::StackTrace::kScriptName));
622             v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
623             int lineNumber = frame->GetLineNumber();
624             int columnNumber = frame->GetColumn();
625             QString url = engine->toString(frame->GetScriptName());
626
627             newBinding = new QQmlBinding(&function, object, context, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber));
628             newBinding->setTarget(object, *property, context);
629             newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
630                                          QQmlBinding::RequiresThisObject);
631         }
632     }
633
634     QQmlAbstractBinding *oldBinding = 
635         QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
636     if (oldBinding)
637         oldBinding->destroy();
638
639     if (!newBinding && property->isVarProperty()) {
640         // allow assignment of "special" values (null, undefined, function) to var properties
641         QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
642         Q_ASSERT(vmemo);
643         vmemo->setVMEProperty(property->coreIndex, value);
644         return;
645     }
646
647 #define PROPERTY_STORE(cpptype, value) \
648     cpptype o = value; \
649     int status = -1; \
650     int flags = 0; \
651     void *argv[] = { &o, 0, &status, &flags }; \
652     QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
653
654     if (value->IsNull() && property->isQObject()) {
655         PROPERTY_STORE(QObject*, 0);
656     } else if (value->IsUndefined() && property->isResettable()) {
657         void *a[] = { 0 };
658         QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
659     } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
660         PROPERTY_STORE(QVariant, QVariant());
661     } else if (value->IsUndefined() && property->propType == QMetaType::QJsonValue) {
662         PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
663     } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) {
664         PROPERTY_STORE(QJSValue, engine->scriptValueFromInternal(value));
665     } else if (value->IsUndefined()) {
666         QString error = QLatin1String("Cannot assign [undefined] to ");
667         if (!QMetaType::typeName(property->propType))
668             error += QLatin1String("[unknown property type]");
669         else
670             error += QLatin1String(QMetaType::typeName(property->propType));
671         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
672     } else if (value->IsFunction()) {
673         // this is handled by the binding creation above
674     } else if (property->propType == QMetaType::Int && value->IsNumber()) {
675         PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
676     } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
677         PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
678     } else if (property->propType == QMetaType::Float && value->IsNumber()) {
679         PROPERTY_STORE(float, float(value->ToNumber()->Value()));
680     } else if (property->propType == QMetaType::Double && value->IsNumber()) {
681         PROPERTY_STORE(double, double(value->ToNumber()->Value()));
682     } else if (property->propType == QMetaType::QString && value->IsString()) {
683         PROPERTY_STORE(QString, engine->toString(value->ToString()));
684     } else if (property->isVarProperty()) {
685         QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
686         Q_ASSERT(vmemo);
687         vmemo->setVMEProperty(property->coreIndex, value);
688     } else {
689         QVariant v;
690         if (property->isQList()) 
691             v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
692         else
693             v = engine->toVariant(value, property->propType);
694
695         QQmlContextData *context = engine->callingContext();
696         if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
697             const char *valueType = 0;
698             if (v.userType() == QVariant::Invalid) valueType = "null";
699             else valueType = QMetaType::typeName(v.userType());
700
701             const char *targetTypeName = QMetaType::typeName(property->propType);
702             if (!targetTypeName)
703                 targetTypeName = "an unregistered type";
704
705             QString error = QLatin1String("Cannot assign ") +
706                             QLatin1String(valueType) +
707                             QLatin1String(" to ") +
708                             QLatin1String(targetTypeName);
709             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
710         }
711     }
712 }
713
714 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property, QQmlContextData *context,
715                                     v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
716 {
717     if (engine->qobjectWrapper()->m_toStringString == property ||
718         engine->qobjectWrapper()->m_destroyString == property)
719         return true;
720
721     if (QQmlData::wasDeleted(object))
722         return false;
723
724     QQmlPropertyData local;
725     QQmlPropertyData *result = 0;
726     result = QQmlPropertyCache::property(engine->engine(), object, property, context, local);
727
728     if (!result)
729         return false;
730
731     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
732         QQmlData *ddata = QQmlData::get(object);
733         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
734             return false;
735     }
736
737     if (!result->isWritable() && !result->isQList()) {
738         QString error = QLatin1String("Cannot assign to read-only property \"") +
739                         engine->toString(property.string()) + QLatin1Char('\"');
740         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
741         return true;
742     }
743
744     StoreProperty(engine, object, result, value);
745
746     return true;
747 }
748
749 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property, 
750                                                 const v8::AccessorInfo &info)
751 {
752     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
753
754     if (QQmlData::wasDeleted(resource->object))
755         return v8::Handle<v8::Value>();
756
757     QObject *object = resource->object;
758
759     QHashedV8String propertystring(property);
760
761     QV8Engine *v8engine = resource->engine;
762     QQmlContextData *context = v8engine->callingContext();
763
764     v8::Handle<v8::Value> This = info.This();
765     v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring, 
766                                                context, QV8QObjectWrapper::IgnoreRevision);
767     if (!result.IsEmpty())
768         return result;
769
770     if (QV8Engine::startsWithUpper(property)) {
771         // Check for attached properties
772         if (context && context->imports) {
773             QQmlTypeNameCache::Result r = context->imports->query(propertystring);
774
775             if (r.isValid()) {
776                 if (r.scriptIndex != -1) {
777                     return v8::Undefined();
778                 } else if (r.type) {
779                     return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
780                 } else if (r.importNamespace) {
781                     return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace, 
782                                                               QV8TypeWrapper::ExcludeEnums);
783                 }
784                 Q_ASSERT(!"Unreachable");
785             }
786         }
787     } 
788
789     return v8::Handle<v8::Value>();
790 }
791
792 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property, 
793                                                 v8::Local<v8::Value> value,
794                                                 const v8::AccessorInfo &info)
795 {
796     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
797
798     if (QQmlData::wasDeleted(resource->object))
799         return value;
800
801     QObject *object = resource->object;
802
803     QHashedV8String propertystring(property);
804
805     QV8Engine *v8engine = resource->engine;
806     QQmlContextData *context = v8engine->callingContext();
807     bool result = SetProperty(v8engine, object, propertystring, context, value, QV8QObjectWrapper::IgnoreRevision);
808
809     if (!result) {
810         QString error = QLatin1String("Cannot assign to non-existent property \"") +
811                         v8engine->toString(property) + QLatin1Char('\"');
812         v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
813         return value;
814     }
815
816     return value;
817 }
818
819 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
820                                                  const v8::AccessorInfo &info)
821 {
822     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
823
824     if (resource->object.isNull()) 
825         return v8::Handle<v8::Integer>();
826
827     QV8Engine *engine = resource->engine;
828     QObject *object = resource->object;
829     QQmlContextData *context = engine->callingContext();
830
831     QHashedV8String propertystring(property);
832
833     QQmlPropertyData local;
834     QQmlPropertyData *result = 0;
835     result = QQmlPropertyCache::property(engine->engine(), object, propertystring, context, local);
836
837     if (!result)
838         return v8::Handle<v8::Integer>();
839     else if (!result->isWritable() && !result->isQList())
840         return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
841     else
842         return v8::Integer::New(v8::DontDelete);
843 }
844
845 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
846 {
847     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
848
849     if (resource->object.isNull()) 
850         return v8::Array::New();
851
852     QObject *object = resource->object;
853
854     QStringList result;
855
856     QQmlEnginePrivate *ep = resource->engine->engine()
857             ? QQmlEnginePrivate::get(resource->engine->engine())
858             : 0;
859
860     QQmlPropertyCache *cache = 0;
861     QQmlData *ddata = QQmlData::get(object);
862     if (ddata)
863         cache = ddata->propertyCache;
864
865     if (!cache) {
866         cache = ep ? ep->cache(object) : 0;
867         if (cache) {
868             if (ddata) { cache->addref(); ddata->propertyCache = cache; }
869         } else {
870             // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
871             const QMetaObject *mo = object->metaObject();
872             int pc = mo->propertyCount();
873             int po = mo->propertyOffset();
874             for (int i=po; i<pc; ++i)
875                 result << QString::fromUtf8(mo->property(i).name());
876         }
877     } else {
878         result = cache->propertyNames();
879     }
880
881     v8::Local<v8::Array> rv = v8::Array::New(result.count());
882
883     for (int ii = 0; ii < result.count(); ++ii) 
884         rv->Set(ii, resource->engine->toString(result.at(ii)));
885
886     return rv;
887 }
888
889 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
890                             const v8::AccessorInfo& info)
891 {
892     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
893
894     if (QQmlData::wasDeleted(resource->object))
895         return; 
896
897     QObject *object = resource->object;
898
899     QQmlPropertyData *property =
900         (QQmlPropertyData *)v8::External::Unwrap(info.Data());
901
902     int index = property->coreIndex;
903
904     QQmlData *ddata = QQmlData::get(object, false);
905     Q_ASSERT(ddata);
906     Q_ASSERT(ddata->propertyCache);
907
908     QQmlPropertyData *pdata = ddata->propertyCache->property(index);
909     Q_ASSERT(pdata);
910
911     Q_ASSERT(pdata->isWritable() || pdata->isQList());
912
913     StoreProperty(resource->engine, object, pdata, value);
914 }
915
916 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
917                                     const v8::AccessorInfo& info)
918 {
919     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
920
921     if (QQmlData::wasDeleted(resource->object))
922         return; 
923
924     QV8Engine *v8engine = resource->engine;
925
926     QString error = QLatin1String("Cannot assign to read-only property \"") +
927                     v8engine->toString(property) + QLatin1Char('\"');
928     v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
929 }
930
931 void QV8QObjectWrapper::WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *wrapper)
932 {
933     Q_ASSERT(handle->IsObject());
934     v8::Handle<v8::Object> v8object = handle->ToObject();
935     QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(v8object);
936     Q_ASSERT(resource);
937
938     static_cast<QV8QObjectWrapper*>(wrapper)->unregisterWeakQObjectReference(resource);
939     if (static_cast<QV8QObjectWrapper*>(wrapper)->deleteWeakQObject(resource, false)) {
940         // dispose
941         v8object->SetExternalResource(0);
942         delete resource;
943         qPersistentDispose(handle);
944     } else {
945         handle.MakeWeak(0, WeakQObjectReferenceCallback); // revive.
946     }
947 }
948
949 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
950 {
951     QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
952     instance->v8object.Clear();
953     qPersistentDispose(handle);
954 }
955
956 v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
957 {
958     Q_ASSERT(object);
959     Q_ASSERT(this->engine);
960
961     Q_ASSERT(QQmlData::get(object, false));
962     Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
963
964     // Setup constructor
965     if (constructor.IsEmpty()) {
966         v8::Local<v8::FunctionTemplate> ft;
967
968         const QHashedString toString(QStringLiteral("toString"));
969         const QHashedString destroy(QStringLiteral("destroy"));
970
971         // As we use hash linking, or with property overrides, it is possible that iterating
972         // over the values can yield duplicates.  To combat this, we must unique'ify our properties.
973         const bool checkForDuplicates = stringCache.isLinked() || _hasPropertyOverrides;
974
975         StringCache uniqueHash;
976         if (checkForDuplicates)
977             uniqueHash.reserve(stringCache.count());
978
979         // XXX TODO: Enables fast property accessors.  These more than double the property access 
980         // performance, but the  cost of setting up this structure hasn't been measured so 
981         // its not guaranteed that this is a win overall.  We need to try and measure the cost.
982         for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
983             if (iter.equals(toString) || iter.equals(destroy))
984                 continue;
985
986             if (checkForDuplicates) {
987                 if (uniqueHash.contains(iter))
988                     continue;
989                 uniqueHash.insert(iter);
990             }
991
992             QQmlPropertyData *property = (*iter).second;
993             if (property->notFullyResolved()) resolve(property);
994
995             if (property->isFunction())
996                 continue;
997
998             v8::AccessorGetter fastgetter = 0;
999
1000             if (property->isQObject()) 
1001                 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
1002             else if (property->propType == QMetaType::Int || property->isEnum()) 
1003                 fastgetter = FAST_GETTER_FUNCTION(property, int);
1004             else if (property->propType == QMetaType::Bool)
1005                 fastgetter = FAST_GETTER_FUNCTION(property, bool);
1006             else if (property->propType == QMetaType::QString)
1007                 fastgetter = FAST_GETTER_FUNCTION(property, QString);
1008             else if (property->propType == QMetaType::UInt)
1009                 fastgetter = FAST_GETTER_FUNCTION(property, uint);
1010             else if (property->propType == QMetaType::Float) 
1011                 fastgetter = FAST_GETTER_FUNCTION(property, float);
1012             else if (property->propType == QMetaType::Double) 
1013                 fastgetter = FAST_GETTER_FUNCTION(property, double);
1014
1015             if (fastgetter) {
1016                 if (ft.IsEmpty()) {
1017                     ft = v8::FunctionTemplate::New();
1018                     ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
1019                                                                        QV8QObjectWrapper::Setter,
1020                                                                        QV8QObjectWrapper::Query, 
1021                                                                        0,
1022                                                                        QV8QObjectWrapper::Enumerator);
1023                     ft->InstanceTemplate()->SetHasExternalResource(true);
1024                 }
1025
1026                 v8::AccessorSetter fastsetter = FastValueSetter;
1027                 if (!property->isWritable())
1028                     fastsetter = FastValueSetterReadOnly;
1029
1030                 // We wrap the raw QQmlPropertyData pointer here.  This is safe as the
1031                 // pointer will remain valid at least as long as the lifetime of any QObject's of
1032                 // this type and the property accessor checks if the object is 0 (deleted) before
1033                 // dereferencing the pointer.
1034                 ft->InstanceTemplate()->SetAccessor(engine->toString(iter.key()), fastgetter, fastsetter,
1035                                                     v8::External::Wrap(property));
1036             }
1037         }
1038
1039         if (ft.IsEmpty()) {
1040             constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
1041         } else {
1042             ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
1043                                                                QV8QObjectWrapper::Setter,
1044                                                                QV8QObjectWrapper::Query, 
1045                                                                0,
1046                                                                QV8QObjectWrapper::Enumerator);
1047             ft->InstanceTemplate()->SetHasExternalResource(true);
1048             constructor = qPersistentNew<v8::Function>(ft->GetFunction());
1049         }
1050
1051         QQmlCleanup::addToEngine(this->engine);
1052     }
1053
1054     v8::Local<v8::Object> result = constructor->NewInstance();
1055     QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1056     result->SetExternalResource(r);
1057     return result;
1058 }
1059
1060 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
1061 {
1062     v8::Local<v8::Object> rv;
1063
1064     if (!ddata->propertyCache && engine->engine()) {
1065         ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
1066         if (ddata->propertyCache) ddata->propertyCache->addref();
1067     }
1068
1069     if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1070         rv = ddata->propertyCache->newQObject(object, engine);
1071     } else {
1072         // XXX NewInstance() should be optimized
1073         rv = m_constructor->NewInstance(); 
1074         QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1075         rv->SetExternalResource(r);
1076     }
1077
1078     return rv;
1079 }
1080
1081 /*
1082 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1083 V8 handle for subsequent calls to newQObject for the same QObject.  To do this we have a two
1084 pronged strategy:
1085    1. If there is no current outstanding V8 handle to the QObject, we create one and store a 
1086       persistent handle in QQmlData::v8object.  We mark the QV8QObjectWrapper that 
1087       "owns" this handle by setting the QQmlData::v8objectid to the id of this 
1088       QV8QObjectWrapper.
1089    2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create 
1090       an entry in the m_taintedObject hash where we store the handle and mark the object as 
1091       "tainted" in the QQmlData::hasTaintedV8Object flag.
1092 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1093 in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has 
1094 released the handle.
1095 */
1096 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1097 {
1098     if (QQmlData::wasDeleted(object))
1099         return v8::Null();
1100
1101     QQmlData *ddata = QQmlData::get(object, true);
1102     if (!ddata) 
1103         return v8::Undefined();
1104
1105     if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1106         // We own the v8object 
1107         return v8::Local<v8::Object>::New(ddata->v8object);
1108     } else if (ddata->v8object.IsEmpty() && 
1109                (ddata->v8objectid == m_id || // We own the QObject
1110                 ddata->v8objectid == 0 ||    // No one owns the QObject
1111                 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1112
1113         v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1114         ddata->v8object = qPersistentNew<v8::Object>(rv);
1115         ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1116         ddata->v8objectid = m_id;
1117         QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1118         registerWeakQObjectReference(resource);
1119         return rv;
1120
1121     } else {
1122         // If this object is tainted, we have to check to see if it is in our
1123         // tainted object list
1124         TaintedHash::Iterator iter =
1125             ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1126         bool found = iter != m_taintedObjects.end();
1127
1128         // If our tainted handle doesn't exist or has been collected, and there isn't
1129         // a handle in the ddata, we can assume ownership of the ddata->v8object
1130         if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1131             v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1132             ddata->v8object = qPersistentNew<v8::Object>(rv);
1133             ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1134             ddata->v8objectid = m_id;
1135             QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1136             registerWeakQObjectReference(resource);
1137
1138             if (found) {
1139                 delete (*iter);
1140                 m_taintedObjects.erase(iter);
1141             }
1142
1143             return rv;
1144         } else if (!found) {
1145             QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1146             iter = m_taintedObjects.insert(object, instance);
1147             ddata->hasTaintedV8Object = true;
1148         }
1149
1150         if ((*iter)->v8object.IsEmpty()) {
1151             v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1152             (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1153             (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1154         }
1155
1156         return v8::Local<v8::Object>::New((*iter)->v8object);
1157     }
1158 }
1159
1160 // returns true if the object's qqmldata v8object handle should
1161 // be disposed by the caller, false if it should not be (due to
1162 // creation status, etc).
1163 bool QV8QObjectWrapper::deleteWeakQObject(QV8QObjectResource *resource, bool calledFromEngineDtor)
1164 {
1165     QObject *object = resource->object;
1166     if (object) {
1167         QQmlData *ddata = QQmlData::get(object, false);
1168         if (ddata) {
1169             if (!calledFromEngineDtor && ddata->rootObjectInCreation) {
1170                 // if weak ref callback is triggered (by gc) for a root object
1171                 // prior to completion of creation, we should NOT delete it.
1172                 return false;
1173             }
1174
1175             ddata->v8object.Clear();
1176             if (!object->parent() && !ddata->indestructible) {
1177                 // This object is notionally destroyed now
1178                 if (ddata->ownContext && ddata->context)
1179                     ddata->context->emitDestruction();
1180                 ddata->isQueuedForDeletion = true;
1181                 if (calledFromEngineDtor)
1182                     delete object;
1183                 else
1184                     object->deleteLater();
1185             }
1186         }
1187     }
1188
1189     return true;
1190 }
1191
1192 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1193 {
1194     if (object->IsFunction())
1195         return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1196
1197     if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1198         return qMakePair(resource->object.data(), resource->index);
1199
1200     return qMakePair((QObject *)0, -1);
1201 }
1202
1203 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1204 {
1205     v8::ScriptOrigin origin = function->GetScriptOrigin();
1206     if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1207
1208         // This is one of our special QObject method wrappers
1209         v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1210         v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1211
1212         if (data->IsArray()) {
1213             v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1214             return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1215         } 
1216
1217         // In theory this can't fall through, but I suppose V8 might run out of memory or something
1218     }
1219
1220     return qMakePair((QObject *)0, -1);
1221 }
1222
1223 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1224 {
1225 public:
1226     QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1227     ~QV8QObjectConnectionList();
1228
1229     struct Connection {
1230         Connection() 
1231         : needsDestroy(false) {}
1232         Connection(const Connection &other) 
1233         : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1234         Connection &operator=(const Connection &other) {
1235             thisObject = other.thisObject;
1236             function = other.function;
1237             needsDestroy = other.needsDestroy;
1238             return *this;
1239         }
1240
1241         v8::Persistent<v8::Object> thisObject;
1242         v8::Persistent<v8::Function> function;
1243
1244         void dispose() {
1245             qPersistentDispose(thisObject);
1246             qPersistentDispose(function);
1247         }
1248
1249         bool needsDestroy;
1250     };
1251
1252     struct ConnectionList : public QList<Connection> {
1253         ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1254         int connectionsInUse;
1255         bool connectionsNeedClean;
1256     };
1257
1258     QV8Engine *engine;
1259
1260     typedef QHash<int, ConnectionList> SlotHash;
1261     SlotHash slotHash;
1262     bool needsDestroy;
1263     int inUse;
1264
1265     virtual void objectDestroyed(QObject *);
1266     virtual int qt_metacall(QMetaObject::Call, int, void **);
1267 };
1268
1269 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1270 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1271 {
1272 }
1273
1274 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1275 {
1276     for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1277         QList<Connection> &connections = *iter;
1278         for (int ii = 0; ii < connections.count(); ++ii) {
1279             qPersistentDispose(connections[ii].thisObject);
1280             qPersistentDispose(connections[ii].function);
1281         }
1282     }
1283     slotHash.clear();
1284 }
1285
1286 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1287 {
1288     engine->qobjectWrapper()->m_connections.remove(object);
1289
1290     if (inUse)
1291         needsDestroy = true;
1292     else
1293         delete this;
1294 }
1295
1296 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1297 {
1298     if (method == QMetaObject::InvokeMetaMethod) {
1299         SlotHash::Iterator iter = slotHash.find(index);
1300         if (iter == slotHash.end())
1301             return -1;
1302         ConnectionList &connectionList = *iter;
1303         if (connectionList.isEmpty())
1304             return -1;
1305
1306         inUse++;
1307
1308         connectionList.connectionsInUse++;
1309
1310         QList<Connection> connections = connectionList;
1311
1312         QVarLengthArray<int, 9> dummy;
1313         int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1314
1315         v8::HandleScope handle_scope;
1316         v8::Context::Scope scope(engine->context());
1317
1318         int argCount = argsTypes?argsTypes[0]:0;
1319         QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1320
1321         for (int ii = 0; ii < argCount; ++ii) {
1322             int type = argsTypes[ii + 1];
1323             if (type == qMetaTypeId<QVariant>()) {
1324                 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1325             } else {
1326                 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1327             }
1328         }
1329
1330         for (int ii = 0; ii < connections.count(); ++ii) {
1331             Connection &connection = connections[ii];
1332             if (connection.needsDestroy)
1333                 continue;
1334
1335             v8::TryCatch try_catch;
1336             if (connection.thisObject.IsEmpty()) {
1337                 connection.function->Call(engine->global(), argCount, args.data());
1338             } else {
1339                 connection.function->Call(connection.thisObject, argCount, args.data());
1340             }
1341
1342             if (try_catch.HasCaught()) {
1343                 QQmlError error;
1344                 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1345                 v8::Local<v8::Message> message = try_catch.Message();
1346                 if (!message.IsEmpty())
1347                     QQmlExpressionPrivate::exceptionToError(message, error);
1348                 QQmlEnginePrivate::get(engine->engine())->warning(error);
1349             }
1350         }
1351
1352         connectionList.connectionsInUse--;
1353         if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1354             for (QList<Connection>::Iterator iter = connectionList.begin(); 
1355                  iter != connectionList.end(); ) {
1356                 if (iter->needsDestroy) {
1357                     iter->dispose();
1358                     iter = connectionList.erase(iter);
1359                 } else {
1360                     ++iter;
1361                 }
1362             }
1363         }
1364
1365         inUse--;
1366         if (inUse == 0 && needsDestroy)
1367             delete this;
1368     } 
1369
1370     return -1;
1371 }
1372
1373 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1374 {
1375     if (args.Length() == 0)
1376         V8THROW_ERROR("Function.prototype.connect: no arguments given");
1377
1378     QV8Engine *engine = V8ENGINE();
1379
1380     QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1381     QObject *signalObject = signalInfo.first;
1382     int signalIndex = signalInfo.second;
1383
1384     if (signalIndex < 0)
1385         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1386
1387     if (!signalObject)
1388         V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1389
1390     if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1391         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1392
1393     v8::Local<v8::Value> functionValue;
1394     v8::Local<v8::Value> functionThisValue;
1395
1396     if (args.Length() == 1) {
1397         functionValue = args[0];
1398     } else {
1399         functionThisValue = args[0];
1400         functionValue = args[1];
1401     }
1402
1403     if (!functionValue->IsFunction())
1404         V8THROW_ERROR("Function.prototype.connect: target is not a function");
1405
1406     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1407         V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1408
1409     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1410     QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1411     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1412     if (iter == connections.end()) 
1413         iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1414
1415     QV8QObjectConnectionList *connectionList = *iter;
1416     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1417     if (slotIter == connectionList->slotHash.end()) {
1418         slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1419         QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1420     }
1421
1422     QV8QObjectConnectionList::Connection connection;
1423     if (!functionThisValue.IsEmpty()) 
1424         connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1425     connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1426
1427     slotIter->append(connection);
1428
1429     return v8::Undefined();
1430 }
1431
1432 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1433 {
1434     if (args.Length() == 0)
1435         V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1436
1437     QV8Engine *engine = V8ENGINE();
1438
1439     QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1440     QObject *signalObject = signalInfo.first;
1441     int signalIndex = signalInfo.second;
1442
1443     if (signalIndex == -1)
1444         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1445
1446     if (!signalObject)
1447         V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1448
1449     if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1450         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1451
1452     v8::Local<v8::Value> functionValue;
1453     v8::Local<v8::Value> functionThisValue;
1454
1455     if (args.Length() == 1) {
1456         functionValue = args[0];
1457     } else {
1458         functionThisValue = args[0];
1459         functionValue = args[1];
1460     }
1461
1462     if (!functionValue->IsFunction())
1463         V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1464
1465     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1466         V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1467
1468     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1469     QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1470     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1471     if (iter == connectionsList.end()) 
1472         return v8::Undefined(); // Nothing to disconnect from
1473
1474     QV8QObjectConnectionList *connectionList = *iter;
1475     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1476     if (slotIter == connectionList->slotHash.end()) 
1477         return v8::Undefined(); // Nothing to disconnect from
1478
1479     QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1480
1481     v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1482     QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1483
1484     if (functionData.second != -1) {
1485         // This is a QObject function wrapper
1486         for (int ii = 0; ii < connections.count(); ++ii) {
1487             QV8QObjectConnectionList::Connection &connection = connections[ii];
1488
1489             if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1490                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1491
1492                 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1493                 if (connectedFunctionData == functionData) {
1494                     // Match!
1495                     if (connections.connectionsInUse) {
1496                         connection.needsDestroy = true;
1497                         connections.connectionsNeedClean = true;
1498                     } else {
1499                         connection.dispose();
1500                         connections.removeAt(ii);
1501                     }
1502                     return v8::Undefined();
1503                 }
1504             }
1505         }
1506
1507     } else {
1508         // This is a normal JS function
1509         for (int ii = 0; ii < connections.count(); ++ii) {
1510             QV8QObjectConnectionList::Connection &connection = connections[ii];
1511             if (connection.function->StrictEquals(function) &&
1512                 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1513                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1514                 // Match!
1515                 if (connections.connectionsInUse) {
1516                     connection.needsDestroy = true;
1517                     connections.connectionsNeedClean = true;
1518                 } else {
1519                     connection.dispose();
1520                     connections.removeAt(ii);
1521                 }
1522                 return v8::Undefined();
1523             }
1524         }
1525     }
1526
1527     return v8::Undefined();
1528 }
1529
1530 /*!
1531     \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1532
1533     Get the \a property of \a object.  Returns an empty handle if the property doesn't exist.
1534
1535     Only searches for real properties of \a object (including methods), not attached properties etc.
1536 */
1537
1538 /*
1539     \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1540
1541     Set the \a property of \a object to \a value.
1542
1543     Returns true if the property was "set" - even if this results in an exception being thrown -
1544     and false if the object has no such property.
1545
1546     Only searches for real properties of \a object (including methods), not attached properties etc.
1547 */
1548
1549 namespace {
1550 struct CallArgs
1551 {
1552     CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1553     int Length() const { return _length; }
1554     v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1555
1556 private:
1557     int _length;
1558     v8::Handle<v8::Object> *_args;
1559 };
1560 }
1561
1562 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount, 
1563                                         int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1564 {
1565     if (argCount > 0) {
1566
1567         // Special handling is required for value types.
1568         // We need to save the current value in a temporary,
1569         // and reapply it after converting all arguments.
1570         // This avoids the "overwriting copy-value-type-value"
1571         // problem during Q_INVOKABLE function invocation.
1572         QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1573         QVariant valueTypeValue;
1574         if (valueTypeObject)
1575             valueTypeValue = valueTypeObject->value();
1576
1577         // Convert all arguments.
1578         QVarLengthArray<CallArgument, 9> args(argCount + 1);
1579         args[0].initAsType(returnType);
1580         for (int ii = 0; ii < argCount; ++ii)
1581             args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1582         QVarLengthArray<void *, 9> argData(args.count());
1583         for (int ii = 0; ii < args.count(); ++ii)
1584             argData[ii] = args[ii].dataPtr();
1585
1586         // Reinstate saved value type object value if required.
1587         if (valueTypeObject)
1588             valueTypeObject->setValue(valueTypeValue);
1589
1590         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1591
1592         return args[0].toValue(engine);
1593
1594     } else if (returnType != QMetaType::Void) {
1595         
1596         CallArgument arg;
1597         arg.initAsType(returnType);
1598
1599         void *args[] = { arg.dataPtr() };
1600
1601         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1602
1603         return arg.toValue(engine);
1604
1605     } else {
1606
1607         void *args[] = { 0 };
1608         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1609         return v8::Undefined();
1610
1611     }
1612 }
1613
1614 /*!
1615     Returns the match score for converting \a actual to be of type \a conversionType.  A 
1616     zero score means "perfect match" whereas a higher score is worse.
1617
1618     The conversion table is copied out of the QtScript callQtMethod() function.
1619 */
1620 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1621 {
1622     if (actual->IsNumber()) {
1623         switch (conversionType) {
1624         case QMetaType::Double:
1625             return 0;
1626         case QMetaType::Float:
1627             return 1;
1628         case QMetaType::LongLong:
1629         case QMetaType::ULongLong:
1630             return 2;
1631         case QMetaType::Long:
1632         case QMetaType::ULong:
1633             return 3;
1634         case QMetaType::Int:
1635         case QMetaType::UInt:
1636             return 4;
1637         case QMetaType::Short:
1638         case QMetaType::UShort:
1639             return 5;
1640             break;
1641         case QMetaType::Char:
1642         case QMetaType::UChar:
1643             return 6;
1644         case QMetaType::QJsonValue:
1645             return 5;
1646         default:
1647             return 10;
1648         }
1649     } else if (actual->IsString()) {
1650         switch (conversionType) {
1651         case QMetaType::QString:
1652             return 0;
1653         case QMetaType::QJsonValue:
1654             return 5;
1655         default:
1656             return 10;
1657         }
1658     } else if (actual->IsBoolean()) {
1659         switch (conversionType) {
1660         case QMetaType::Bool:
1661             return 0;
1662         case QMetaType::QJsonValue:
1663             return 5;
1664         default:
1665             return 10;
1666         }
1667     } else if (actual->IsDate()) {
1668         switch (conversionType) {
1669         case QMetaType::QDateTime:
1670             return 0;
1671         case QMetaType::QDate:
1672             return 1;
1673         case QMetaType::QTime:
1674             return 2;
1675         default:
1676             return 10;
1677         }
1678     } else if (actual->IsRegExp()) {
1679         switch (conversionType) {
1680         case QMetaType::QRegExp:
1681             return 0;
1682         default:
1683             return 10;
1684         }
1685     } else if (actual->IsArray()) {
1686         switch (conversionType) {
1687         case QMetaType::QJsonArray:
1688             return 3;
1689         case QMetaType::QStringList:
1690         case QMetaType::QVariantList:
1691             return 5;
1692         case QMetaType::QVector4D:
1693         case QMetaType::QMatrix4x4:
1694             return 6;
1695         case QMetaType::QVector3D:
1696             return 7;
1697         default:
1698             return 10;
1699         }
1700     } else if (actual->IsNull()) {
1701         switch (conversionType) {
1702         case QMetaType::VoidStar:
1703         case QMetaType::QObjectStar:
1704         case QMetaType::QJsonValue:
1705             return 0;
1706         default: {
1707             const char *typeName = QMetaType::typeName(conversionType);
1708             if (typeName && typeName[strlen(typeName) - 1] == '*')
1709                 return 0;
1710             else
1711                 return 10;
1712         }
1713         }
1714     } else if (actual->IsObject()) {
1715         v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1716
1717         QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1718         if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1719             switch (conversionType) {
1720             case QMetaType::QObjectStar:
1721                 return 0;
1722             default:
1723                 return 10;
1724             }
1725         } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1726             if (conversionType == qMetaTypeId<QVariant>())
1727                 return 0;
1728             else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1729                 return 0;
1730             else
1731                 return 10;
1732         } else if (r && r->resourceType() == QV8ObjectResource::ValueTypeType) {
1733             if (r->engine->toVariant(actual, -1).userType() == conversionType)
1734                 return 0;
1735             return 10;
1736         } else if (conversionType == QMetaType::QJsonObject) {
1737             return 5;
1738         } else {
1739             return 10;
1740         }
1741
1742     } else {
1743         return 10;
1744     }
1745 }
1746
1747 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1748 {
1749     struct Private
1750     {
1751         int revision;
1752         int className;
1753         int classInfoCount, classInfoData;
1754         int methodCount, methodData;
1755     };
1756
1757     return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1758 }
1759
1760 /*!
1761 Returns the next related method, if one, or 0.
1762 */
1763 static const QQmlPropertyData * RelatedMethod(QObject *object,
1764                                                       const QQmlPropertyData *current,
1765                                                       QQmlPropertyData &dummy)
1766 {
1767     QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1768     if (!current->isOverload())
1769         return 0;
1770
1771     Q_ASSERT(!current->overrideIndexIsProperty);
1772
1773     if (cache) {
1774         return cache->method(current->overrideIndex);
1775     } else {
1776         const QMetaObject *mo = object->metaObject();
1777         int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1778
1779         while (methodOffset > current->overrideIndex) {
1780             mo = mo->superClass();
1781             methodOffset -= QMetaObject_methods(mo);
1782         }
1783
1784         QMetaMethod method = mo->method(current->overrideIndex);
1785         dummy.load(method);
1786         
1787         // Look for overloaded methods
1788         QByteArray methodName = method.name();
1789         for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1790             if (methodName == mo->method(ii).name()) {
1791                 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1792                 dummy.overrideIndexIsProperty = 0;
1793                 dummy.overrideIndex = ii;
1794                 return &dummy;
1795             }
1796         }
1797
1798         return &dummy;
1799     }
1800 }
1801
1802 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1803                                          QV8Engine *engine, CallArgs &callArgs)
1804 {
1805     QByteArray unknownTypeError;
1806
1807     int returnType = QQmlPropertyCache::methodReturnType(object, data, &unknownTypeError);
1808
1809     if (returnType == QMetaType::UnknownType) {
1810         QString typeName = QString::fromLatin1(unknownTypeError);
1811         QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName);
1812         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1813         return v8::Handle<v8::Value>();
1814     }
1815
1816     if (data.hasArguments()) {
1817
1818         int *args = 0;
1819         QVarLengthArray<int, 9> dummy;
1820
1821         args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy, 
1822                                                                &unknownTypeError);
1823
1824         if (!args) {
1825             QString typeName = QString::fromLatin1(unknownTypeError);
1826             QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1827             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1828             return v8::Handle<v8::Value>();
1829         }
1830
1831         if (args[0] > callArgs.Length()) {
1832             QString error = QLatin1String("Insufficient arguments");
1833             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1834             return v8::Handle<v8::Value>();
1835         }
1836
1837         return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs);
1838
1839     } else {
1840
1841         return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs);
1842
1843     }
1844 }
1845
1846 /*!
1847 Resolve the overloaded method to call.  The algorithm works conceptually like this:
1848     1.  Resolve the set of overloads it is *possible* to call.
1849         Impossible overloads include those that have too many parameters or have parameters 
1850         of unknown type.  
1851     2.  Filter the set of overloads to only contain those with the closest number of 
1852         parameters.
1853         For example, if we are called with 3 parameters and there are 2 overloads that
1854         take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1855     3.  Find the best remaining overload based on its match score.  
1856         If two or more overloads have the same match score, call the last one.  The match
1857         score is constructed by adding the matchScore() result for each of the parameters.
1858 */
1859 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1860                                             QV8Engine *engine, CallArgs &callArgs)
1861 {
1862     int argumentCount = callArgs.Length();
1863
1864     const QQmlPropertyData *best = 0;
1865     int bestParameterScore = INT_MAX;
1866     int bestMatchScore = INT_MAX;
1867
1868     // Special handling is required for value types.
1869     // We need to save the current value in a temporary,
1870     // and reapply it after converting all arguments.
1871     // This avoids the "overwriting copy-value-type-value"
1872     // problem during Q_INVOKABLE function invocation.
1873     QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1874     QVariant valueTypeValue;
1875     if (valueTypeObject)
1876         valueTypeValue = valueTypeObject->value();
1877
1878     QQmlPropertyData dummy;
1879     const QQmlPropertyData *attempt = &data;
1880
1881     do {
1882         QVarLengthArray<int, 9> dummy;
1883         int methodArgumentCount = 0;
1884         int *methodArgTypes = 0;
1885         if (attempt->hasArguments()) {
1886             typedef QQmlPropertyCache PC;
1887             int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1888             if (!args) // Must be an unknown argument
1889                 continue;
1890
1891             methodArgumentCount = args[0];
1892             methodArgTypes = args + 1;
1893         }
1894
1895         if (methodArgumentCount > argumentCount)
1896             continue; // We don't have sufficient arguments to call this method
1897
1898         int methodParameterScore = argumentCount - methodArgumentCount;
1899         if (methodParameterScore > bestParameterScore)
1900             continue; // We already have a better option
1901
1902         int methodMatchScore = 0;
1903         for (int ii = 0; ii < methodArgumentCount; ++ii) 
1904             methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1905
1906         if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1907             best = attempt;
1908             bestParameterScore = methodParameterScore;
1909             bestMatchScore = methodMatchScore;
1910         }
1911
1912         if (bestParameterScore == 0 && bestMatchScore == 0)
1913             break; // We can't get better than that
1914
1915     } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1916
1917     if (best) {
1918         if (valueTypeObject)
1919             valueTypeObject->setValue(valueTypeValue);
1920         return CallPrecise(object, *best, engine, callArgs);
1921     } else {
1922         QString error = QLatin1String("Unable to determine callable overload.  Candidates are:");
1923         const QQmlPropertyData *candidate = &data;
1924         while (candidate) {
1925             error += QLatin1String("\n    ") + 
1926                      QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData());
1927             candidate = RelatedMethod(object, candidate, dummy);
1928         }
1929
1930         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1931         return v8::Handle<v8::Value>();
1932     }
1933 }
1934
1935 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1936 {
1937     QString result;
1938     if (object) {
1939         QString objectName = object->objectName();
1940
1941         result += QString::fromUtf8(object->metaObject()->className());
1942         result += QLatin1String("(0x");
1943         result += QString::number((quintptr)object,16);
1944
1945         if (!objectName.isEmpty()) {
1946             result += QLatin1String(", \"");
1947             result += objectName;
1948             result += QLatin1Char('\"');
1949         }
1950
1951         result += QLatin1Char(')');
1952     } else {
1953         result = QLatin1String("null");
1954     }
1955
1956     return engine->toString(result);
1957 }
1958
1959 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1960 {
1961     QQmlData *ddata = QQmlData::get(object, false);
1962     if (!ddata || ddata->indestructible || ddata->rootObjectInCreation) {
1963         const char *error = "Invalid attempt to destroy() an indestructible object";
1964         v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1965         return v8::Undefined();
1966     }
1967
1968     int delay = 0;
1969     if (argCount > 0)
1970         delay = args->Get(0)->Uint32Value();
1971
1972     if (delay > 0)
1973         QTimer::singleShot(delay, object, SLOT(deleteLater()));
1974     else
1975         object->deleteLater();
1976
1977     return v8::Undefined();
1978 }
1979
1980 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1981 {
1982     // object, index, qmlglobal, argCount, args
1983     Q_ASSERT(args.Length() == 5);
1984     Q_ASSERT(args[0]->IsObject());
1985
1986     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1987
1988     if (!resource)
1989         return v8::Undefined();
1990
1991     int argCount = args[3]->Int32Value();
1992     v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1993
1994     // Special hack to return info about this closure.
1995     if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1996         v8::Local<v8::Array> data = v8::Array::New(2);
1997         data->Set(0, args[0]);
1998         data->Set(1, args[1]);
1999         return data;
2000     }
2001
2002     QObject *object = resource->object;
2003     int index = args[1]->Int32Value();
2004
2005     if (!object)
2006         return v8::Undefined();
2007
2008     if (index < 0) {
2009         // Builtin functions
2010         if (index == QOBJECT_TOSTRING_INDEX) {
2011             return ToString(resource->engine, object, argCount, arguments);
2012         } else if (index == QOBJECT_DESTROY_INDEX) {
2013             return Destroy(resource->engine, object, argCount, arguments);
2014         } else {
2015             return v8::Undefined();
2016         }
2017     }
2018
2019     QQmlPropertyData method;
2020
2021     if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
2022         if (ddata->propertyCache) {
2023             QQmlPropertyData *d = ddata->propertyCache->method(index);
2024             if (!d) 
2025                 return v8::Undefined();
2026             method = *d;
2027         } 
2028     }
2029
2030     if (method.coreIndex == -1) {
2031         method.load(object->metaObject()->method(index));
2032
2033         if (method.coreIndex == -1)
2034             return v8::Undefined();
2035     }
2036
2037     if (method.isV8Function()) {
2038         v8::Handle<v8::Value> rv;
2039         v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
2040
2041         QQmlV8Function func(argCount, arguments, rv, qmlglobal, 
2042                                     resource->engine->contextWrapper()->context(qmlglobal),
2043                                     resource->engine);
2044         QQmlV8Function *funcptr = &func;
2045
2046         void *args[] = { 0, &funcptr };
2047         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
2048
2049         if (rv.IsEmpty()) return v8::Undefined();
2050         return rv;
2051     }
2052
2053     CallArgs callArgs(argCount, &arguments);
2054     if (!method.isOverload()) {
2055         return CallPrecise(object, method, resource->engine, callArgs);
2056     } else {
2057         return CallOverloaded(object, method, resource->engine, callArgs);
2058     }
2059 }
2060
2061 CallArgument::CallArgument()
2062 : type(QVariant::Invalid)
2063 {
2064 }
2065
2066 CallArgument::~CallArgument()
2067 {
2068     cleanup();
2069 }
2070
2071 void CallArgument::cleanup()
2072 {
2073     if (type == QMetaType::QString) {
2074         qstringPtr->~QString();
2075     } else if (type == -1 || type == QMetaType::QVariant) {
2076         qvariantPtr->~QVariant();
2077     } else if (type == qMetaTypeId<QJSValue>()) {
2078         qjsValuePtr->~QJSValue();
2079     } else if (type == qMetaTypeId<QList<QObject *> >()) {
2080         qlistPtr->~QList<QObject *>();
2081     }  else if (type == QMetaType::QJsonArray) {
2082         jsonArrayPtr->~QJsonArray();
2083     }  else if (type == QMetaType::QJsonObject) {
2084         jsonObjectPtr->~QJsonObject();
2085     }  else if (type == QMetaType::QJsonValue) {
2086         jsonValuePtr->~QJsonValue();
2087     } 
2088 }
2089
2090 void *CallArgument::dataPtr()
2091 {
2092     if (type == -1)
2093         return qvariantPtr->data();
2094     else
2095         return (void *)&allocData;
2096 }
2097
2098 void CallArgument::initAsType(int callType)
2099 {
2100     if (type != 0) { cleanup(); type = 0; }
2101     if (callType == QMetaType::UnknownType) return;
2102
2103     if (callType == qMetaTypeId<QJSValue>()) {
2104         qjsValuePtr = new (&allocData) QJSValue();
2105         type = callType;
2106     } else if (callType == QMetaType::Int ||
2107                callType == QMetaType::UInt ||
2108                callType == QMetaType::Bool ||
2109                callType == QMetaType::Double ||
2110                callType == QMetaType::Float) {
2111         type = callType;
2112     } else if (callType == QMetaType::QObjectStar) {
2113         qobjectPtr = 0;
2114         type = callType;
2115     } else if (callType == QMetaType::QString) {
2116         qstringPtr = new (&allocData) QString();
2117         type = callType;
2118     } else if (callType == QMetaType::QVariant) {
2119         type = callType;
2120         qvariantPtr = new (&allocData) QVariant();
2121     } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2122         type = callType;
2123         qlistPtr = new (&allocData) QList<QObject *>();
2124     } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2125         type = callType;
2126         handlePtr = new (&allocData) QQmlV8Handle;
2127     } else if (callType == QMetaType::QJsonArray) {
2128         type = callType;
2129         jsonArrayPtr = new (&allocData) QJsonArray();
2130     } else if (callType == QMetaType::QJsonObject) {
2131         type = callType;
2132         jsonObjectPtr = new (&allocData) QJsonObject();
2133     } else if (callType == QMetaType::QJsonValue) {
2134         type = callType;
2135         jsonValuePtr = new (&allocData) QJsonValue();
2136     } else if (callType == QMetaType::Void) {
2137         type = -1;
2138         qvariantPtr = new (&allocData) QVariant();
2139     } else {
2140         type = -1;
2141         qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2142     }
2143 }
2144
2145 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2146 {
2147     if (type != 0) { cleanup(); type = 0; }
2148
2149     if (callType == qMetaTypeId<QJSValue>()) {
2150         qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2151         type = qMetaTypeId<QJSValue>();
2152     } else if (callType == QMetaType::Int) {
2153         intValue = quint32(value->Int32Value());
2154         type = callType;
2155     } else if (callType == QMetaType::UInt) {
2156         intValue = quint32(value->Uint32Value());
2157         type = callType;
2158     } else if (callType == QMetaType::Bool) {
2159         boolValue = value->BooleanValue();
2160         type = callType;
2161     } else if (callType == QMetaType::Double) {
2162         doubleValue = double(value->NumberValue());
2163         type = callType;
2164     } else if (callType == QMetaType::Float) {
2165         floatValue = float(value->NumberValue());
2166         type = callType;
2167     } else if (callType == QMetaType::QString) {
2168         if (value->IsNull() || value->IsUndefined())
2169             qstringPtr = new (&allocData) QString();
2170         else
2171             qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2172         type = callType;
2173     } else if (callType == QMetaType::QObjectStar) {
2174         qobjectPtr = engine->toQObject(value);
2175         type = callType;
2176     } else if (callType == qMetaTypeId<QVariant>()) {
2177         qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2178         type = callType;
2179     } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2180         qlistPtr = new (&allocData) QList<QObject *>();
2181         if (value->IsArray()) {
2182             v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2183             uint32_t length = array->Length();
2184             for (uint32_t ii = 0; ii < length; ++ii) 
2185                 qlistPtr->append(engine->toQObject(array->Get(ii)));
2186         } else {
2187             qlistPtr->append(engine->toQObject(value));
2188         }
2189         type = callType;
2190     } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2191         handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2192         type = callType;
2193     } else if (callType == QMetaType::QJsonArray) {
2194         jsonArrayPtr = new (&allocData) QJsonArray(engine->jsonArrayFromJS(value));
2195         type = callType;
2196     } else if (callType == QMetaType::QJsonObject) {
2197         jsonObjectPtr = new (&allocData) QJsonObject(engine->jsonObjectFromJS(value));
2198         type = callType;
2199     } else if (callType == QMetaType::QJsonValue) {
2200         jsonValuePtr = new (&allocData) QJsonValue(engine->jsonValueFromJS(value));
2201         type = callType;
2202     } else if (callType == QMetaType::Void) {
2203         *qvariantPtr = QVariant();
2204     } else {
2205         qvariantPtr = new (&allocData) QVariant();
2206         type = -1;
2207
2208         QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2209         QVariant v = engine->toVariant(value, -1); // why -1 instead of callType?
2210
2211         if (v.userType() == callType) {
2212             *qvariantPtr = v;
2213         } else if (v.canConvert(callType)) {
2214             *qvariantPtr = v;
2215             qvariantPtr->convert(callType);
2216         } else if (engine->sequenceWrapper()->isSequenceType(callType) && v.userType() == qMetaTypeId<QVariantList>()) {
2217             // convert the JS array to a sequence of the correct type.
2218             QVariant seqV = engine->toVariant(value, callType);
2219             *qvariantPtr = seqV;
2220         } else {
2221             QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
2222             if (!mo.isNull()) {
2223                 QObject *obj = ep->toQObject(v);
2224
2225                 if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo))
2226                     obj = 0;
2227
2228                 *qvariantPtr = QVariant(callType, &obj);
2229             } else {
2230                 *qvariantPtr = QVariant(callType, (void *)0);
2231             }
2232         }
2233     }
2234 }
2235
2236 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2237 {
2238     if (type == qMetaTypeId<QJSValue>()) {
2239         return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2240     } else if (type == QMetaType::Int) {
2241         return v8::Integer::New(int(intValue));
2242     } else if (type == QMetaType::UInt) {
2243         return v8::Integer::NewFromUnsigned(intValue);
2244     } else if (type == QMetaType::Bool) {
2245         return v8::Boolean::New(boolValue);
2246     } else if (type == QMetaType::Double) {
2247         return v8::Number::New(doubleValue);
2248     } else if (type == QMetaType::Float) {
2249         return v8::Number::New(floatValue);
2250     } else if (type == QMetaType::QString) {
2251         return engine->toString(*qstringPtr);
2252     } else if (type == QMetaType::QObjectStar) {
2253         QObject *object = qobjectPtr;
2254         if (object)
2255             QQmlData::get(object, true)->setImplicitDestructible();
2256         return engine->newQObject(object);
2257     } else if (type == qMetaTypeId<QList<QObject *> >()) {
2258         // XXX Can this be made more by using Array as a prototype and implementing
2259         // directly against QList<QObject*>?
2260         QList<QObject *> &list = *qlistPtr;
2261         v8::Local<v8::Array> array = v8::Array::New(list.count());
2262         for (int ii = 0; ii < list.count(); ++ii) 
2263             array->Set(ii, engine->newQObject(list.at(ii)));
2264         return array;
2265     } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2266         return handlePtr->toHandle();
2267     } else if (type == QMetaType::QJsonArray) {
2268         return engine->jsonArrayToJS(*jsonArrayPtr);
2269     } else if (type == QMetaType::QJsonObject) {
2270         return engine->jsonObjectToJS(*jsonObjectPtr);
2271     } else if (type == QMetaType::QJsonValue) {
2272         return engine->jsonValueToJS(*jsonValuePtr);
2273     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2274         QVariant value = *qvariantPtr;
2275         v8::Handle<v8::Value> rv = engine->fromVariant(value);
2276         if (QObject *object = engine->toQObject(rv)) 
2277             QQmlData::get(object, true)->setImplicitDestructible();
2278         return rv;
2279     } else {
2280         return v8::Undefined();
2281     }
2282 }
2283
2284 QT_END_NAMESPACE
2285