Use optimized QObject wrapper whenever it is possible
[profile/ivi/qtdeclarative.git] / src / declarative / qml / v8 / qv8qobjectwrapper.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
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
52 #include <QtScript/qscriptvalue.h>
53 #include <QtCore/qvarlengtharray.h>
54 #include <QtCore/qtimer.h>
55 #include <QtCore/qatomic.h>
56
57 QT_BEGIN_NAMESPACE
58
59 Q_DECLARE_METATYPE(QScriptValue);
60 Q_DECLARE_METATYPE(QDeclarativeV8Handle);
61
62 #if defined(__GNUC__)
63 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
64 // The code in this file does not violate strict aliasing, but GCC thinks it does
65 // so turn off the warnings for us to have a clean build
66 #  pragma GCC diagnostic ignored "-Wstrict-aliasing"
67 # endif
68 #endif
69
70 #define QOBJECT_TOSTRING_INDEX -2
71 #define QOBJECT_DESTROY_INDEX -3
72
73 // XXX TODO: Need to review all calls to QDeclarativeEngine *engine() to confirm QObjects work
74 // correctly in a worker thread
75
76 class QV8QObjectResource : public QV8ObjectResource
77 {
78     V8_RESOURCE_TYPE(QObjectType);
79
80 public:
81     QV8QObjectResource(QV8Engine *engine, QObject *object);
82
83     QDeclarativeGuard<QObject> object;
84 };
85
86 class QV8QObjectInstance : public QDeclarativeGuard<QObject>
87 {
88 public:
89     QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
90     : QDeclarativeGuard<QObject>(o), wrapper(w)
91     {
92     }
93
94     ~QV8QObjectInstance()
95     {
96         qPersistentDispose(v8object);
97     }
98
99     virtual void objectDestroyed(QObject *o)
100     {
101         if (wrapper)
102             wrapper->m_taintedObjects.remove(o);
103         delete this;
104     }
105
106     v8::Persistent<v8::Object> v8object;
107     QV8QObjectWrapper *wrapper;
108 };
109
110 namespace {
111 struct MetaCallArgument {
112     inline MetaCallArgument();
113     inline ~MetaCallArgument();
114     inline void *dataPtr();
115
116     inline void initAsType(int type);
117     inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
118     inline v8::Handle<v8::Value> toValue(QV8Engine *);
119
120 private:
121     MetaCallArgument(const MetaCallArgument &);
122
123     inline void cleanup();
124
125     char data[4 * sizeof(void *)];
126     int type;
127     bool isObjectType;
128 };
129 }
130
131 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object) 
132 : QV8ObjectResource(engine), object(object) 
133 {
134 }
135
136 static QAtomicInt objectIdCounter(1);
137
138 QV8QObjectWrapper::QV8QObjectWrapper()
139 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
140 {
141 }
142
143 QV8QObjectWrapper::~QV8QObjectWrapper()
144 {
145     for (TaintedHash::Iterator iter = m_taintedObjects.begin(); 
146          iter != m_taintedObjects.end();
147          ++iter) {
148         (*iter)->wrapper = 0;
149     }
150     m_taintedObjects.clear();
151 }
152
153 void QV8QObjectWrapper::destroy()
154 {
155     qDeleteAll(m_connections);
156     m_connections.clear();
157
158     qPersistentDispose(m_hiddenObject);
159     qPersistentDispose(m_destroySymbol);
160     qPersistentDispose(m_toStringSymbol);
161     qPersistentDispose(m_methodConstructor);
162     qPersistentDispose(m_constructor);
163 }
164
165 #define FAST_VALUE_GETTER(name, cpptype, defaultvalue, constructor) \
166 static v8::Handle<v8::Value> name ## ValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) \
167 { \
168     v8::Handle<v8::Object> This = info.This(); \
169     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(This); \
170  \
171     if (!resource || resource->object.isNull()) return v8::Undefined(); \
172  \
173     QObject *object = resource->object; \
174  \
175     uint32_t data = info.Data()->Uint32Value(); \
176     int index = data & 0x7FFF; \
177     int notify = (data & 0x7FFF0000) >> 16; \
178     if (notify == 0x7FFF) notify = -1; \
179  \
180     QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
181     if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
182         typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
183         ep->capturedProperties << CapturedProperty(object, index, notify); \
184     } \
185  \
186     cpptype value = defaultvalue; \
187     void *args[] = { &value, 0 }; \
188     QMetaObject::metacall(object, QMetaObject::ReadProperty, index, args); \
189  \
190     return constructor(value); \
191
192
193 #define CREATE_FUNCTION \
194     "(function(method) { "\
195         "return (function(object, data, qmlglobal) { "\
196             "return (function() { "\
197                 "return method(object, data, qmlglobal, arguments.length, arguments); "\
198             "});"\
199         "});"\
200     "})"
201
202 void QV8QObjectWrapper::init(QV8Engine *engine)
203 {
204     m_engine = engine;
205
206     m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
207     m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
208     m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
209
210     {
211     v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
212     ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
213     ft->InstanceTemplate()->SetHasExternalResource(true);
214     m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
215     }
216     {
217     v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
218     v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin);
219     v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
220     v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
221     v8::Handle<v8::Value> args[] = { invokeFn };
222     v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
223     m_methodConstructor = qPersistentNew<v8::Function>(createFn);
224     }
225
226     {
227     v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
228     prototype->Set(v8::String::New("connect"), V8FUNCTION(Connect, engine));
229     prototype->Set(v8::String::New("disconnect"), V8FUNCTION(Disconnect, engine));
230     }
231 }
232
233 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
234 {
235     return v8_resource_cast<QV8QObjectResource>(obj) != 0;
236 }
237
238 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
239 {
240     QV8QObjectResource *r =  v8_resource_cast<QV8QObjectResource>(obj);
241     return r?r->object:0;
242 }
243
244 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
245 QObject *QV8QObjectWrapper::QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
246 {
247     Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
248     return static_cast<QV8QObjectResource *>(r)->object;
249 }
250
251 // Load value properties
252 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object, 
253                                           const QDeclarativePropertyCache::Data &property)
254 {
255     Q_ASSERT(!property.isFunction());
256
257 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
258     if (property.propType == QMetaType:: metatype) { \
259         cpptype type = cpptype(); \
260         void *args[] = { &type, 0 }; \
261         QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \
262         return constructor(type); \
263     }
264
265     if (property.isQObject()) {
266         QObject *rv = 0;
267         void *args[] = { &rv, 0 };
268         QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
269         return engine->newQObject(rv);
270     } else if (property.isQList()) {
271         return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
272     } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
273     else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Number::New)
274     else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
275     else PROPERTY_LOAD(QString, QString, engine->toString)
276     else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
277     else PROPERTY_LOAD(Float, float, v8::Number::New)
278     else PROPERTY_LOAD(Double, double, v8::Number::New)
279     else if(property.isV8Handle()) {
280         QDeclarativeV8Handle handle;
281         void *args[] = { &handle, 0 };
282         QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); 
283         return reinterpret_cast<v8::Handle<v8::Value> &>(handle);
284     } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)) {
285         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
286         QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
287         if (valueType)
288             return engine->newValueType(object, property.coreIndex, valueType);
289     } 
290
291     QVariant var = object->metaObject()->property(property.coreIndex).read(object);
292     return engine->fromVariant(var);
293 }
294
295 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object, 
296                                                      v8::Handle<v8::Value> *objectHandle, 
297                                                      v8::Handle<v8::String> property,
298                                                      QV8QObjectWrapper::RevisionMode revisionMode)
299 {
300     // XXX More recent versions of V8 introduced "Callable" objects.  It is possible that these
301     // will be a faster way of creating QObject method objects.
302     struct MethodClosure {
303        static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object, 
304                                            v8::Handle<v8::Value> *objectHandle, 
305                                            int index) { 
306            v8::Handle<v8::Value> argv[] = { 
307                objectHandle?*objectHandle:engine->newQObject(object),
308                v8::Integer::New(index)
309            };
310            return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
311        }
312        static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object, 
313                                                      v8::Handle<v8::Value> *objectHandle, 
314                                                      int index) { 
315            v8::Handle<v8::Value> argv[] = { 
316                objectHandle?*objectHandle:engine->newQObject(object),
317                v8::Integer::New(index),
318                v8::Context::GetCallingQmlGlobal()
319            };
320            return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
321        }
322     };
323
324     if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property)) {
325         return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
326     } else if (engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property)) {
327         return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
328     }
329
330     QDeclarativePropertyCache::Data local;
331     QDeclarativePropertyCache::Data *result = 0;
332     result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
333
334     if (!result)
335         return v8::Handle<v8::Value>();
336
337     QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
338
339     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
340         QDeclarativeData *ddata = QDeclarativeData::get(object);
341         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
342             return v8::Handle<v8::Value>();
343     }
344
345     typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
346
347     if (result->isFunction()) {
348         if (result->flags & QDeclarativePropertyCache::Data::IsVMEFunction) {
349             return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
350         } else if (result->flags & QDeclarativePropertyCache::Data::IsV8Function) {
351             return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
352         } else {
353             return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
354         }
355     }
356
357     if (ep && ep->captureProperties && !result->isConstant()) {
358         if (result->coreIndex == 0)
359             ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
360         else
361             ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
362     }
363
364     return LoadProperty(engine, object, *result);
365 }
366
367 // Setter for writable properties.  Shared between the interceptor and fast property accessor
368 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property,
369                                  v8::Handle<v8::Value> value)
370 {
371     QDeclarativeBinding *newBinding = 0;
372
373     if (value->IsFunction()) {
374         QDeclarativeContextData *context = engine->callingContext();
375         v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
376
377         v8::Local<v8::StackTrace> trace = 
378             v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | 
379                                                                                      v8::StackTrace::kScriptName));
380         v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
381         int lineNumber = frame->GetLineNumber();
382         QString url = engine->toString(frame->GetScriptName());
383
384         QDeclarativePropertyCache::ValueTypeData valueTypeData;
385         newBinding = new QDeclarativeBinding(&function, object, context);
386         newBinding->setSourceLocation(url, lineNumber);
387         newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context));
388         newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
389     }
390
391     QDeclarativeAbstractBinding *oldBinding = 
392         QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
393     if (oldBinding)
394         oldBinding->destroy();
395
396 #define PROPERTY_STORE(cpptype, value) \
397     cpptype o = value; \
398     int status = -1; \
399     int flags = 0; \
400     void *argv[] = { &o, 0, &status, &flags }; \
401     QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
402
403
404     if (value->IsNull() && property->isQObject()) {
405         PROPERTY_STORE(QObject*, 0);
406     } else if (value->IsUndefined() && property->isResettable()) {
407         void *a[] = { 0 };
408         QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
409     } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
410         PROPERTY_STORE(QVariant, QVariant());
411     } else if (value->IsUndefined()) {
412         QString error = QLatin1String("Cannot assign [undefined] to ") +
413                         QLatin1String(QMetaType::typeName(property->propType));
414         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
415     } else if (value->IsFunction()) {
416         // this is handled by the binding creation above
417     } else if (property->propType == QMetaType::Int && value->IsNumber()) {
418         PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
419     } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
420         PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
421     } else if (property->propType == QMetaType::Float && value->IsNumber()) {
422         PROPERTY_STORE(float, float(value->ToNumber()->Value()));
423     } else if (property->propType == QMetaType::Double && value->IsNumber()) {
424         PROPERTY_STORE(double, double(value->ToNumber()->Value()));
425     } else if (property->propType == QMetaType::QString && value->IsString()) {
426         PROPERTY_STORE(QString, engine->toString(value->ToString()));
427     } else {
428         QVariant v;
429         if (property->isQList()) 
430             v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
431         else
432             v = engine->toVariant(value, property->propType);
433
434         QDeclarativeContextData *context = engine->callingContext();
435
436         if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
437             const char *valueType = 0;
438             if (v.userType() == QVariant::Invalid) valueType = "null";
439             else valueType = QMetaType::typeName(v.userType());
440
441             QString error = QLatin1String("Cannot assign ") +
442                             QLatin1String(valueType) +
443                             QLatin1String(" to ") +
444                             QLatin1String(QMetaType::typeName(property->propType));
445             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
446         }
447     }
448 }
449
450 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, v8::Handle<v8::String> property,
451                                     v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
452 {
453     if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property) ||
454         engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property))
455         return true;
456
457     QDeclarativePropertyCache::Data local;
458     QDeclarativePropertyCache::Data *result = 0;
459     result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
460
461     if (!result)
462         return false;
463
464     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
465         QDeclarativeData *ddata = QDeclarativeData::get(object);
466         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
467             return false;
468     }
469
470     if (!result->isWritable() && !result->isQList()) {
471         QString error = QLatin1String("Cannot assign to read-only property \"") +
472                         engine->toString(property) + QLatin1Char('\"');
473         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
474         return true;
475     }
476
477     StoreProperty(engine, object, result, value);
478
479     return true;
480 }
481
482 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property, 
483                                                 const v8::AccessorInfo &info)
484 {
485     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
486     v8::Handle<v8::Value> This = info.This();
487
488     if (!resource || resource->object.isNull()) return v8::Undefined();
489
490     QObject *object = resource->object;
491
492     QV8Engine *v8engine = resource->engine;
493     v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, property, QV8QObjectWrapper::IgnoreRevision);
494     if (!result.IsEmpty())
495         return result;
496
497     if (QV8Engine::startsWithUpper(property)) {
498         // Check for attached properties
499         QDeclarativeContextData *context = v8engine->callingContext();
500         QDeclarativeTypeNameCache::Data *data = context && (context->imports)?context->imports->data(property):0;
501
502         if (data) {
503             if (data->type) {
504                 return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums);
505             } else if (data->typeNamespace) {
506                 return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
507             }
508         }
509     } 
510
511     return v8::Undefined();
512 }
513
514 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property, 
515                                                 v8::Local<v8::Value> value,
516                                                 const v8::AccessorInfo &info)
517 {
518     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
519
520     if (!resource || resource->object.isNull()) 
521         return value;
522
523     QObject *object = resource->object;
524
525     QV8Engine *v8engine = resource->engine;
526     bool result = SetProperty(v8engine, object, property, value, QV8QObjectWrapper::IgnoreRevision);
527
528     if (!result) {
529         QString error = QLatin1String("Cannot assign to non-existent property \"") +
530                         v8engine->toString(property) + QLatin1Char('\"');
531         v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
532         return value;
533     }
534
535     return value;
536 }
537
538 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
539                                                  const v8::AccessorInfo &info)
540 {
541     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
542
543     if (!resource || resource->object.isNull()) 
544         return v8::Handle<v8::Integer>();
545
546     QV8Engine *engine = resource->engine;
547     QObject *object = resource->object;
548
549     QDeclarativePropertyCache::Data local;
550     QDeclarativePropertyCache::Data *result = 0;
551     result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
552
553     if (!result)
554         return v8::Handle<v8::Integer>();
555     else if (!result->isWritable() && !result->isQList())
556         return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
557     else
558         return v8::Integer::New(v8::DontDelete);
559 }
560
561 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
562 {
563     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
564
565     if (!resource || resource->object.isNull()) 
566         return v8::Array::New();
567
568     QObject *object = resource->object;
569
570     QStringList result;
571
572     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine());
573
574     QDeclarativePropertyCache *cache = 0;
575     QDeclarativeData *ddata = QDeclarativeData::get(object);
576     if (ddata)
577         cache = ddata->propertyCache;
578
579     if (!cache) {
580         cache = ep->cache(object);
581         if (cache) {
582             if (ddata) { cache->addref(); ddata->propertyCache = cache; }
583         } else {
584             // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
585             const QMetaObject *mo = object->metaObject();
586             int pc = mo->propertyCount();
587             int po = mo->propertyOffset();
588             for (int i=po; i<pc; ++i)
589                 result << QString::fromUtf8(mo->property(i).name());
590         }
591     } else {
592         result = cache->propertyNames();
593     }
594
595     v8::Local<v8::Array> rv = v8::Array::New(result.count());
596
597     for (int ii = 0; ii < result.count(); ++ii) 
598         rv->Set(ii, resource->engine->toString(result.at(ii)));
599
600     return rv;
601 }
602
603 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
604 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
605 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
606 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
607 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
608 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
609 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
610
611 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
612                             const v8::AccessorInfo& info)
613 {
614     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
615
616     if (!resource || resource->object.isNull()) 
617         return; 
618
619     QObject *object = resource->object;
620
621     uint32_t data = info.Data()->Uint32Value(); 
622     int index = data & 0x7FFF;  // So that we can use the same data for Setter and Getter
623
624     QDeclarativeData *ddata = QDeclarativeData::get(object, false);
625     Q_ASSERT(ddata);
626     Q_ASSERT(ddata->propertyCache);
627
628     QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index);
629     Q_ASSERT(pdata);
630
631     Q_ASSERT(pdata->isWritable() || pdata->isQList());
632
633     StoreProperty(resource->engine, object, pdata, value);
634 }
635
636 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
637 {
638     Q_ASSERT(handle->IsObject());
639     
640     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(handle->ToObject());
641
642     Q_ASSERT(resource);
643
644     QObject *object = resource->object;
645     if (object) {
646         QDeclarativeData *ddata = QDeclarativeData::get(object, false);
647         if (ddata) {
648             ddata->v8object.Clear();
649             if (!object->parent() && !ddata->indestructible)
650                 object->deleteLater();
651         }
652     }
653
654     qPersistentDispose(handle);
655 }
656
657 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
658 {
659     QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
660     instance->v8object.Clear();
661     qPersistentDispose(handle);
662 }
663
664 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
665 {
666     Q_ASSERT(object);
667
668     Q_ASSERT(QDeclarativeData::get(object, false));
669     Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
670
671     // Setup constructor
672     if (constructor.IsEmpty()) {
673         v8::Local<v8::FunctionTemplate> ft;
674
675         QString toString = QLatin1String("toString");
676         QString destroy = QLatin1String("destroy");
677
678         // XXX TODO: Enables fast property accessors.  These more than double the property access 
679         // performance, but the  cost of setting up this structure hasn't been measured so 
680         // its not guarenteed that this is a win overall.  We need to try and measure the cost.
681         for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
682             Data *property = *iter;
683             if (property->isFunction() || !property->isWritable() ||
684                 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x7FFF || 
685                 property->coreIndex == 0)
686                 continue;
687
688             v8::AccessorGetter fastgetter = 0;
689
690             if (property->isQObject()) 
691                 fastgetter = QObjectValueGetter;
692             else if (property->propType == QMetaType::Int || property->isEnum()) 
693                 fastgetter = IntValueGetter;
694             else if (property->propType == QMetaType::Bool)
695                 fastgetter = BoolValueGetter;
696             else if (property->propType == QMetaType::QString)
697                 fastgetter = QStringValueGetter;
698             else if (property->propType == QMetaType::UInt)
699                 fastgetter = UIntValueGetter;
700             else if (property->propType == QMetaType::Float) 
701                 fastgetter = FloatValueGetter;
702             else if (property->propType == QMetaType::Double) 
703                 fastgetter = DoubleValueGetter;
704
705             if (fastgetter) {
706                 int notifyIndex = property->notifyIndex;
707                 if (property->isConstant()) notifyIndex = 0;
708                 else if (notifyIndex == -1) notifyIndex = 0x7FFF;
709                 uint32_t data = (property->notifyIndex & 0x7FFF) << 16 | property->coreIndex;
710
711                 QString name = iter.key();
712                 if (name == toString || name == destroy)
713                     continue;
714
715                 if (ft.IsEmpty()) {
716                     ft = v8::FunctionTemplate::New();
717                     ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
718                                                                        QV8QObjectWrapper::Setter,
719                                                                        QV8QObjectWrapper::Query, 
720                                                                        0,
721                                                                        QV8QObjectWrapper::Enumerator);
722                     ft->InstanceTemplate()->SetHasExternalResource(true);
723                 }
724
725                 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, FastValueSetter,
726                                                     v8::Integer::NewFromUnsigned(data));
727             }
728         }
729
730         if (ft.IsEmpty()) {
731             constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
732         } else {
733             ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
734                                                                QV8QObjectWrapper::Setter,
735                                                                QV8QObjectWrapper::Query, 
736                                                                0,
737                                                                QV8QObjectWrapper::Enumerator);
738             ft->InstanceTemplate()->SetHasExternalResource(true);
739             constructor = qPersistentNew<v8::Function>(ft->GetFunction());
740         }
741     }
742
743     v8::Local<v8::Object> result = constructor->NewInstance();
744     QV8QObjectResource *r = new QV8QObjectResource(engine, object);
745     result->SetExternalResource(r);
746     return result;
747 }
748
749 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
750 {
751     v8::Local<v8::Object> rv;
752
753     if (!ddata->propertyCache && engine->engine()) {
754         ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
755         if (ddata->propertyCache) ddata->propertyCache->addref();
756     }
757
758     if (ddata->propertyCache) {
759         rv = ddata->propertyCache->newQObject(object, engine);
760     } else {
761         // XXX NewInstance() should be optimized
762         rv = m_constructor->NewInstance(); 
763         QV8QObjectResource *r = new QV8QObjectResource(engine, object);
764         rv->SetExternalResource(r);
765     }
766
767     return rv;
768 }
769
770 /*
771 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
772 V8 handle for subsequent calls to newQObject for the same QObject.  To do this we have a two
773 pronged strategy:
774    1. If there is no current outstanding V8 handle to the QObject, we create one and store a 
775       persistent handle in QDeclarativeData::v8object.  We mark the QV8QObjectWrapper that 
776       "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this 
777       QV8QObjectWrapper.
778    2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create 
779       an entry in the m_taintedObject hash where we store the handle and mark the object as 
780       "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
781 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
782 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has 
783 released the handle.
784 */
785 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
786 {
787     if (!object)
788         return v8::Null();
789
790     if (QObjectPrivate::get(object)->wasDeleted)
791        return v8::Undefined();
792     
793     QDeclarativeData *ddata = QDeclarativeData::get(object, true);
794
795     if (!ddata) 
796         return v8::Undefined();
797
798     if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
799         // We own the v8object 
800         return v8::Local<v8::Object>::New(ddata->v8object);
801     } else if (ddata->v8object.IsEmpty() && 
802                (ddata->v8objectid == m_id || // We own the QObject
803                 ddata->v8objectid == 0 ||    // No one owns the QObject
804                 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
805
806         v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
807         ddata->v8object = qPersistentNew<v8::Object>(rv);
808         ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
809         ddata->v8objectid = m_id;
810         return rv;
811
812     } else {
813         // If this object is tainted, we have to check to see if it is in our
814         // tainted object list
815         TaintedHash::Iterator iter =
816             ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
817         bool found = iter != m_taintedObjects.end();
818
819         // If our tainted handle doesn't exist or has been collected, and there isn't
820         // a handle in the ddata, we can assume ownership of the ddata->v8object
821         if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
822             v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
823             ddata->v8object = qPersistentNew<v8::Object>(rv);
824             ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
825             ddata->v8objectid = m_id;
826
827             if (found) {
828                 delete (*iter);
829                 m_taintedObjects.erase(iter);
830             }
831
832             return rv;
833         } else if (!found) {
834             QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
835             iter = m_taintedObjects.insert(object, instance);
836             ddata->hasTaintedV8Object = true;
837         }
838
839         if ((*iter)->v8object.IsEmpty()) {
840             v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
841             (*iter)->v8object = qPersistentNew<v8::Object>(rv);
842             (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
843         }
844
845         return v8::Local<v8::Object>::New((*iter)->v8object);
846     }
847 }
848
849 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
850 {
851     v8::ScriptOrigin origin = function->GetScriptOrigin();
852     if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
853
854         // This is one of our special QObject method wrappers
855         v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
856         v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
857
858         if (data->IsArray()) {
859             v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
860             return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
861         } 
862
863         // In theory this can't fall through, but I suppose V8 might run out of memory or something
864     }
865
866     return qMakePair((QObject *)0, -1);
867 }
868
869 struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
870 {
871     QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
872     ~QV8QObjectConnectionList();
873
874     struct Connection {
875         Connection() 
876         : needsDestroy(false) {}
877         Connection(const Connection &other) 
878         : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
879         Connection &operator=(const Connection &other) {
880             thisObject = other.thisObject;
881             function = other.function;
882             needsDestroy = other.needsDestroy;
883             return *this;
884         }
885
886         v8::Persistent<v8::Object> thisObject;
887         v8::Persistent<v8::Function> function;
888
889         void dispose() {
890             qPersistentDispose(thisObject);
891             qPersistentDispose(function);
892         }
893
894         bool needsDestroy;
895     };
896
897     struct ConnectionList : public QList<Connection> {
898         ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
899         int connectionsInUse;
900         bool connectionsNeedClean;
901     };
902
903     QV8Engine *engine;
904
905     typedef QHash<int, ConnectionList> SlotHash;
906     SlotHash slotHash;
907     bool needsDestroy;
908     int inUse;
909
910     virtual void objectDestroyed(QObject *);
911     virtual int qt_metacall(QMetaObject::Call, int, void **);
912 };
913
914 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
915 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
916 {
917 }
918
919 QV8QObjectConnectionList::~QV8QObjectConnectionList()
920 {
921     for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
922         QList<Connection> &connections = *iter;
923         for (int ii = 0; ii < connections.count(); ++ii) {
924             qPersistentDispose(connections[ii].thisObject);
925             qPersistentDispose(connections[ii].function);
926         }
927     }
928     slotHash.clear();
929 }
930
931 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
932 {
933     engine->qobjectWrapper()->m_connections.remove(object);
934
935     if (inUse)
936         needsDestroy = true;
937     else
938         delete this;
939 }
940
941 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
942 {
943     if (method == QMetaObject::InvokeMetaMethod) {
944         SlotHash::Iterator iter = slotHash.find(index);
945         if (iter == slotHash.end())
946             return -1;
947         ConnectionList &connectionList = *iter;
948         if (connectionList.isEmpty())
949             return -1;
950
951         inUse++;
952
953         connectionList.connectionsInUse++;
954
955         QList<Connection> connections = connectionList;
956
957         QMetaMethod method = data()->metaObject()->method(index);
958         Q_ASSERT(method.methodType() == QMetaMethod::Signal);
959         // XXX TODO: We should figure out a way to cache the parameter types to avoid resolving
960         // them each time.
961         QList<QByteArray> params = method.parameterTypes();
962
963         v8::HandleScope handle_scope;
964         v8::Context::Scope scope(engine->context());
965
966         QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
967         int argCount = params.count();
968
969         for (int ii = 0; ii < argCount; ++ii) {
970             int type = QMetaType::type(params.at(ii).constData());
971             if (type == qMetaTypeId<QVariant>()) {
972                 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
973             } else {
974                 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
975             }
976         }
977
978         for (int ii = 0; ii < connections.count(); ++ii) {
979             Connection &connection = connections[ii];
980             if (connection.needsDestroy)
981                 continue;
982             if (connection.thisObject.IsEmpty()) {
983                 connection.function->Call(engine->global(), argCount, args.data());
984             } else {
985                 connection.function->Call(connection.thisObject, argCount, args.data());
986             }
987         }
988
989         connectionList.connectionsInUse--;
990         if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
991             for (QList<Connection>::Iterator iter = connectionList.begin(); 
992                  iter != connectionList.end(); ) {
993                 if (iter->needsDestroy) {
994                     iter->dispose();
995                     iter = connectionList.erase(iter);
996                 } else {
997                     ++iter;
998                 }
999             }
1000         }
1001
1002         inUse--;
1003         if (inUse == 0 && needsDestroy)
1004             delete this;
1005     } 
1006
1007     return -1;
1008 }
1009
1010 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1011 {
1012     if (args.Length() == 0)
1013         V8THROW_ERROR("Function.prototype.connect: no arguments given");
1014
1015     QV8Engine *engine = V8ENGINE();
1016
1017     if (!args.This()->IsFunction())
1018         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1019
1020     QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1021     QObject *signalObject = signalInfo.first;
1022     int signalIndex = signalInfo.second;
1023
1024     if (signalIndex == -1)
1025         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1026
1027     if (!signalObject)
1028         V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1029
1030     if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1031         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1032
1033     v8::Local<v8::Value> functionValue;
1034     v8::Local<v8::Value> functionThisValue;
1035
1036     if (args.Length() == 1) {
1037         functionValue = args[0];
1038     } else {
1039         functionThisValue = args[0];
1040         functionValue = args[1];
1041     }
1042
1043     if (!functionValue->IsFunction())
1044         V8THROW_ERROR("Function.prototype.connect: target is not a function");
1045
1046     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1047         V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1048
1049     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1050     QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1051     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1052     if (iter == connections.end()) 
1053         iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1054
1055     QV8QObjectConnectionList *connectionList = *iter;
1056     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1057     if (slotIter == connectionList->slotHash.end()) {
1058         slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1059         QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1060     }
1061
1062     QV8QObjectConnectionList::Connection connection;
1063     if (!functionThisValue.IsEmpty()) 
1064         connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1065     connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1066
1067     slotIter->append(connection);
1068
1069     return v8::Undefined();
1070 }
1071
1072 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1073 {
1074     if (args.Length() == 0)
1075         V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1076
1077     QV8Engine *engine = V8ENGINE();
1078
1079     if (!args.This()->IsFunction())
1080         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1081
1082     QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1083     QObject *signalObject = signalInfo.first;
1084     int signalIndex = signalInfo.second;
1085
1086     if (signalIndex == -1)
1087         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1088
1089     if (!signalObject)
1090         V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1091
1092     if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1093         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1094
1095     v8::Local<v8::Value> functionValue;
1096     v8::Local<v8::Value> functionThisValue;
1097
1098     if (args.Length() == 1) {
1099         functionValue = args[0];
1100     } else {
1101         functionThisValue = args[0];
1102         functionValue = args[1];
1103     }
1104
1105     if (!functionValue->IsFunction())
1106         V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1107
1108     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1109         V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1110
1111     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1112     QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1113     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1114     if (iter == connectionsList.end()) 
1115         return v8::Undefined(); // Nothing to disconnect from
1116
1117     QV8QObjectConnectionList *connectionList = *iter;
1118     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1119     if (slotIter == connectionList->slotHash.end()) 
1120         return v8::Undefined(); // Nothing to disconnect from
1121
1122     QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1123
1124     v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1125     QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1126
1127     if (functionData.second != -1) {
1128         // This is a QObject function wrapper
1129         for (int ii = 0; ii < connections.count(); ++ii) {
1130             QV8QObjectConnectionList::Connection &connection = connections[ii];
1131
1132             if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1133                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1134
1135                 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1136                 if (connectedFunctionData == functionData) {
1137                     // Match!
1138                     if (connections.connectionsInUse) {
1139                         connection.needsDestroy = true;
1140                     } else {
1141                         connection.dispose();
1142                         connections.removeAt(ii);
1143                     }
1144                     return v8::Undefined();
1145                 }
1146             }
1147         }
1148
1149     } else {
1150         // This is a normal JS function
1151         for (int ii = 0; ii < connections.count(); ++ii) {
1152             QV8QObjectConnectionList::Connection &connection = connections[ii];
1153             if (connection.function->StrictEquals(function) &&
1154                 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1155                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1156                 // Match!
1157                 if (connections.connectionsInUse) {
1158                     connection.needsDestroy = true;
1159                 } else {
1160                     connection.dispose();
1161                     connections.removeAt(ii);
1162                 }
1163                 return v8::Undefined();
1164             }
1165         }
1166     }
1167
1168     return v8::Undefined();
1169 }
1170
1171 /*!
1172     Get the \a property of \a object.  Returns an empty handle if the property doesn't exist.
1173
1174     Only searches for real properties of \a object (including methods), not attached properties etc.
1175 */
1176 v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, v8::Handle<v8::String> property,
1177                                                      QV8QObjectWrapper::RevisionMode revisionMode)
1178 {
1179     return GetProperty(m_engine, object, 0, property, revisionMode);
1180 }
1181
1182 /*
1183     Set the \a property of \a object to \a value.
1184
1185     Returns true if the property was "set" - even if this results in an exception being thrown -
1186     and false if the object has no such property.
1187
1188     Only searches for real properties of \a object (including methods), not attached properties etc.
1189 */
1190 bool QV8QObjectWrapper::setProperty(QObject *object, v8::Handle<v8::String> property, 
1191                                     v8::Handle<v8::Value> value, RevisionMode revisionMode)
1192 {
1193     return SetProperty(m_engine, object, property, value, revisionMode);
1194 }
1195
1196 namespace {
1197 struct CallArgs
1198 {
1199     CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1200     int Length() const { return _length; }
1201     v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1202
1203 private:
1204     int _length;
1205     v8::Handle<v8::Object> *_args;
1206 };
1207 }
1208
1209 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount, 
1210                                         int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1211 {
1212     if (argCount > 0) {
1213
1214         QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1215         args[0].initAsType(returnType);
1216
1217         for (int ii = 0; ii < argCount; ++ii)
1218             args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1219
1220         QVarLengthArray<void *, 9> argData(args.count());
1221         for (int ii = 0; ii < args.count(); ++ii)
1222             argData[ii] = args[ii].dataPtr();
1223
1224         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1225
1226         return args[0].toValue(engine);
1227
1228     } else if (returnType != 0) {
1229         
1230         MetaCallArgument arg;
1231         arg.initAsType(returnType);
1232
1233         void *args[] = { arg.dataPtr() };
1234
1235         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1236
1237         return arg.toValue(engine);
1238
1239     } else {
1240
1241         void *args[] = { 0 };
1242         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1243         return v8::Undefined();
1244
1245     }
1246 }
1247
1248 static int EnumType(const QMetaObject *meta, const QString &strname)
1249 {
1250     QByteArray str = strname.toUtf8();
1251     QByteArray scope;
1252     QByteArray name;
1253     int scopeIdx = str.lastIndexOf("::");
1254     if (scopeIdx != -1) {
1255         scope = str.left(scopeIdx);
1256         name = str.mid(scopeIdx + 2);
1257     } else { 
1258         name = str;
1259     }
1260     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1261         QMetaEnum m = meta->enumerator(i);
1262         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1263             return QVariant::Int;
1264     }
1265     return QVariant::Invalid;
1266 }
1267
1268 /*!
1269     Returns the match score for converting \a actual to be of type \a conversionType.  A 
1270     zero score means "perfect match" whereas a higher score is worse.
1271
1272     The conversion table is copied out of the QtScript callQtMethod() function.
1273 */
1274 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType, 
1275                       const QByteArray &conversionTypeName)
1276 {
1277     if (actual->IsNumber()) {
1278         switch (conversionType) {
1279         case QMetaType::Double:
1280             return 0;
1281         case QMetaType::Float:
1282             return 1;
1283         case QMetaType::LongLong:
1284         case QMetaType::ULongLong:
1285             return 2;
1286         case QMetaType::Long:
1287         case QMetaType::ULong:
1288             return 3;
1289         case QMetaType::Int:
1290         case QMetaType::UInt:
1291             return 4;
1292         case QMetaType::Short:
1293         case QMetaType::UShort:
1294             return 5;
1295             break;
1296         case QMetaType::Char:
1297         case QMetaType::UChar:
1298             return 6;
1299         default:
1300             return 10;
1301         }
1302     } else if (actual->IsString()) {
1303         switch (conversionType) {
1304         case QMetaType::QString:
1305             return 0;
1306         default:
1307             return 10;
1308         }
1309     } else if (actual->IsBoolean()) {
1310         switch (conversionType) {
1311         case QMetaType::Bool:
1312             return 0;
1313         default:
1314             return 10;
1315         }
1316     } else if (actual->IsDate()) {
1317         switch (conversionType) {
1318         case QMetaType::QDateTime:
1319             return 0;
1320         case QMetaType::QDate:
1321             return 1;
1322         case QMetaType::QTime:
1323             return 2;
1324         default:
1325             return 10;
1326         }
1327     } else if (actual->IsRegExp()) {
1328         switch (conversionType) {
1329         case QMetaType::QRegExp:
1330             return 0;
1331         default:
1332             return 10;
1333         }
1334     } else if (actual->IsArray()) {
1335         switch (conversionType) {
1336         case QMetaType::QStringList:
1337         case QMetaType::QVariantList:
1338             return 5;
1339         default:
1340             return 10;
1341         }
1342     } else if (actual->IsNull()) {
1343         switch (conversionType) {
1344         case QMetaType::VoidStar:
1345         case QMetaType::QObjectStar:
1346             return 0;
1347         default:
1348             if (!conversionTypeName.endsWith('*'))
1349                 return 10;
1350             else
1351                 return 0;
1352         }
1353     } else if (actual->IsObject()) {
1354         v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1355
1356         QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1357         if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1358             switch (conversionType) {
1359             case QMetaType::QObjectStar:
1360                 return 0;
1361             default:
1362                 return 10;
1363             }
1364         } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1365             if (conversionType == qMetaTypeId<QVariant>())
1366                 return 0;
1367             else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1368                 return 0;
1369             else
1370                 return 10;
1371         } else {
1372             return 10;
1373         }
1374
1375     } else {
1376         return 10;
1377     }
1378 }
1379
1380 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1381 {
1382     struct Private
1383     {
1384         int revision;
1385         int className;
1386         int classInfoCount, classInfoData;
1387         int methodCount, methodData;
1388     };
1389
1390     return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1391 }
1392
1393 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1394 {
1395     QByteArray sig = m.signature();
1396     int paren = sig.indexOf('(');
1397     if (paren == -1)
1398         return sig;
1399     else
1400         return sig.left(paren);
1401 }
1402
1403 /*!
1404 Returns the next related method, if one, or 0.
1405 */
1406 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object, 
1407                                                              const QDeclarativePropertyCache::Data *current, 
1408                                                              QDeclarativePropertyCache::Data &dummy)
1409 {
1410     QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1411     if (current->relatedIndex == -1)
1412         return 0;
1413
1414     if (cache) {
1415         return cache->method(current->relatedIndex);
1416     } else {
1417         const QMetaObject *mo = object->metaObject();
1418         int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1419
1420         while (methodOffset > current->relatedIndex) {
1421             mo = mo->superClass();
1422             methodOffset -= QMetaObject_methods(mo);
1423         }
1424
1425         QMetaMethod method = mo->method(current->relatedIndex);
1426         dummy.load(method);
1427         
1428         // Look for overloaded methods
1429         QByteArray methodName = QMetaMethod_name(method);
1430         for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1431             if (methodName == QMetaMethod_name(mo->method(ii))) {
1432                 dummy.relatedIndex = ii;
1433                 return &dummy;
1434             }
1435         }
1436
1437         return &dummy;
1438     }
1439 }
1440
1441 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data, 
1442                                          QV8Engine *engine, CallArgs &callArgs)
1443 {
1444     if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
1445
1446         QMetaMethod m = object->metaObject()->method(data.coreIndex);
1447         QList<QByteArray> argTypeNames = m.parameterTypes();
1448         QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1449
1450         // ### Cache
1451         for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1452             argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1453             if (argTypes[ii] == QVariant::Invalid) 
1454                 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1455             if (argTypes[ii] == QVariant::Invalid) {
1456                 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1457                 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1458                 return v8::Handle<v8::Value>();
1459             }
1460         }
1461
1462         if (argTypes.count() > callArgs.Length()) {
1463             QString error = QLatin1String("Insufficient arguments");
1464             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1465             return v8::Handle<v8::Value>();
1466         }
1467
1468         return CallMethod(object, data.coreIndex, data.propType, argTypes.count(), 
1469                           argTypes.data(), engine, callArgs);
1470
1471     } else {
1472
1473         return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1474
1475     }
1476 }
1477
1478 /*!
1479 Resolve the overloaded method to call.  The algorithm works conceptually like this:
1480     1.  Resolve the set of overloads it is *possible* to call.
1481         Impossible overloads include those that have too many parameters or have parameters 
1482         of unknown type.  
1483     2.  Filter the set of overloads to only contain those with the closest number of 
1484         parameters.
1485         For example, if we are called with 3 parameters and there are 2 overloads that
1486         take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1487     3.  Find the best remaining overload based on its match score.  
1488         If two or more overloads have the same match score, call the last one.  The match
1489         score is constructed by adding the matchScore() result for each of the parameters.
1490 */
1491 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data, 
1492                                             QV8Engine *engine, CallArgs &callArgs)
1493 {
1494     int argumentCount = callArgs.Length();
1495
1496     const QDeclarativePropertyCache::Data *best = 0;
1497     int bestParameterScore = INT_MAX;
1498     int bestMatchScore = INT_MAX;
1499
1500     QDeclarativePropertyCache::Data dummy;
1501     const QDeclarativePropertyCache::Data *attempt = &data;
1502
1503     do {
1504         QList<QByteArray> methodArgTypeNames;
1505
1506         if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1507             methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1508
1509         int methodArgumentCount = methodArgTypeNames.count();
1510
1511         if (methodArgumentCount > argumentCount)
1512             continue; // We don't have sufficient arguments to call this method
1513
1514         int methodParameterScore = argumentCount - methodArgumentCount;
1515         if (methodParameterScore > bestParameterScore)
1516             continue; // We already have a better option
1517
1518         int methodMatchScore = 0;
1519         QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1520
1521         bool unknownArgument = false;
1522         for (int ii = 0; ii < methodArgumentCount; ++ii) {
1523             methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1524             if (methodArgTypes[ii] == QVariant::Invalid) 
1525                 methodArgTypes[ii] = EnumType(object->metaObject(), 
1526                                               QString::fromLatin1(methodArgTypeNames.at(ii)));
1527             if (methodArgTypes[ii] == QVariant::Invalid) {
1528                 unknownArgument = true;
1529                 break;
1530             }
1531             methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1532         }
1533         if (unknownArgument)
1534             continue; // We don't understand all the parameters
1535
1536         if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1537             best = attempt;
1538             bestParameterScore = methodParameterScore;
1539             bestMatchScore = methodMatchScore;
1540         }
1541
1542         if (bestParameterScore == 0 && bestMatchScore == 0)
1543             break; // We can't get better than that
1544
1545     } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1546
1547     if (best) {
1548         return CallPrecise(object, *best, engine, callArgs);
1549     } else {
1550         QString error = QLatin1String("Unable to determine callable overload.  Candidates are:");
1551         const QDeclarativePropertyCache::Data *candidate = &data;
1552         while (candidate) {
1553             error += QLatin1String("\n    ") + 
1554                      QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1555             candidate = RelatedMethod(object, candidate, dummy);
1556         }
1557
1558         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1559         return v8::Handle<v8::Value>();
1560     }
1561 }
1562
1563 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1564 {
1565     QString result;
1566     if (object) {
1567         QString objectName = object->objectName();
1568
1569         result += QString::fromUtf8(object->metaObject()->className());
1570         result += QLatin1String("(0x");
1571         result += QString::number((quintptr)object,16);
1572
1573         if (!objectName.isEmpty()) {
1574             result += QLatin1String(", \"");
1575             result += objectName;
1576             result += QLatin1Char('\"');
1577         }
1578
1579         result += QLatin1Char(')');
1580     } else {
1581         result = QLatin1String("null");
1582     }
1583
1584     return engine->toString(result);
1585 }
1586
1587 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1588 {
1589     QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1590     if (!ddata || ddata->indestructible) {
1591         const char *error = "Invalid attempt to destroy() an indestructible object";
1592         v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1593         return v8::Undefined();
1594     }
1595
1596     int delay = 0;
1597     if (argCount > 0)
1598         delay = args->Get(0)->Uint32Value();
1599
1600     if (delay > 0)
1601         QTimer::singleShot(delay, object, SLOT(deleteLater()));
1602     else
1603         object->deleteLater();
1604
1605     return v8::Undefined();
1606 }
1607
1608 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1609 {
1610     // object, index, qmlglobal, argCount, args
1611     Q_ASSERT(args.Length() == 5);
1612     Q_ASSERT(args[0]->IsObject());
1613
1614     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1615
1616     if (!resource)
1617         return v8::Undefined();
1618
1619     int argCount = args[3]->Int32Value();
1620     v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1621
1622     // Special hack to return info about this closure.
1623     if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1624         v8::Local<v8::Array> data = v8::Array::New(2);
1625         data->Set(0, args[0]);
1626         data->Set(1, args[1]);
1627         return data;
1628     }
1629
1630     QObject *object = resource->object;
1631     int index = args[1]->Int32Value();
1632
1633     if (!object)
1634         return v8::Undefined();
1635
1636     if (index < 0) {
1637         // Builtin functions
1638         if (index == QOBJECT_TOSTRING_INDEX) {
1639             return ToString(resource->engine, object, argCount, arguments);
1640         } else if (index == QOBJECT_DESTROY_INDEX) {
1641             return Destroy(resource->engine, object, argCount, arguments);
1642         } else {
1643             return v8::Undefined();
1644         }
1645     }
1646
1647     QDeclarativePropertyCache::Data method;
1648
1649     if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1650         if (ddata->propertyCache) {
1651             QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1652             if (!d) 
1653                 return v8::Undefined();
1654             method = *d;
1655         } 
1656     }
1657
1658     if (method.coreIndex == -1) {
1659         QMetaMethod mm = object->metaObject()->method(index);
1660         method.load(object->metaObject()->method(index));
1661
1662         if (method.coreIndex == -1)
1663             return v8::Undefined();
1664     }
1665
1666     if (method.flags & QDeclarativePropertyCache::Data::IsV8Function) {
1667         v8::Handle<v8::Value> rv;
1668         v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1669
1670         QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal, 
1671                                     resource->engine->contextWrapper()->context(qmlglobal),
1672                                     resource->engine);
1673         QDeclarativeV8Function *funcptr = &func;
1674
1675         void *args[] = { 0, &funcptr };
1676         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1677
1678         if (rv.IsEmpty()) return v8::Undefined();
1679         return rv;
1680     }
1681
1682     CallArgs callArgs(argCount, &arguments);
1683     if (method.relatedIndex == -1) {
1684         return CallPrecise(object, method, resource->engine, callArgs);
1685     } else {
1686         return CallOverloaded(object, method, resource->engine, callArgs);
1687     }
1688 }
1689
1690 MetaCallArgument::MetaCallArgument()
1691 : type(QVariant::Invalid), isObjectType(false)
1692 {
1693 }
1694
1695 MetaCallArgument::~MetaCallArgument()
1696 {
1697     cleanup();
1698 }
1699
1700 void MetaCallArgument::cleanup()
1701 {
1702     if (type == QMetaType::QString) {
1703         ((QString *)&data)->~QString();
1704     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1705         ((QVariant *)&data)->~QVariant();
1706     } else if (type == qMetaTypeId<QScriptValue>()) {
1707         ((QScriptValue *)&data)->~QScriptValue();
1708     } else if (type == qMetaTypeId<QList<QObject *> >()) {
1709         ((QList<QObject *> *)&data)->~QList<QObject *>();
1710     }
1711 }
1712
1713 void *MetaCallArgument::dataPtr()
1714 {
1715     if (type == -1)
1716         return ((QVariant *)data)->data();
1717     else
1718         return (void *)&data;
1719 }
1720
1721 void MetaCallArgument::initAsType(int callType)
1722 {
1723     if (type != 0) { cleanup(); type = 0; }
1724     if (callType == 0) return;
1725
1726     if (callType == qMetaTypeId<QScriptValue>()) {
1727         new (&data) QScriptValue();
1728         type = callType;
1729     } else if (callType == QMetaType::Int ||
1730                callType == QMetaType::UInt ||
1731                callType == QMetaType::Bool ||
1732                callType == QMetaType::Double ||
1733                callType == QMetaType::Float) {
1734         type = callType;
1735     } else if (callType == QMetaType::QObjectStar) {
1736         *((QObject **)&data) = 0;
1737         type = callType;
1738     } else if (callType == QMetaType::QString) {
1739         new (&data) QString();
1740         type = callType;
1741     } else if (callType == qMetaTypeId<QVariant>()) {
1742         type = callType;
1743         new (&data) QVariant();
1744     } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1745         type = callType;
1746         new (&data) QList<QObject *>();
1747     } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1748         type = callType;
1749         new (&data) v8::Handle<v8::Value>();
1750     } else {
1751         type = -1;
1752         new (&data) QVariant(callType, (void *)0);
1753     }
1754 }
1755
1756 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1757 {
1758     if (type != 0) { cleanup(); type = 0; }
1759
1760     if (callType == qMetaTypeId<QScriptValue>()) {
1761         new (&data) QScriptValue();
1762         type = qMetaTypeId<QScriptValue>();
1763     } else if (callType == QMetaType::Int) {
1764         *((int *)&data) = int(value->Int32Value());
1765         type = callType;
1766     } else if (callType == QMetaType::UInt) {
1767         *((uint *)&data) = uint(value->Uint32Value());
1768         type = callType;
1769     } else if (callType == QMetaType::Bool) {
1770         *((bool *)&data) = value->BooleanValue();
1771         type = callType;
1772     } else if (callType == QMetaType::Double) {
1773         *((double *)&data) = double(value->NumberValue());
1774         type = callType;
1775     } else if (callType == QMetaType::Float) {
1776         *((float *)&data) = float(value->NumberValue());
1777         type = callType;
1778     } else if (callType == QMetaType::QString) {
1779         if (value->IsNull() || value->IsUndefined())
1780             new (&data) QString();
1781         else
1782             new (&data) QString(engine->toString(value->ToString()));
1783         type = callType;
1784     } else if (callType == QMetaType::QObjectStar) {
1785         *((QObject **)&data) = engine->toQObject(value);
1786         type = callType;
1787     } else if (callType == qMetaTypeId<QVariant>()) {
1788         new (&data) QVariant(engine->toVariant(value, -1));
1789         type = callType;
1790     } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1791         QList<QObject *> *list = new (&data) QList<QObject *>(); 
1792         if (value->IsArray()) {
1793             v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1794             uint32_t length = array->Length();
1795             for (uint32_t ii = 0; ii < length; ++ii) 
1796                 list->append(engine->toQObject(array->Get(ii)));
1797         } else {
1798             list->append(engine->toQObject(value));
1799         }
1800         type = callType;
1801     } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1802         new (&data) v8::Handle<v8::Value>(value);
1803         type = callType;
1804     } else {
1805         new (&data) QVariant();
1806         type = -1;
1807
1808         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
1809         QVariant v = engine->toVariant(value, -1);
1810
1811         if (v.userType() == callType) {
1812             *((QVariant *)&data) = v;
1813         } else if (v.canConvert((QVariant::Type)callType)) {
1814             *((QVariant *)&data) = v;
1815             ((QVariant *)&data)->convert((QVariant::Type)callType);
1816         } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) {
1817             QObject *obj = ep->toQObject(v);
1818             
1819             if (obj) {
1820                 const QMetaObject *objMo = obj->metaObject();
1821                 while (objMo && objMo != mo) objMo = objMo->superClass();
1822                 if (!objMo) obj = 0;
1823             }
1824
1825             *((QVariant *)&data) = QVariant(callType, &obj);
1826         } else {
1827             *((QVariant *)&data) = QVariant(callType, (void *)0);
1828         }
1829     }
1830 }
1831
1832 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1833 {
1834     if (type == qMetaTypeId<QScriptValue>()) {
1835         return v8::Undefined();
1836     } else if (type == QMetaType::Int) {
1837         return v8::Integer::New(*((int *)&data));
1838     } else if (type == QMetaType::UInt) {
1839         return v8::Integer::NewFromUnsigned(*((uint *)&data));
1840     } else if (type == QMetaType::Bool) {
1841         return v8::Boolean::New(*((bool *)&data));
1842     } else if (type == QMetaType::Double) {
1843         return v8::Number::New(*((double *)&data));
1844     } else if (type == QMetaType::Float) {
1845         return v8::Number::New(*((float *)&data));
1846     } else if (type == QMetaType::QString) {
1847         return engine->toString(*((QString *)&data));
1848     } else if (type == QMetaType::QObjectStar) {
1849         QObject *object = *((QObject **)&data);
1850         if (object)
1851             QDeclarativeData::get(object, true)->setImplicitDestructible();
1852         return engine->newQObject(object);
1853     } else if (type == qMetaTypeId<QList<QObject *> >()) {
1854         // XXX Can this be made more by using Array as a prototype and implementing
1855         // directly against QList<QObject*>?
1856         QList<QObject *> &list = *(QList<QObject *>*)&data;
1857         v8::Local<v8::Array> array = v8::Array::New(list.count());
1858         for (int ii = 0; ii < list.count(); ++ii) 
1859             array->Set(ii, engine->newQObject(list.at(ii)));
1860         return array;
1861     } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
1862         return *(v8::Handle<v8::Value>*)&data;
1863     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1864         QVariant value = *((QVariant *)&data);
1865         v8::Handle<v8::Value> rv = engine->fromVariant(value);
1866         if (QObject *object = engine->toQObject(rv)) 
1867             QDeclarativeData::get(object, true)->setImplicitDestructible();
1868         return rv;
1869     } else {
1870         return v8::Undefined();
1871     }
1872 }