Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / declarative / 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 QtDeclarative 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/qdeclarativeguard_p.h>
47 #include <private/qdeclarativepropertycache_p.h>
48 #include <private/qdeclarativeengine_p.h>
49 #include <private/qdeclarativevmemetaobject_p.h>
50 #include <private/qdeclarativebinding_p.h>
51 #include <private/qjsvalue_p.h>
52 #include <private/qscript_impl_p.h>
53 #include <private/qdeclarativeaccessors_p.h>
54 #include <private/qdeclarativeexpression_p.h>
55
56 #include <QtDeclarative/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(QDeclarativeV8Handle);
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 QDeclarativeEngine *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     QDeclarativeGuard<QObject> object;
88 };
89
90 class QV8QObjectInstance : public QDeclarativeGuard<QObject>
91 {
92 public:
93     QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
94     : QDeclarativeGuard<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     QDeclarativeGuard<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                                 QDeclarativeV8Handle>::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         QDeclarativeV8Handle *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 QDeclarativePropertyData &property,
220                                 void *output, QDeclarativeNotifier **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 QDeclarativePropertyData &property,
230                               void *output, QDeclarativeNotifier **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 QDeclarativePropertyData &property,
240                                 void *output, QDeclarativeNotifier **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 QDeclarativePropertyData &,
265                                           void *, QDeclarativeNotifier **)>
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     QDeclarativePropertyData *property =
275         (QDeclarativePropertyData *)v8::External::Unwrap(info.Data());
276
277     QDeclarativeEngine *engine = resource->engine->engine();
278     QDeclarativeEnginePrivate *ep = engine?QDeclarativeEnginePrivate::get(engine):0;
279
280     T value = T();
281
282     if (ep && ep->propertyCapture) {
283         if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) {
284             QDeclarativeNotifier *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 QDeclarativePropertyData &,
384                               void *, QDeclarativeNotifier **)>
385 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
386                                           const QDeclarativePropertyData &property,
387                                           QDeclarativeNotifier **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         QDeclarativeV8Handle 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 (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)
434                && engine->engine()) {
435         Q_ASSERT(notifier == 0);
436
437         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
438         QDeclarativeValueType *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     QDeclarativePropertyData local;
504     QDeclarativePropertyData *result = 0;
505     {
506         QDeclarativeData *ddata = QDeclarativeData::get(object, false);
507         if (ddata && ddata->propertyCache)
508             result = ddata->propertyCache->property(property);
509         else
510             result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
511     }
512
513     if (!result)
514         return v8::Handle<v8::Value>();
515
516     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
517         QDeclarativeData *ddata = QDeclarativeData::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 ((QDeclarativeVMEMetaObject *)(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     QDeclarativeEnginePrivate *ep =
538         engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
539
540     if (result->hasAccessors()) {
541         QDeclarativeNotifier *n = 0;
542         QDeclarativeNotifier **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(QDeclarativeData::get(object, true)->objectNameNotifier());
562         else
563             ep->captureProperty(object, result->coreIndex, result->notifyIndex);
564     }
565
566     if (result->isVMEProperty()) {
567         typedef QDeclarativeVMEMetaObject 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, QDeclarativePropertyData *property,
579                                  v8::Handle<v8::Value> value)
580 {
581     QDeclarativeBinding *newBinding = 0;
582
583     if (value->IsFunction()) {
584         QDeclarativeContextData *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 QDeclarativeBinding(&function, object, context);
596         newBinding->setSourceLocation(url, lineNumber, columNumber);
597         newBinding->setTarget(object, *property, context);
598         newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
599                                      QDeclarativeBinding::RequiresThisObject);
600     }
601
602     QDeclarativeAbstractBinding *oldBinding = 
603         QDeclarativePropertyPrivate::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<QDeclarativeVMEMetaObject *>(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         QDeclarativeContextData *context = engine->callingContext();
648         if (!QDeclarativePropertyPrivate::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     QDeclarativePropertyData local;
670     QDeclarativePropertyData *result = 0;
671     result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
672
673     if (!result)
674         return false;
675
676     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
677         QDeclarativeData *ddata = QDeclarativeData::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         QDeclarativeContextData *context = v8engine->callingContext();
716
717         if (context && context->imports) {
718             QDeclarativeTypeNameCache::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     QDeclarativePropertyData local;
777     QDeclarativePropertyData *result = 0;
778     result = QDeclarativePropertyCache::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     QDeclarativeEnginePrivate *ep = resource->engine->engine()
800             ? QDeclarativeEnginePrivate::get(resource->engine->engine())
801             : 0;
802
803     QDeclarativePropertyCache *cache = 0;
804     QDeclarativeData *ddata = QDeclarativeData::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     QDeclarativePropertyData *property =
843         (QDeclarativePropertyData *)v8::External::Unwrap(info.Data());
844
845     int index = property->coreIndex;
846
847     QDeclarativeData *ddata = QDeclarativeData::get(object, false);
848     Q_ASSERT(ddata);
849     Q_ASSERT(ddata->propertyCache);
850
851     QDeclarativePropertyData *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         QDeclarativeData *ddata = QDeclarativeData::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> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
903 {
904     Q_ASSERT(object);
905     Q_ASSERT(this->engine);
906
907     Q_ASSERT(QDeclarativeData::get(object, false));
908     Q_ASSERT(QDeclarativeData::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         // XXX TODO: Enables fast property accessors.  These more than double the property access 
918         // performance, but the  cost of setting up this structure hasn't been measured so 
919         // its not guarenteed that this is a win overall.  We need to try and measure the cost.
920         for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
921             QDeclarativePropertyData *property = *iter;
922             if (property->notFullyResolved()) resolve(property);
923
924             if (property->isFunction())
925                 continue;
926
927             v8::AccessorGetter fastgetter = 0;
928             v8::AccessorSetter fastsetter = FastValueSetter;
929             if (!property->isWritable())
930                 fastsetter = FastValueSetterReadOnly;
931
932             if (property->isQObject()) 
933                 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
934             else if (property->propType == QMetaType::Int || property->isEnum()) 
935                 fastgetter = FAST_GETTER_FUNCTION(property, int);
936             else if (property->propType == QMetaType::Bool)
937                 fastgetter = FAST_GETTER_FUNCTION(property, bool);
938             else if (property->propType == QMetaType::QString)
939                 fastgetter = FAST_GETTER_FUNCTION(property, QString);
940             else if (property->propType == QMetaType::UInt)
941                 fastgetter = FAST_GETTER_FUNCTION(property, uint);
942             else if (property->propType == QMetaType::Float) 
943                 fastgetter = FAST_GETTER_FUNCTION(property, float);
944             else if (property->propType == QMetaType::Double) 
945                 fastgetter = FAST_GETTER_FUNCTION(property, double);
946
947             if (fastgetter) {
948                 QString name = iter.key();
949                 if (name == toString || name == destroy)
950                     continue;
951
952                 if (ft.IsEmpty()) {
953                     ft = v8::FunctionTemplate::New();
954                     ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
955                                                                        QV8QObjectWrapper::Setter,
956                                                                        QV8QObjectWrapper::Query, 
957                                                                        0,
958                                                                        QV8QObjectWrapper::Enumerator);
959                     ft->InstanceTemplate()->SetHasExternalResource(true);
960                 }
961
962                 // We wrap the raw QDeclarativePropertyData pointer here.  This is safe as the
963                 // pointer will remain valid at least as long as the lifetime of any QObject's of
964                 // this type and the property accessor checks if the object is 0 (deleted) before
965                 // dereferencing the pointer.
966                 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
967                                                     v8::External::Wrap(property));
968             }
969         }
970
971         if (ft.IsEmpty()) {
972             constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
973         } else {
974             ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
975                                                                QV8QObjectWrapper::Setter,
976                                                                QV8QObjectWrapper::Query, 
977                                                                0,
978                                                                QV8QObjectWrapper::Enumerator);
979             ft->InstanceTemplate()->SetHasExternalResource(true);
980             constructor = qPersistentNew<v8::Function>(ft->GetFunction());
981         }
982
983         QDeclarativeCleanup::addToEngine(this->engine);
984     }
985
986     v8::Local<v8::Object> result = constructor->NewInstance();
987     QV8QObjectResource *r = new QV8QObjectResource(engine, object);
988     result->SetExternalResource(r);
989     return result;
990 }
991
992 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
993 {
994     v8::Local<v8::Object> rv;
995
996     if (!ddata->propertyCache && engine->engine()) {
997         ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
998         if (ddata->propertyCache) ddata->propertyCache->addref();
999     }
1000
1001     if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1002         rv = ddata->propertyCache->newQObject(object, engine);
1003     } else {
1004         // XXX NewInstance() should be optimized
1005         rv = m_constructor->NewInstance(); 
1006         QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1007         rv->SetExternalResource(r);
1008     }
1009
1010     return rv;
1011 }
1012
1013 /*
1014 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1015 V8 handle for subsequent calls to newQObject for the same QObject.  To do this we have a two
1016 pronged strategy:
1017    1. If there is no current outstanding V8 handle to the QObject, we create one and store a 
1018       persistent handle in QDeclarativeData::v8object.  We mark the QV8QObjectWrapper that 
1019       "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this 
1020       QV8QObjectWrapper.
1021    2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create 
1022       an entry in the m_taintedObject hash where we store the handle and mark the object as 
1023       "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
1024 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1025 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has 
1026 released the handle.
1027 */
1028 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1029 {
1030     if (!object)
1031         return v8::Null();
1032
1033     if (QObjectPrivate::get(object)->wasDeleted)
1034        return v8::Undefined();
1035     
1036     QDeclarativeData *ddata = QDeclarativeData::get(object, true);
1037
1038     if (!ddata) 
1039         return v8::Undefined();
1040
1041     if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1042         // We own the v8object 
1043         return v8::Local<v8::Object>::New(ddata->v8object);
1044     } else if (ddata->v8object.IsEmpty() && 
1045                (ddata->v8objectid == m_id || // We own the QObject
1046                 ddata->v8objectid == 0 ||    // No one owns the QObject
1047                 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1048
1049         v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1050         ddata->v8object = qPersistentNew<v8::Object>(rv);
1051         ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1052         ddata->v8objectid = m_id;
1053         return rv;
1054
1055     } else {
1056         // If this object is tainted, we have to check to see if it is in our
1057         // tainted object list
1058         TaintedHash::Iterator iter =
1059             ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1060         bool found = iter != m_taintedObjects.end();
1061
1062         // If our tainted handle doesn't exist or has been collected, and there isn't
1063         // a handle in the ddata, we can assume ownership of the ddata->v8object
1064         if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1065             v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1066             ddata->v8object = qPersistentNew<v8::Object>(rv);
1067             ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1068             ddata->v8objectid = m_id;
1069
1070             if (found) {
1071                 delete (*iter);
1072                 m_taintedObjects.erase(iter);
1073             }
1074
1075             return rv;
1076         } else if (!found) {
1077             QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1078             iter = m_taintedObjects.insert(object, instance);
1079             ddata->hasTaintedV8Object = true;
1080         }
1081
1082         if ((*iter)->v8object.IsEmpty()) {
1083             v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1084             (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1085             (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1086         }
1087
1088         return v8::Local<v8::Object>::New((*iter)->v8object);
1089     }
1090 }
1091
1092 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1093 {
1094     if (object->IsFunction())
1095         return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1096
1097     if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1098         return qMakePair(resource->object.data(), resource->index);
1099
1100     return qMakePair((QObject *)0, -1);
1101 }
1102
1103 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1104 {
1105     v8::ScriptOrigin origin = function->GetScriptOrigin();
1106     if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1107
1108         // This is one of our special QObject method wrappers
1109         v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1110         v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1111
1112         if (data->IsArray()) {
1113             v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1114             return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1115         } 
1116
1117         // In theory this can't fall through, but I suppose V8 might run out of memory or something
1118     }
1119
1120     return qMakePair((QObject *)0, -1);
1121 }
1122
1123 class QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
1124 {
1125 public:
1126     QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1127     ~QV8QObjectConnectionList();
1128
1129     struct Connection {
1130         Connection() 
1131         : needsDestroy(false) {}
1132         Connection(const Connection &other) 
1133         : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1134         Connection &operator=(const Connection &other) {
1135             thisObject = other.thisObject;
1136             function = other.function;
1137             needsDestroy = other.needsDestroy;
1138             return *this;
1139         }
1140
1141         v8::Persistent<v8::Object> thisObject;
1142         v8::Persistent<v8::Function> function;
1143
1144         void dispose() {
1145             qPersistentDispose(thisObject);
1146             qPersistentDispose(function);
1147         }
1148
1149         bool needsDestroy;
1150     };
1151
1152     struct ConnectionList : public QList<Connection> {
1153         ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1154         int connectionsInUse;
1155         bool connectionsNeedClean;
1156     };
1157
1158     QV8Engine *engine;
1159
1160     typedef QHash<int, ConnectionList> SlotHash;
1161     SlotHash slotHash;
1162     bool needsDestroy;
1163     int inUse;
1164
1165     virtual void objectDestroyed(QObject *);
1166     virtual int qt_metacall(QMetaObject::Call, int, void **);
1167 };
1168
1169 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1170 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1171 {
1172 }
1173
1174 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1175 {
1176     for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1177         QList<Connection> &connections = *iter;
1178         for (int ii = 0; ii < connections.count(); ++ii) {
1179             qPersistentDispose(connections[ii].thisObject);
1180             qPersistentDispose(connections[ii].function);
1181         }
1182     }
1183     slotHash.clear();
1184 }
1185
1186 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1187 {
1188     engine->qobjectWrapper()->m_connections.remove(object);
1189
1190     if (inUse)
1191         needsDestroy = true;
1192     else
1193         delete this;
1194 }
1195
1196 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1197 {
1198     if (method == QMetaObject::InvokeMetaMethod) {
1199         SlotHash::Iterator iter = slotHash.find(index);
1200         if (iter == slotHash.end())
1201             return -1;
1202         ConnectionList &connectionList = *iter;
1203         if (connectionList.isEmpty())
1204             return -1;
1205
1206         inUse++;
1207
1208         connectionList.connectionsInUse++;
1209
1210         QList<Connection> connections = connectionList;
1211
1212         QVarLengthArray<int, 9> dummy;
1213         int *argsTypes = QDeclarativePropertyCache::methodParameterTypes(data(), index, dummy, 0);
1214
1215         v8::HandleScope handle_scope;
1216         v8::Context::Scope scope(engine->context());
1217
1218         int argCount = argsTypes?argsTypes[0]:0;
1219         QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1220
1221         for (int ii = 0; ii < argCount; ++ii) {
1222             int type = argsTypes[ii + 1];
1223             if (type == qMetaTypeId<QVariant>()) {
1224                 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1225             } else {
1226                 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1227             }
1228         }
1229
1230         for (int ii = 0; ii < connections.count(); ++ii) {
1231             Connection &connection = connections[ii];
1232             if (connection.needsDestroy)
1233                 continue;
1234
1235             v8::TryCatch try_catch;
1236             if (connection.thisObject.IsEmpty()) {
1237                 connection.function->Call(engine->global(), argCount, args.data());
1238             } else {
1239                 connection.function->Call(connection.thisObject, argCount, args.data());
1240             }
1241
1242             if (try_catch.HasCaught()) {
1243                 QDeclarativeError error;
1244                 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1245                 v8::Local<v8::Message> message = try_catch.Message();
1246                 if (!message.IsEmpty())
1247                     QDeclarativeExpressionPrivate::exceptionToError(message, error);
1248                 QDeclarativeEnginePrivate::get(engine->engine())->warning(error);
1249             }
1250         }
1251
1252         connectionList.connectionsInUse--;
1253         if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1254             for (QList<Connection>::Iterator iter = connectionList.begin(); 
1255                  iter != connectionList.end(); ) {
1256                 if (iter->needsDestroy) {
1257                     iter->dispose();
1258                     iter = connectionList.erase(iter);
1259                 } else {
1260                     ++iter;
1261                 }
1262             }
1263         }
1264
1265         inUse--;
1266         if (inUse == 0 && needsDestroy)
1267             delete this;
1268     } 
1269
1270     return -1;
1271 }
1272
1273 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1274 {
1275     if (args.Length() == 0)
1276         V8THROW_ERROR("Function.prototype.connect: no arguments given");
1277
1278     QV8Engine *engine = V8ENGINE();
1279
1280     QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1281     QObject *signalObject = signalInfo.first;
1282     int signalIndex = signalInfo.second;
1283
1284     if (signalIndex == -1)
1285         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1286
1287     if (!signalObject)
1288         V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1289
1290     if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1291         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1292
1293     v8::Local<v8::Value> functionValue;
1294     v8::Local<v8::Value> functionThisValue;
1295
1296     if (args.Length() == 1) {
1297         functionValue = args[0];
1298     } else {
1299         functionThisValue = args[0];
1300         functionValue = args[1];
1301     }
1302
1303     if (!functionValue->IsFunction())
1304         V8THROW_ERROR("Function.prototype.connect: target is not a function");
1305
1306     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1307         V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1308
1309     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1310     QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1311     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1312     if (iter == connections.end()) 
1313         iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1314
1315     QV8QObjectConnectionList *connectionList = *iter;
1316     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1317     if (slotIter == connectionList->slotHash.end()) {
1318         slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1319         QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1320     }
1321
1322     QV8QObjectConnectionList::Connection connection;
1323     if (!functionThisValue.IsEmpty()) 
1324         connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1325     connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1326
1327     slotIter->append(connection);
1328
1329     return v8::Undefined();
1330 }
1331
1332 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1333 {
1334     if (args.Length() == 0)
1335         V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1336
1337     QV8Engine *engine = V8ENGINE();
1338
1339     QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1340     QObject *signalObject = signalInfo.first;
1341     int signalIndex = signalInfo.second;
1342
1343     if (signalIndex == -1)
1344         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1345
1346     if (!signalObject)
1347         V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1348
1349     if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1350         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1351
1352     v8::Local<v8::Value> functionValue;
1353     v8::Local<v8::Value> functionThisValue;
1354
1355     if (args.Length() == 1) {
1356         functionValue = args[0];
1357     } else {
1358         functionThisValue = args[0];
1359         functionValue = args[1];
1360     }
1361
1362     if (!functionValue->IsFunction())
1363         V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1364
1365     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1366         V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1367
1368     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1369     QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1370     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1371     if (iter == connectionsList.end()) 
1372         return v8::Undefined(); // Nothing to disconnect from
1373
1374     QV8QObjectConnectionList *connectionList = *iter;
1375     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1376     if (slotIter == connectionList->slotHash.end()) 
1377         return v8::Undefined(); // Nothing to disconnect from
1378
1379     QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1380
1381     v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1382     QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1383
1384     if (functionData.second != -1) {
1385         // This is a QObject function wrapper
1386         for (int ii = 0; ii < connections.count(); ++ii) {
1387             QV8QObjectConnectionList::Connection &connection = connections[ii];
1388
1389             if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1390                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1391
1392                 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1393                 if (connectedFunctionData == functionData) {
1394                     // Match!
1395                     if (connections.connectionsInUse) {
1396                         connection.needsDestroy = true;
1397                         connections.connectionsNeedClean = true;
1398                     } else {
1399                         connection.dispose();
1400                         connections.removeAt(ii);
1401                     }
1402                     return v8::Undefined();
1403                 }
1404             }
1405         }
1406
1407     } else {
1408         // This is a normal JS function
1409         for (int ii = 0; ii < connections.count(); ++ii) {
1410             QV8QObjectConnectionList::Connection &connection = connections[ii];
1411             if (connection.function->StrictEquals(function) &&
1412                 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1413                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1414                 // Match!
1415                 if (connections.connectionsInUse) {
1416                     connection.needsDestroy = true;
1417                     connections.connectionsNeedClean = true;
1418                 } else {
1419                     connection.dispose();
1420                     connections.removeAt(ii);
1421                 }
1422                 return v8::Undefined();
1423             }
1424         }
1425     }
1426
1427     return v8::Undefined();
1428 }
1429
1430 /*!
1431     \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1432
1433     Get the \a property of \a object.  Returns an empty handle if the property doesn't exist.
1434
1435     Only searches for real properties of \a object (including methods), not attached properties etc.
1436 */
1437
1438 /*
1439     \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1440
1441     Set the \a property of \a object to \a value.
1442
1443     Returns true if the property was "set" - even if this results in an exception being thrown -
1444     and false if the object has no such property.
1445
1446     Only searches for real properties of \a object (including methods), not attached properties etc.
1447 */
1448
1449 namespace {
1450 struct CallArgs
1451 {
1452     CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1453     int Length() const { return _length; }
1454     v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1455
1456 private:
1457     int _length;
1458     v8::Handle<v8::Object> *_args;
1459 };
1460 }
1461
1462 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount, 
1463                                         int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1464 {
1465     if (argCount > 0) {
1466
1467         QVarLengthArray<CallArgument, 9> args(argCount + 1);
1468         args[0].initAsType(returnType);
1469
1470         for (int ii = 0; ii < argCount; ++ii)
1471             args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1472
1473         QVarLengthArray<void *, 9> argData(args.count());
1474         for (int ii = 0; ii < args.count(); ++ii)
1475             argData[ii] = args[ii].dataPtr();
1476
1477         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1478
1479         return args[0].toValue(engine);
1480
1481     } else if (returnType != 0) {
1482         
1483         CallArgument arg;
1484         arg.initAsType(returnType);
1485
1486         void *args[] = { arg.dataPtr() };
1487
1488         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1489
1490         return arg.toValue(engine);
1491
1492     } else {
1493
1494         void *args[] = { 0 };
1495         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1496         return v8::Undefined();
1497
1498     }
1499 }
1500
1501 /*!
1502     Returns the match score for converting \a actual to be of type \a conversionType.  A 
1503     zero score means "perfect match" whereas a higher score is worse.
1504
1505     The conversion table is copied out of the QtScript callQtMethod() function.
1506 */
1507 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1508 {
1509     if (actual->IsNumber()) {
1510         switch (conversionType) {
1511         case QMetaType::Double:
1512             return 0;
1513         case QMetaType::Float:
1514             return 1;
1515         case QMetaType::LongLong:
1516         case QMetaType::ULongLong:
1517             return 2;
1518         case QMetaType::Long:
1519         case QMetaType::ULong:
1520             return 3;
1521         case QMetaType::Int:
1522         case QMetaType::UInt:
1523             return 4;
1524         case QMetaType::Short:
1525         case QMetaType::UShort:
1526             return 5;
1527             break;
1528         case QMetaType::Char:
1529         case QMetaType::UChar:
1530             return 6;
1531         default:
1532             return 10;
1533         }
1534     } else if (actual->IsString()) {
1535         switch (conversionType) {
1536         case QMetaType::QString:
1537             return 0;
1538         default:
1539             return 10;
1540         }
1541     } else if (actual->IsBoolean()) {
1542         switch (conversionType) {
1543         case QMetaType::Bool:
1544             return 0;
1545         default:
1546             return 10;
1547         }
1548     } else if (actual->IsDate()) {
1549         switch (conversionType) {
1550         case QMetaType::QDateTime:
1551             return 0;
1552         case QMetaType::QDate:
1553             return 1;
1554         case QMetaType::QTime:
1555             return 2;
1556         default:
1557             return 10;
1558         }
1559     } else if (actual->IsRegExp()) {
1560         switch (conversionType) {
1561         case QMetaType::QRegExp:
1562             return 0;
1563         default:
1564             return 10;
1565         }
1566     } else if (actual->IsArray()) {
1567         switch (conversionType) {
1568         case QMetaType::QStringList:
1569         case QMetaType::QVariantList:
1570             return 5;
1571         default:
1572             return 10;
1573         }
1574     } else if (actual->IsNull()) {
1575         switch (conversionType) {
1576         case QMetaType::VoidStar:
1577         case QMetaType::QObjectStar:
1578             return 0;
1579         default: {
1580             const char *typeName = QMetaType::typeName(conversionType);
1581             if (typeName && typeName[strlen(typeName) - 1] == '*')
1582                 return 0;
1583             else
1584                 return 10;
1585         }
1586         }
1587     } else if (actual->IsObject()) {
1588         v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1589
1590         QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1591         if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1592             switch (conversionType) {
1593             case QMetaType::QObjectStar:
1594                 return 0;
1595             default:
1596                 return 10;
1597             }
1598         } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1599             if (conversionType == qMetaTypeId<QVariant>())
1600                 return 0;
1601             else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1602                 return 0;
1603             else
1604                 return 10;
1605         } else {
1606             return 10;
1607         }
1608
1609     } else {
1610         return 10;
1611     }
1612 }
1613
1614 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1615 {
1616     struct Private
1617     {
1618         int revision;
1619         int className;
1620         int classInfoCount, classInfoData;
1621         int methodCount, methodData;
1622     };
1623
1624     return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1625 }
1626
1627 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1628 {
1629     QByteArray sig = m.signature();
1630     int paren = sig.indexOf('(');
1631     if (paren == -1)
1632         return sig;
1633     else
1634         return sig.left(paren);
1635 }
1636
1637 /*!
1638 Returns the next related method, if one, or 0.
1639 */
1640 static const QDeclarativePropertyData * RelatedMethod(QObject *object,
1641                                                       const QDeclarativePropertyData *current,
1642                                                       QDeclarativePropertyData &dummy)
1643 {
1644     QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1645     if (!current->isOverload())
1646         return 0;
1647
1648     Q_ASSERT(!current->overrideIndexIsProperty);
1649
1650     if (cache) {
1651         return cache->method(current->overrideIndex);
1652     } else {
1653         const QMetaObject *mo = object->metaObject();
1654         int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1655
1656         while (methodOffset > current->overrideIndex) {
1657             mo = mo->superClass();
1658             methodOffset -= QMetaObject_methods(mo);
1659         }
1660
1661         QMetaMethod method = mo->method(current->overrideIndex);
1662         dummy.load(method);
1663         
1664         // Look for overloaded methods
1665         QByteArray methodName = QMetaMethod_name(method);
1666         for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1667             if (methodName == QMetaMethod_name(mo->method(ii))) {
1668                 dummy.setFlags(dummy.getFlags() | QDeclarativePropertyData::IsOverload);
1669                 dummy.overrideIndexIsProperty = 0;
1670                 dummy.overrideIndex = ii;
1671                 return &dummy;
1672             }
1673         }
1674
1675         return &dummy;
1676     }
1677 }
1678
1679 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyData &data,
1680                                          QV8Engine *engine, CallArgs &callArgs)
1681 {
1682     if (data.hasArguments()) {
1683
1684         int *args = 0;
1685         QVarLengthArray<int, 9> dummy;
1686         QByteArray unknownTypeError;
1687
1688         args = QDeclarativePropertyCache::methodParameterTypes(object, data.coreIndex, dummy, 
1689                                                                &unknownTypeError);
1690
1691         if (!args) {
1692             QString typeName = QString::fromLatin1(unknownTypeError);
1693             QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1694             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1695             return v8::Handle<v8::Value>();
1696         }
1697
1698         if (args[0] > callArgs.Length()) {
1699             QString error = QLatin1String("Insufficient arguments");
1700             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1701             return v8::Handle<v8::Value>();
1702         }
1703
1704         return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1705
1706     } else {
1707
1708         return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1709
1710     }
1711 }
1712
1713 /*!
1714 Resolve the overloaded method to call.  The algorithm works conceptually like this:
1715     1.  Resolve the set of overloads it is *possible* to call.
1716         Impossible overloads include those that have too many parameters or have parameters 
1717         of unknown type.  
1718     2.  Filter the set of overloads to only contain those with the closest number of 
1719         parameters.
1720         For example, if we are called with 3 parameters and there are 2 overloads that
1721         take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1722     3.  Find the best remaining overload based on its match score.  
1723         If two or more overloads have the same match score, call the last one.  The match
1724         score is constructed by adding the matchScore() result for each of the parameters.
1725 */
1726 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyData &data,
1727                                             QV8Engine *engine, CallArgs &callArgs)
1728 {
1729     int argumentCount = callArgs.Length();
1730
1731     const QDeclarativePropertyData *best = 0;
1732     int bestParameterScore = INT_MAX;
1733     int bestMatchScore = INT_MAX;
1734
1735     QDeclarativePropertyData dummy;
1736     const QDeclarativePropertyData *attempt = &data;
1737
1738     do {
1739         QVarLengthArray<int, 9> dummy;
1740         int methodArgumentCount = 0;
1741         int *methodArgTypes = 0;
1742         if (attempt->hasArguments()) {
1743             typedef QDeclarativePropertyCache PC;
1744             int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1745             if (!args) // Must be an unknown argument
1746                 continue;
1747
1748             methodArgumentCount = args[0];
1749             methodArgTypes = args + 1;
1750         }
1751
1752         if (methodArgumentCount > argumentCount)
1753             continue; // We don't have sufficient arguments to call this method
1754
1755         int methodParameterScore = argumentCount - methodArgumentCount;
1756         if (methodParameterScore > bestParameterScore)
1757             continue; // We already have a better option
1758
1759         int methodMatchScore = 0;
1760         for (int ii = 0; ii < methodArgumentCount; ++ii) 
1761             methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1762
1763         if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1764             best = attempt;
1765             bestParameterScore = methodParameterScore;
1766             bestMatchScore = methodMatchScore;
1767         }
1768
1769         if (bestParameterScore == 0 && bestMatchScore == 0)
1770             break; // We can't get better than that
1771
1772     } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1773
1774     if (best) {
1775         return CallPrecise(object, *best, engine, callArgs);
1776     } else {
1777         QString error = QLatin1String("Unable to determine callable overload.  Candidates are:");
1778         const QDeclarativePropertyData *candidate = &data;
1779         while (candidate) {
1780             error += QLatin1String("\n    ") + 
1781                      QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1782             candidate = RelatedMethod(object, candidate, dummy);
1783         }
1784
1785         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1786         return v8::Handle<v8::Value>();
1787     }
1788 }
1789
1790 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1791 {
1792     QString result;
1793     if (object) {
1794         QString objectName = object->objectName();
1795
1796         result += QString::fromUtf8(object->metaObject()->className());
1797         result += QLatin1String("(0x");
1798         result += QString::number((quintptr)object,16);
1799
1800         if (!objectName.isEmpty()) {
1801             result += QLatin1String(", \"");
1802             result += objectName;
1803             result += QLatin1Char('\"');
1804         }
1805
1806         result += QLatin1Char(')');
1807     } else {
1808         result = QLatin1String("null");
1809     }
1810
1811     return engine->toString(result);
1812 }
1813
1814 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1815 {
1816     QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1817     if (!ddata || ddata->indestructible) {
1818         const char *error = "Invalid attempt to destroy() an indestructible object";
1819         v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1820         return v8::Undefined();
1821     }
1822
1823     int delay = 0;
1824     if (argCount > 0)
1825         delay = args->Get(0)->Uint32Value();
1826
1827     if (delay > 0)
1828         QTimer::singleShot(delay, object, SLOT(deleteLater()));
1829     else
1830         object->deleteLater();
1831
1832     return v8::Undefined();
1833 }
1834
1835 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1836 {
1837     // object, index, qmlglobal, argCount, args
1838     Q_ASSERT(args.Length() == 5);
1839     Q_ASSERT(args[0]->IsObject());
1840
1841     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1842
1843     if (!resource)
1844         return v8::Undefined();
1845
1846     int argCount = args[3]->Int32Value();
1847     v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1848
1849     // Special hack to return info about this closure.
1850     if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1851         v8::Local<v8::Array> data = v8::Array::New(2);
1852         data->Set(0, args[0]);
1853         data->Set(1, args[1]);
1854         return data;
1855     }
1856
1857     QObject *object = resource->object;
1858     int index = args[1]->Int32Value();
1859
1860     if (!object)
1861         return v8::Undefined();
1862
1863     if (index < 0) {
1864         // Builtin functions
1865         if (index == QOBJECT_TOSTRING_INDEX) {
1866             return ToString(resource->engine, object, argCount, arguments);
1867         } else if (index == QOBJECT_DESTROY_INDEX) {
1868             return Destroy(resource->engine, object, argCount, arguments);
1869         } else {
1870             return v8::Undefined();
1871         }
1872     }
1873
1874     QDeclarativePropertyData method;
1875
1876     if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1877         if (ddata->propertyCache) {
1878             QDeclarativePropertyData *d = ddata->propertyCache->method(index);
1879             if (!d) 
1880                 return v8::Undefined();
1881             method = *d;
1882         } 
1883     }
1884
1885     if (method.coreIndex == -1) {
1886         method.load(object->metaObject()->method(index));
1887
1888         if (method.coreIndex == -1)
1889             return v8::Undefined();
1890     }
1891
1892     if (method.isV8Function()) {
1893         v8::Handle<v8::Value> rv;
1894         v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1895
1896         QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal, 
1897                                     resource->engine->contextWrapper()->context(qmlglobal),
1898                                     resource->engine);
1899         QDeclarativeV8Function *funcptr = &func;
1900
1901         void *args[] = { 0, &funcptr };
1902         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1903
1904         if (rv.IsEmpty()) return v8::Undefined();
1905         return rv;
1906     }
1907
1908     CallArgs callArgs(argCount, &arguments);
1909     if (!method.isOverload()) {
1910         return CallPrecise(object, method, resource->engine, callArgs);
1911     } else {
1912         return CallOverloaded(object, method, resource->engine, callArgs);
1913     }
1914 }
1915
1916 CallArgument::CallArgument()
1917 : type(QVariant::Invalid)
1918 {
1919 }
1920
1921 CallArgument::~CallArgument()
1922 {
1923     cleanup();
1924 }
1925
1926 void CallArgument::cleanup()
1927 {
1928     if (type == QMetaType::QString) {
1929         qstringPtr->~QString();
1930     } else if (type == -1 || type == QMetaType::QVariant) {
1931         qvariantPtr->~QVariant();
1932     } else if (type == qMetaTypeId<QJSValue>()) {
1933         qjsValuePtr->~QJSValue();
1934     } else if (type == qMetaTypeId<QList<QObject *> >()) {
1935         qlistPtr->~QList<QObject *>();
1936     } 
1937 }
1938
1939 void *CallArgument::dataPtr()
1940 {
1941     if (type == -1)
1942         return qvariantPtr->data();
1943     else
1944         return (void *)&allocData;
1945 }
1946
1947 void CallArgument::initAsType(int callType)
1948 {
1949     if (type != 0) { cleanup(); type = 0; }
1950     if (callType == 0) return;
1951
1952     if (callType == qMetaTypeId<QJSValue>()) {
1953         qjsValuePtr = new (&allocData) QJSValue();
1954         type = callType;
1955     } else if (callType == QMetaType::Int ||
1956                callType == QMetaType::UInt ||
1957                callType == QMetaType::Bool ||
1958                callType == QMetaType::Double ||
1959                callType == QMetaType::Float) {
1960         type = callType;
1961     } else if (callType == QMetaType::QObjectStar) {
1962         qobjectPtr = 0;
1963         type = callType;
1964     } else if (callType == QMetaType::QString) {
1965         qstringPtr = new (&allocData) QString();
1966         type = callType;
1967     } else if (callType == QMetaType::QVariant) {
1968         type = callType;
1969         qvariantPtr = new (&allocData) QVariant();
1970     } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1971         type = callType;
1972         qlistPtr = new (&allocData) QList<QObject *>();
1973     } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1974         type = callType;
1975         handlePtr = new (&allocData) QDeclarativeV8Handle;
1976     } else {
1977         type = -1;
1978         qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
1979     }
1980 }
1981
1982 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1983 {
1984     if (type != 0) { cleanup(); type = 0; }
1985
1986     if (callType == qMetaTypeId<QJSValue>()) {
1987         qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
1988         type = qMetaTypeId<QJSValue>();
1989     } else if (callType == QMetaType::Int) {
1990         intValue = quint32(value->Int32Value());
1991         type = callType;
1992     } else if (callType == QMetaType::UInt) {
1993         intValue = quint32(value->Uint32Value());
1994         type = callType;
1995     } else if (callType == QMetaType::Bool) {
1996         boolValue = value->BooleanValue();
1997         type = callType;
1998     } else if (callType == QMetaType::Double) {
1999         doubleValue = double(value->NumberValue());
2000         type = callType;
2001     } else if (callType == QMetaType::Float) {
2002         floatValue = float(value->NumberValue());
2003         type = callType;
2004     } else if (callType == QMetaType::QString) {
2005         if (value->IsNull() || value->IsUndefined())
2006             qstringPtr = new (&allocData) QString();
2007         else
2008             qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2009         type = callType;
2010     } else if (callType == QMetaType::QObjectStar) {
2011         qobjectPtr = engine->toQObject(value);
2012         type = callType;
2013     } else if (callType == qMetaTypeId<QVariant>()) {
2014         qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2015         type = callType;
2016     } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2017         qlistPtr = new (&allocData) QList<QObject *>();
2018         if (value->IsArray()) {
2019             v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2020             uint32_t length = array->Length();
2021             for (uint32_t ii = 0; ii < length; ++ii) 
2022                 qlistPtr->append(engine->toQObject(array->Get(ii)));
2023         } else {
2024             qlistPtr->append(engine->toQObject(value));
2025         }
2026         type = callType;
2027     } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
2028         handlePtr = new (&allocData) QDeclarativeV8Handle(QDeclarativeV8Handle::fromHandle(value));
2029         type = callType;
2030     } else {
2031         qvariantPtr = new (&allocData) QVariant();
2032         type = -1;
2033
2034         QDeclarativeEnginePrivate *ep = engine->engine() ? QDeclarativeEnginePrivate::get(engine->engine()) : 0;
2035         QVariant v = engine->toVariant(value, -1);
2036
2037         if (v.userType() == callType) {
2038             *qvariantPtr = v;
2039         } else if (v.canConvert((QVariant::Type)callType)) {
2040             *qvariantPtr = v;
2041             qvariantPtr->convert((QVariant::Type)callType);
2042         } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
2043             QObject *obj = ep->toQObject(v);
2044             
2045             if (obj) {
2046                 const QMetaObject *objMo = obj->metaObject();
2047                 while (objMo && objMo != mo) objMo = objMo->superClass();
2048                 if (!objMo) obj = 0;
2049             }
2050
2051             *qvariantPtr = QVariant(callType, &obj);
2052         } else {
2053             *qvariantPtr = QVariant(callType, (void *)0);
2054         }
2055     }
2056 }
2057
2058 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2059 {
2060     if (type == qMetaTypeId<QJSValue>()) {
2061         return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2062     } else if (type == QMetaType::Int) {
2063         return v8::Integer::New(int(intValue));
2064     } else if (type == QMetaType::UInt) {
2065         return v8::Integer::NewFromUnsigned(intValue);
2066     } else if (type == QMetaType::Bool) {
2067         return v8::Boolean::New(boolValue);
2068     } else if (type == QMetaType::Double) {
2069         return v8::Number::New(doubleValue);
2070     } else if (type == QMetaType::Float) {
2071         return v8::Number::New(floatValue);
2072     } else if (type == QMetaType::QString) {
2073         return engine->toString(*qstringPtr);
2074     } else if (type == QMetaType::QObjectStar) {
2075         QObject *object = qobjectPtr;
2076         if (object)
2077             QDeclarativeData::get(object, true)->setImplicitDestructible();
2078         return engine->newQObject(object);
2079     } else if (type == qMetaTypeId<QList<QObject *> >()) {
2080         // XXX Can this be made more by using Array as a prototype and implementing
2081         // directly against QList<QObject*>?
2082         QList<QObject *> &list = *qlistPtr;
2083         v8::Local<v8::Array> array = v8::Array::New(list.count());
2084         for (int ii = 0; ii < list.count(); ++ii) 
2085             array->Set(ii, engine->newQObject(list.at(ii)));
2086         return array;
2087     } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
2088         return handlePtr->toHandle();
2089     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2090         QVariant value = *qvariantPtr;
2091         v8::Handle<v8::Value> rv = engine->fromVariant(value);
2092         if (QObject *object = engine->toQObject(rv)) 
2093             QDeclarativeData::get(object, true)->setImplicitDestructible();
2094         return rv;
2095     } else {
2096         return v8::Undefined();
2097     }
2098 }
2099
2100 QT_END_NAMESPACE
2101