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