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