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