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