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