Implement global handle tracking
[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 Need to check all calls to QDeclarativeEngine *engine() to confirm this class works
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 aakenned This can't possibly be the best solution!!!
301     struct MethodClosure {
302        static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object, 
303                                            v8::Handle<v8::Value> *objectHandle, 
304                                            int index) { 
305            v8::Handle<v8::Value> argv[] = { 
306                objectHandle?*objectHandle:engine->newQObject(object),
307                v8::Integer::New(index)
308            };
309            return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
310        }
311        static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object, 
312                                                      v8::Handle<v8::Value> *objectHandle, 
313                                                      int index) { 
314            v8::Handle<v8::Value> argv[] = { 
315                objectHandle?*objectHandle:engine->newQObject(object),
316                v8::Integer::New(index),
317                v8::Context::GetCallingQmlGlobal()
318            };
319            return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
320        }
321     };
322
323     if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property)) {
324         return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
325     } else if (engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property)) {
326         return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
327     }
328
329     QDeclarativePropertyCache::Data local;
330     QDeclarativePropertyCache::Data *result = 0;
331     result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
332
333     if (!result)
334         return v8::Handle<v8::Value>();
335
336     QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
337
338     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
339         QDeclarativeData *ddata = QDeclarativeData::get(object);
340         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
341             return v8::Handle<v8::Value>();
342     }
343
344     typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
345
346     if (result->isFunction()) {
347         if (result->flags & QDeclarativePropertyCache::Data::IsVMEFunction) {
348             return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
349         } else if (result->flags & QDeclarativePropertyCache::Data::IsV8Function) {
350             return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
351         } else {
352             return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
353         }
354     }
355
356     if (ep && ep->captureProperties && !result->isConstant()) {
357         if (result->coreIndex == 0)
358             ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
359         else
360             ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
361     }
362
363     return LoadProperty(engine, object, *result);
364 }
365
366 // Setter for writable properties.  Shared between the interceptor and fast property accessor
367 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property,
368                                  v8::Handle<v8::Value> value)
369 {
370     QDeclarativeBinding *newBinding = 0;
371
372     if (value->IsFunction()) {
373         QDeclarativeContextData *context = engine->callingContext();
374         v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
375
376         v8::Local<v8::StackTrace> trace = 
377             v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | 
378                                                                                      v8::StackTrace::kScriptName));
379         v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
380         int lineNumber = frame->GetLineNumber();
381         QString url = engine->toString(frame->GetScriptName());
382
383         QDeclarativePropertyCache::ValueTypeData valueTypeData;
384         newBinding = new QDeclarativeBinding(&function, object, context);
385         newBinding->setSourceLocation(url, lineNumber);
386         newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context));
387         newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
388     }
389
390     QDeclarativeAbstractBinding *oldBinding = 
391         QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
392     if (oldBinding)
393         oldBinding->destroy();
394
395 #define PROPERTY_STORE(cpptype, value) \
396     cpptype o = value; \
397     int status = -1; \
398     int flags = 0; \
399     void *argv[] = { &o, 0, &status, &flags }; \
400     QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
401
402
403     if (value->IsNull() && property->isQObject()) {
404         PROPERTY_STORE(QObject*, 0);
405     } else if (value->IsUndefined() && property->isResettable()) {
406         void *a[] = { 0 };
407         QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
408     } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
409         PROPERTY_STORE(QVariant, QVariant());
410     } else if (value->IsUndefined()) {
411         QString error = QLatin1String("Cannot assign [undefined] to ") +
412                         QLatin1String(QMetaType::typeName(property->propType));
413         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
414     } else if (value->IsFunction()) {
415         // this is handled by the binding creation above
416     } else if (property->propType == QMetaType::Int && value->IsNumber()) {
417         PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
418     } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
419         PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
420     } else if (property->propType == QMetaType::Float && value->IsNumber()) {
421         PROPERTY_STORE(float, float(value->ToNumber()->Value()));
422     } else if (property->propType == QMetaType::Double && value->IsNumber()) {
423         PROPERTY_STORE(double, double(value->ToNumber()->Value()));
424     } else if (property->propType == QMetaType::QString && value->IsString()) {
425         PROPERTY_STORE(QString, engine->toString(value->ToString()));
426     } else {
427         QVariant v;
428         if (property->isQList()) 
429             v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
430         else
431             v = engine->toVariant(value, property->propType);
432
433         QDeclarativeContextData *context = engine->callingContext();
434
435         if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
436             const char *valueType = 0;
437             if (v.userType() == QVariant::Invalid) valueType = "null";
438             else valueType = QMetaType::typeName(v.userType());
439
440             QString error = QLatin1String("Cannot assign ") +
441                             QLatin1String(valueType) +
442                             QLatin1String(" to ") +
443                             QLatin1String(QMetaType::typeName(property->propType));
444             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
445         }
446     }
447 }
448
449 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, v8::Handle<v8::String> property,
450                                     v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
451 {
452     if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property) ||
453         engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property))
454         return true;
455
456     QDeclarativePropertyCache::Data local;
457     QDeclarativePropertyCache::Data *result = 0;
458     result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
459
460     if (!result)
461         return false;
462
463     if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
464         QDeclarativeData *ddata = QDeclarativeData::get(object);
465         if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
466             return false;
467     }
468
469     if (!result->isWritable() && !result->isQList()) {
470         QString error = QLatin1String("Cannot assign to read-only property \"") +
471                         engine->toString(property) + QLatin1Char('\"');
472         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
473         return true;
474     }
475
476     StoreProperty(engine, object, result, value);
477
478     return true;
479 }
480
481 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property, 
482                                                 const v8::AccessorInfo &info)
483 {
484     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
485     v8::Handle<v8::Value> This = info.This();
486
487     if (!resource || resource->object.isNull()) return v8::Undefined();
488
489     QObject *object = resource->object;
490
491     QV8Engine *v8engine = resource->engine;
492     v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, property, QV8QObjectWrapper::IgnoreRevision);
493     if (!result.IsEmpty())
494         return result;
495
496     if (QV8Engine::startsWithUpper(property)) {
497         // Check for attached properties
498         QDeclarativeContextData *context = v8engine->callingContext();
499         QDeclarativeTypeNameCache::Data *data = context && (context->imports)?context->imports->data(property):0;
500
501         if (data) {
502             if (data->type) {
503                 return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums);
504             } else if (data->typeNamespace) {
505                 return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
506             }
507         }
508
509         return v8::Undefined();
510     } else {
511         // XXX throw?
512         return v8::Undefined();
513     }
514 }
515
516 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property, 
517                                                 v8::Local<v8::Value> value,
518                                                 const v8::AccessorInfo &info)
519 {
520     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
521
522     if (!resource || resource->object.isNull()) 
523         return value;
524
525     QObject *object = resource->object;
526
527     QV8Engine *v8engine = resource->engine;
528     bool result = SetProperty(v8engine, object, property, value, QV8QObjectWrapper::IgnoreRevision);
529
530     if (!result) {
531         QString error = QLatin1String("Cannot assign to non-existent property \"") +
532                         v8engine->toString(property) + QLatin1Char('\"');
533         v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
534         return value;
535     }
536
537     return value;
538 }
539
540 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
541                                                  const v8::AccessorInfo &info)
542 {
543     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
544
545     if (!resource || resource->object.isNull()) 
546         return v8::Handle<v8::Integer>();
547
548     QV8Engine *engine = resource->engine;
549     QObject *object = resource->object;
550
551     QDeclarativePropertyCache::Data local;
552     QDeclarativePropertyCache::Data *result = 0;
553     result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
554
555     if (!result)
556         return v8::Handle<v8::Integer>();
557     else if (!result->isWritable() && !result->isQList())
558         return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
559     else
560         return v8::Integer::New(v8::DontDelete);
561 }
562
563 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
564 {
565     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
566
567     if (!resource || resource->object.isNull()) 
568         return v8::Array::New();
569
570     QObject *object = resource->object;
571
572     QStringList result;
573
574     QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine());
575
576     QDeclarativePropertyCache *cache = 0;
577     QDeclarativeData *ddata = QDeclarativeData::get(object);
578     if (ddata)
579         cache = ddata->propertyCache;
580
581     if (!cache) {
582         cache = ep->cache(object);
583         if (cache) {
584             if (ddata) { cache->addref(); ddata->propertyCache = cache; }
585         } else {
586             // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
587             // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal.
588             // XXX This is a workaround for QTBUG-9420.
589             const QMetaObject *mo = object->metaObject();
590             int pc = mo->propertyCount();
591             int po = mo->propertyOffset();
592             for (int i=po; i<pc; ++i)
593                 result << QString::fromUtf8(mo->property(i).name());
594         }
595     } else {
596         result = cache->propertyNames();
597     }
598
599     v8::Local<v8::Array> rv = v8::Array::New(result.count());
600
601     for (int ii = 0; ii < result.count(); ++ii) 
602         rv->Set(ii, resource->engine->toString(result.at(ii)));
603
604     return rv;
605 }
606
607 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
608 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
609 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
610 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
611 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
612 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
613 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
614
615 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
616                             const v8::AccessorInfo& info)
617 {
618     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
619
620     if (!resource || resource->object.isNull()) 
621         return; 
622
623     QObject *object = resource->object;
624
625     uint32_t data = info.Data()->Uint32Value(); 
626     int index = data & 0x7FFF;  // So that we can use the same data for Setter and Getter
627
628     QDeclarativeData *ddata = QDeclarativeData::get(object, false);
629     Q_ASSERT(ddata);
630     Q_ASSERT(ddata->propertyCache);
631
632     QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index);
633     Q_ASSERT(pdata);
634
635     Q_ASSERT(pdata->isWritable() || pdata->isQList());
636
637     StoreProperty(resource->engine, object, pdata, value);
638 }
639
640 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
641 {
642     Q_ASSERT(handle->IsObject());
643     
644     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(handle->ToObject());
645
646     Q_ASSERT(resource);
647
648     QObject *object = resource->object;
649     if (object) {
650         QDeclarativeData *ddata = QDeclarativeData::get(object, false);
651         if (ddata) {
652             ddata->v8object.Clear();
653             if (!object->parent() && !ddata->indestructible)
654                 object->deleteLater();
655         }
656     }
657
658     qPersistentDispose(handle);
659 }
660
661 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
662 {
663     QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
664     instance->v8object.Clear();
665     qPersistentDispose(handle);
666 }
667
668 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
669 {
670     Q_ASSERT(object);
671
672     Q_ASSERT(QDeclarativeData::get(object, false));
673     Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
674
675     // Setup constructor
676     if (constructor.IsEmpty()) {
677         v8::Local<v8::FunctionTemplate> ft;
678
679         QString toString = QLatin1String("toString");
680         QString destroy = QLatin1String("destroy");
681
682         // XXX Enables fast property accessors.  These more than double the property access 
683         // performance, but the  cost of setting up this structure hasn't been measured so 
684         // its not guarenteed that this is a win overall
685         for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
686             Data *property = *iter;
687             if (property->isFunction() || !property->isWritable() ||
688                 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x7FFF || 
689                 property->coreIndex == 0)
690                 continue;
691
692             v8::AccessorGetter fastgetter = 0;
693
694             
695             if (property->isQObject()) 
696                 fastgetter = QObjectValueGetter;
697             else if (property->propType == QMetaType::Int || property->isEnum()) 
698                 fastgetter = IntValueGetter;
699             else if (property->propType == QMetaType::Bool)
700                 fastgetter = BoolValueGetter;
701             else if (property->propType == QMetaType::QString)
702                 fastgetter = QStringValueGetter;
703             else if (property->propType == QMetaType::UInt)
704                 fastgetter = UIntValueGetter;
705             else if (property->propType == QMetaType::Float) 
706                 fastgetter = FloatValueGetter;
707             else if (property->propType == QMetaType::Double) 
708                 fastgetter = DoubleValueGetter;
709
710             if (fastgetter) {
711                 int notifyIndex = property->notifyIndex;
712                 if (property->isConstant()) notifyIndex = 0;
713                 else if (notifyIndex == -1) notifyIndex = 0x7FFF;
714                 uint32_t data = (property->notifyIndex & 0x7FFF) << 16 | property->coreIndex;
715
716                 QString name = iter.key();
717                 if (name == toString || name == destroy)
718                     continue;
719
720                 if (ft.IsEmpty()) {
721                     ft = v8::FunctionTemplate::New();
722                     ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
723                                                                        QV8QObjectWrapper::Setter,
724                                                                        QV8QObjectWrapper::Query, 
725                                                                        0,
726                                                                        QV8QObjectWrapper::Enumerator);
727                     ft->InstanceTemplate()->SetHasExternalResource(true);
728                 }
729
730                 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, FastValueSetter,
731                                                     v8::Integer::NewFromUnsigned(data));
732             }
733         }
734
735         if (ft.IsEmpty()) {
736             constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
737         } else {
738             ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, 
739                                                                QV8QObjectWrapper::Setter,
740                                                                QV8QObjectWrapper::Query, 
741                                                                0,
742                                                                QV8QObjectWrapper::Enumerator);
743             ft->InstanceTemplate()->SetHasExternalResource(true);
744             constructor = qPersistentNew<v8::Function>(ft->GetFunction());
745         }
746     }
747
748     v8::Local<v8::Object> result = constructor->NewInstance();
749     QV8QObjectResource *r = new QV8QObjectResource(engine, object);
750     result->SetExternalResource(r);
751     return result;
752 }
753
754 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
755 {
756     v8::Local<v8::Object> rv;
757
758     if (ddata->propertyCache) {
759         rv = ddata->propertyCache->newQObject(object, engine);
760     } else {
761         // XXX aakenned - NewInstance() is slow for our case
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         v8::Persistent<v8::Object> thisObject;
876         v8::Persistent<v8::Function> function;
877     };
878
879     QV8Engine *engine;
880
881     typedef QHash<int, QList<Connection> > SlotHash;
882     SlotHash slotHash;
883
884     virtual void objectDestroyed(QObject *);
885     virtual int qt_metacall(QMetaObject::Call, int, void **);
886 };
887
888 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
889 : QDeclarativeGuard<QObject>(object), engine(engine)
890 {
891 }
892
893 QV8QObjectConnectionList::~QV8QObjectConnectionList()
894 {
895     for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
896         QList<Connection> &connections = *iter;
897         for (int ii = 0; ii < connections.count(); ++ii) {
898             qPersistentDispose(connections[ii].thisObject);
899             qPersistentDispose(connections[ii].function);
900         }
901     }
902     slotHash.clear();
903 }
904
905 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
906 {
907     engine->qobjectWrapper()->m_connections.remove(object);
908     delete this;
909 }
910
911 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
912 {
913     if (method == QMetaObject::InvokeMetaMethod) {
914         SlotHash::Iterator iter = slotHash.find(index);
915         if (iter == slotHash.end())
916             return -1;
917         QList<Connection> &connections = *iter;
918         if (connections.isEmpty())
919             return -1;
920
921         // XXX optimize
922         QMetaMethod method = data()->metaObject()->method(index);
923         Q_ASSERT(method.methodType() == QMetaMethod::Signal);
924         QList<QByteArray> params = method.parameterTypes();
925
926         v8::HandleScope handle_scope;
927         v8::Context::Scope scope(engine->context());
928
929         QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
930         int argCount = params.count();
931
932         for (int ii = 0; ii < argCount; ++ii) {
933             int type = QMetaType::type(params.at(ii).constData());
934             if (type == qMetaTypeId<QVariant>()) {
935                 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
936             } else {
937                 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
938             }
939         }
940
941         // XXX what if this list changes or this object is deleted during the calls?
942         for (int ii = 0; ii < connections.count(); ++ii) {
943             Connection &connection = connections[ii];
944             if (connection.thisObject.IsEmpty()) {
945                 connection.function->Call(engine->global(), argCount, args.data());
946             } else {
947                 connection.function->Call(connection.thisObject, argCount, args.data());
948             }
949         }
950     } 
951
952     return -1;
953 }
954
955 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
956 {
957     if (args.Length() == 0)
958         V8THROW_ERROR("Function.prototype.connect: no arguments given");
959
960     QV8Engine *engine = V8ENGINE();
961
962     if (!args.This()->IsFunction())
963         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
964
965     QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
966     QObject *signalObject = signalInfo.first;
967     int signalIndex = signalInfo.second;
968
969     if (signalIndex == -1)
970         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
971
972     if (!signalObject)
973         V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
974
975     if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
976         V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
977
978     v8::Local<v8::Value> functionValue;
979     v8::Local<v8::Value> functionThisValue;
980
981     if (args.Length() == 1) {
982         functionValue = args[0];
983     } else {
984         functionThisValue = args[0];
985         functionValue = args[1];
986     }
987
988     if (!functionValue->IsFunction())
989         V8THROW_ERROR("Function.prototype.connect: target is not a function");
990
991     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
992         V8THROW_ERROR("Function.prototype.connect: target this is not an object");
993
994     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
995     QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
996     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
997     if (iter == connections.end()) 
998         iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
999
1000     QV8QObjectConnectionList *connectionList = *iter;
1001     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1002     if (slotIter == connectionList->slotHash.end()) {
1003         slotIter = connectionList->slotHash.insert(signalIndex, QList<QV8QObjectConnectionList::Connection>());
1004         QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1005     }
1006
1007     QV8QObjectConnectionList::Connection connection;
1008     if (!functionThisValue.IsEmpty()) 
1009         connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1010     connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1011
1012     slotIter->append(connection);
1013
1014     return v8::Undefined();
1015 }
1016
1017 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1018 {
1019     if (args.Length() == 0)
1020         V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1021
1022     QV8Engine *engine = V8ENGINE();
1023
1024     if (!args.This()->IsFunction())
1025         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1026
1027     QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1028     QObject *signalObject = signalInfo.first;
1029     int signalIndex = signalInfo.second;
1030
1031     if (signalIndex == -1)
1032         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1033
1034     if (!signalObject)
1035         V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1036
1037     if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1038         V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1039
1040     v8::Local<v8::Value> functionValue;
1041     v8::Local<v8::Value> functionThisValue;
1042
1043     if (args.Length() == 1) {
1044         functionValue = args[0];
1045     } else {
1046         functionThisValue = args[0];
1047         functionValue = args[1];
1048     }
1049
1050     if (!functionValue->IsFunction())
1051         V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1052
1053     if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1054         V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1055
1056     QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1057     QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1058     QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1059     if (iter == connectionsList.end()) 
1060         return v8::Undefined(); // Nothing to disconnect from
1061
1062     QV8QObjectConnectionList *connectionList = *iter;
1063     QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1064     if (slotIter == connectionList->slotHash.end()) 
1065         return v8::Undefined(); // Nothing to disconnect from
1066
1067     QList<QV8QObjectConnectionList::Connection> &connections = *slotIter;
1068
1069     v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1070     QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1071
1072     if (functionData.second != -1) {
1073         // This is a QObject function wrapper
1074         for (int ii = 0; ii < connections.count(); ++ii) {
1075             QV8QObjectConnectionList::Connection &connection = connections[ii];
1076
1077             if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1078                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1079
1080                 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1081                 if (connectedFunctionData == functionData) {
1082                     // Match!
1083                     qPersistentDispose(connection.thisObject);
1084                     qPersistentDispose(connection.function);
1085                     connections.removeAt(ii);
1086                     return v8::Undefined();
1087                 }
1088             }
1089         }
1090
1091     } else {
1092         // This is a normal JS function
1093         for (int ii = 0; ii < connections.count(); ++ii) {
1094             QV8QObjectConnectionList::Connection &connection = connections[ii];
1095             if (connection.function->StrictEquals(function) &&
1096                 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1097                 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1098                 // Match!
1099                 qPersistentDispose(connection.thisObject);
1100                 qPersistentDispose(connection.function);
1101                 connections.removeAt(ii);
1102                 return v8::Undefined();
1103             }
1104         }
1105     }
1106
1107     return v8::Undefined();
1108 }
1109
1110 /*!
1111     Get the \a property of \a object.  Returns an empty handle if the property doesn't exist.
1112
1113     Only searches for real properties of \a object (including methods), not attached properties etc.
1114 */
1115 v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, v8::Handle<v8::String> property,
1116                                                      QV8QObjectWrapper::RevisionMode revisionMode)
1117 {
1118     return GetProperty(m_engine, object, 0, property, revisionMode);
1119 }
1120
1121 /*
1122     Set the \a property of \a object to \a value.
1123
1124     Returns true if the property was "set" - even if this results in an exception being thrown -
1125     and false if the object has no such property.
1126
1127     Only searches for real properties of \a object (including methods), not attached properties etc.
1128 */
1129 bool QV8QObjectWrapper::setProperty(QObject *object, v8::Handle<v8::String> property, 
1130                                     v8::Handle<v8::Value> value, RevisionMode revisionMode)
1131 {
1132     return SetProperty(m_engine, object, property, value, revisionMode);
1133 }
1134
1135 namespace {
1136 struct CallArgs
1137 {
1138     CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1139     int Length() const { return _length; }
1140     v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1141
1142 private:
1143     int _length;
1144     v8::Handle<v8::Object> *_args;
1145 };
1146 }
1147
1148 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount, 
1149                                         int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1150 {
1151     if (argCount > 0) {
1152
1153         QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1154         args[0].initAsType(returnType);
1155
1156         for (int ii = 0; ii < argCount; ++ii)
1157             args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1158
1159         QVarLengthArray<void *, 9> argData(args.count());
1160         for (int ii = 0; ii < args.count(); ++ii)
1161             argData[ii] = args[ii].dataPtr();
1162
1163         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1164
1165         return args[0].toValue(engine);
1166
1167     } else if (returnType != 0) {
1168         
1169         MetaCallArgument arg;
1170         arg.initAsType(returnType);
1171
1172         void *args[] = { arg.dataPtr() };
1173
1174         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1175
1176         return arg.toValue(engine);
1177
1178     } else {
1179
1180         void *args[] = { 0 };
1181         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1182         return v8::Undefined();
1183
1184     }
1185 }
1186
1187 static int EnumType(const QMetaObject *meta, const QString &strname)
1188 {
1189     QByteArray str = strname.toUtf8();
1190     QByteArray scope;
1191     QByteArray name;
1192     int scopeIdx = str.lastIndexOf("::");
1193     if (scopeIdx != -1) {
1194         scope = str.left(scopeIdx);
1195         name = str.mid(scopeIdx + 2);
1196     } else { 
1197         name = str;
1198     }
1199     for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1200         QMetaEnum m = meta->enumerator(i);
1201         if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1202             return QVariant::Int;
1203     }
1204     return QVariant::Invalid;
1205 }
1206
1207 /*!
1208     Returns the match score for converting \a actual to be of type \a conversionType.  A 
1209     zero score means "perfect match" whereas a higher score is worse.
1210
1211     The conversion table is copied out of the QtScript callQtMethod() function.
1212 */
1213 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType, 
1214                       const QByteArray &conversionTypeName)
1215 {
1216     if (actual->IsNumber()) {
1217         switch (conversionType) {
1218         case QMetaType::Double:
1219             return 0;
1220         case QMetaType::Float:
1221             return 1;
1222         case QMetaType::LongLong:
1223         case QMetaType::ULongLong:
1224             return 2;
1225         case QMetaType::Long:
1226         case QMetaType::ULong:
1227             return 3;
1228         case QMetaType::Int:
1229         case QMetaType::UInt:
1230             return 4;
1231         case QMetaType::Short:
1232         case QMetaType::UShort:
1233             return 5;
1234             break;
1235         case QMetaType::Char:
1236         case QMetaType::UChar:
1237             return 6;
1238         default:
1239             return 10;
1240         }
1241     } else if (actual->IsString()) {
1242         switch (conversionType) {
1243         case QMetaType::QString:
1244             return 0;
1245         default:
1246             return 10;
1247         }
1248     } else if (actual->IsBoolean()) {
1249         switch (conversionType) {
1250         case QMetaType::Bool:
1251             return 0;
1252         default:
1253             return 10;
1254         }
1255     } else if (actual->IsDate()) {
1256         switch (conversionType) {
1257         case QMetaType::QDateTime:
1258             return 0;
1259         case QMetaType::QDate:
1260             return 1;
1261         case QMetaType::QTime:
1262             return 2;
1263         default:
1264             return 10;
1265         }
1266     } else if (actual->IsRegExp()) {
1267         switch (conversionType) {
1268         case QMetaType::QRegExp:
1269             return 0;
1270         default:
1271             return 10;
1272         }
1273     } else if (actual->IsArray()) {
1274         switch (conversionType) {
1275         case QMetaType::QStringList:
1276         case QMetaType::QVariantList:
1277             return 5;
1278         default:
1279             return 10;
1280         }
1281     } else if (actual->IsNull()) {
1282         switch (conversionType) {
1283         case QMetaType::VoidStar:
1284         case QMetaType::QObjectStar:
1285             return 0;
1286         default:
1287             if (!conversionTypeName.endsWith('*'))
1288                 return 10;
1289             else
1290                 return 0;
1291         }
1292     } else if (actual->IsObject()) {
1293         v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1294
1295         QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1296         if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1297             switch (conversionType) {
1298             case QMetaType::QObjectStar:
1299                 return 0;
1300             default:
1301                 return 10;
1302             }
1303         } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1304             if (conversionType == qMetaTypeId<QVariant>())
1305                 return 0;
1306             else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1307                 return 0;
1308             else
1309                 return 10;
1310         } else {
1311             return 10;
1312         }
1313
1314     } else {
1315         return 10;
1316     }
1317 }
1318
1319 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1320 {
1321     struct Private
1322     {
1323         int revision;
1324         int className;
1325         int classInfoCount, classInfoData;
1326         int methodCount, methodData;
1327     };
1328
1329     return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1330 }
1331
1332 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1333 {
1334     QByteArray sig = m.signature();
1335     int paren = sig.indexOf('(');
1336     if (paren == -1)
1337         return sig;
1338     else
1339         return sig.left(paren);
1340 }
1341
1342 /*!
1343 Returns the next related method, if one, or 0.
1344 */
1345 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object, 
1346                                                              const QDeclarativePropertyCache::Data *current, 
1347                                                              QDeclarativePropertyCache::Data &dummy)
1348 {
1349     QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1350     if (current->relatedIndex == -1)
1351         return 0;
1352
1353     if (cache) {
1354         return cache->method(current->relatedIndex);
1355     } else {
1356         const QMetaObject *mo = object->metaObject();
1357         int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1358
1359         while (methodOffset > current->relatedIndex) {
1360             mo = mo->superClass();
1361             methodOffset -= QMetaObject_methods(mo);
1362         }
1363
1364         QMetaMethod method = mo->method(current->relatedIndex);
1365         dummy.load(method);
1366         
1367         // Look for overloaded methods
1368         QByteArray methodName = QMetaMethod_name(method);
1369         for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1370             if (methodName == QMetaMethod_name(mo->method(ii))) {
1371                 dummy.relatedIndex = ii;
1372                 return &dummy;
1373             }
1374         }
1375
1376         return &dummy;
1377     }
1378 }
1379
1380 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data, 
1381                                          QV8Engine *engine, CallArgs &callArgs)
1382 {
1383     if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
1384
1385         QMetaMethod m = object->metaObject()->method(data.coreIndex);
1386         QList<QByteArray> argTypeNames = m.parameterTypes();
1387         QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1388
1389         // ### Cache
1390         for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1391             argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1392             if (argTypes[ii] == QVariant::Invalid) 
1393                 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1394             if (argTypes[ii] == QVariant::Invalid) {
1395                 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1396                 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1397                 return v8::Handle<v8::Value>();
1398             }
1399         }
1400
1401         if (argTypes.count() > callArgs.Length()) {
1402             QString error = QLatin1String("Insufficient arguments");
1403             v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1404             return v8::Handle<v8::Value>();
1405         }
1406
1407         return CallMethod(object, data.coreIndex, data.propType, argTypes.count(), 
1408                           argTypes.data(), engine, callArgs);
1409
1410     } else {
1411
1412         return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1413
1414     }
1415 }
1416
1417 /*!
1418 Resolve the overloaded method to call.  The algorithm works conceptually like this:
1419     1.  Resolve the set of overloads it is *possible* to call.
1420         Impossible overloads include those that have too many parameters or have parameters 
1421         of unknown type.  
1422     2.  Filter the set of overloads to only contain those with the closest number of 
1423         parameters.
1424         For example, if we are called with 3 parameters and there are 2 overloads that
1425         take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1426     3.  Find the best remaining overload based on its match score.  
1427         If two or more overloads have the same match score, call the last one.  The match
1428         score is constructed by adding the matchScore() result for each of the parameters.
1429 */
1430 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data, 
1431                                             QV8Engine *engine, CallArgs &callArgs)
1432 {
1433     int argumentCount = callArgs.Length();
1434
1435     const QDeclarativePropertyCache::Data *best = 0;
1436     int bestParameterScore = INT_MAX;
1437     int bestMatchScore = INT_MAX;
1438
1439     QDeclarativePropertyCache::Data dummy;
1440     const QDeclarativePropertyCache::Data *attempt = &data;
1441
1442     do {
1443         QList<QByteArray> methodArgTypeNames;
1444
1445         if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1446             methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1447
1448         int methodArgumentCount = methodArgTypeNames.count();
1449
1450         if (methodArgumentCount > argumentCount)
1451             continue; // We don't have sufficient arguments to call this method
1452
1453         int methodParameterScore = argumentCount - methodArgumentCount;
1454         if (methodParameterScore > bestParameterScore)
1455             continue; // We already have a better option
1456
1457         int methodMatchScore = 0;
1458         QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1459
1460         bool unknownArgument = false;
1461         for (int ii = 0; ii < methodArgumentCount; ++ii) {
1462             methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1463             if (methodArgTypes[ii] == QVariant::Invalid) 
1464                 methodArgTypes[ii] = EnumType(object->metaObject(), 
1465                                               QString::fromLatin1(methodArgTypeNames.at(ii)));
1466             if (methodArgTypes[ii] == QVariant::Invalid) {
1467                 unknownArgument = true;
1468                 break;
1469             }
1470             methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1471         }
1472         if (unknownArgument)
1473             continue; // We don't understand all the parameters
1474
1475         if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1476             best = attempt;
1477             bestParameterScore = methodParameterScore;
1478             bestMatchScore = methodMatchScore;
1479         }
1480
1481         if (bestParameterScore == 0 && bestMatchScore == 0)
1482             break; // We can't get better than that
1483
1484     } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1485
1486     if (best) {
1487         return CallPrecise(object, *best, engine, callArgs);
1488     } else {
1489         QString error = QLatin1String("Unable to determine callable overload.  Candidates are:");
1490         const QDeclarativePropertyCache::Data *candidate = &data;
1491         while (candidate) {
1492             error += QLatin1String("\n    ") + 
1493                      QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1494             candidate = RelatedMethod(object, candidate, dummy);
1495         }
1496
1497         v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1498         return v8::Handle<v8::Value>();
1499     }
1500 }
1501
1502 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1503 {
1504     QString result;
1505     if (object) {
1506         QString objectName = object->objectName();
1507
1508         result += QString::fromUtf8(object->metaObject()->className());
1509         result += QLatin1String("(0x");
1510         result += QString::number((quintptr)object,16);
1511
1512         if (!objectName.isEmpty()) {
1513             result += QLatin1String(", \"");
1514             result += objectName;
1515             result += QLatin1Char('\"');
1516         }
1517
1518         result += QLatin1Char(')');
1519     } else {
1520         result = QLatin1String("null");
1521     }
1522
1523     return engine->toString(result);
1524 }
1525
1526 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1527 {
1528     QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1529     if (!ddata || ddata->indestructible) {
1530         const char *error = "Invalid attempt to destroy() an indestructible object";
1531         v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1532         return v8::Undefined();
1533     }
1534
1535     int delay = 0;
1536     if (argCount > 0)
1537         delay = args->Get(0)->Uint32Value();
1538
1539     if (delay > 0)
1540         QTimer::singleShot(delay, object, SLOT(deleteLater()));
1541     else
1542         object->deleteLater();
1543
1544     return v8::Undefined();
1545 }
1546
1547 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1548 {
1549     // object, index, qmlglobal, argCount, args
1550     Q_ASSERT(args.Length() == 5);
1551     Q_ASSERT(args[0]->IsObject());
1552
1553     QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1554
1555     if (!resource)
1556         return v8::Undefined();
1557
1558     int argCount = args[3]->Int32Value();
1559     v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1560
1561     // Special hack to return info about this closure.
1562     if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1563         v8::Local<v8::Array> data = v8::Array::New(2);
1564         data->Set(0, args[0]);
1565         data->Set(1, args[1]);
1566         return data;
1567     }
1568
1569     QObject *object = resource->object;
1570     int index = args[1]->Int32Value();
1571
1572     if (!object)
1573         return v8::Undefined();
1574
1575     if (index < 0) {
1576         // Builtin functions
1577         if (index == QOBJECT_TOSTRING_INDEX) {
1578             return ToString(resource->engine, object, argCount, arguments);
1579         } else if (index == QOBJECT_DESTROY_INDEX) {
1580             return Destroy(resource->engine, object, argCount, arguments);
1581         } else {
1582             return v8::Undefined();
1583         }
1584     }
1585
1586     QDeclarativePropertyCache::Data method;
1587
1588     if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1589         if (ddata->propertyCache) {
1590             QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1591             if (!d) 
1592                 return v8::Undefined();
1593             method = *d;
1594         } 
1595     }
1596
1597     if (method.coreIndex == -1) {
1598         QMetaMethod mm = object->metaObject()->method(index);
1599         method.load(object->metaObject()->method(index));
1600
1601         if (method.coreIndex == -1)
1602             return v8::Undefined();
1603     }
1604
1605     if (method.flags & QDeclarativePropertyCache::Data::IsV8Function) {
1606         v8::Handle<v8::Value> rv;
1607         v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1608
1609         QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal, 
1610                                     resource->engine->contextWrapper()->context(qmlglobal),
1611                                     resource->engine);
1612         QDeclarativeV8Function *funcptr = &func;
1613
1614         void *args[] = { 0, &funcptr };
1615         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1616
1617         if (rv.IsEmpty()) return v8::Undefined();
1618         return rv;
1619     }
1620
1621     CallArgs callArgs(argCount, &arguments);
1622     if (method.relatedIndex == -1) {
1623         return CallPrecise(object, method, resource->engine, callArgs);
1624     } else {
1625         return CallOverloaded(object, method, resource->engine, callArgs);
1626     }
1627 }
1628
1629 MetaCallArgument::MetaCallArgument()
1630 : type(QVariant::Invalid), isObjectType(false)
1631 {
1632 }
1633
1634 MetaCallArgument::~MetaCallArgument()
1635 {
1636     cleanup();
1637 }
1638
1639 void MetaCallArgument::cleanup()
1640 {
1641     if (type == QMetaType::QString) {
1642         ((QString *)&data)->~QString();
1643     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1644         ((QVariant *)&data)->~QVariant();
1645     } else if (type == qMetaTypeId<QScriptValue>()) {
1646         ((QScriptValue *)&data)->~QScriptValue();
1647     } else if (type == qMetaTypeId<QList<QObject *> >()) {
1648         ((QList<QObject *> *)&data)->~QList<QObject *>();
1649     }
1650 }
1651
1652 void *MetaCallArgument::dataPtr()
1653 {
1654     if (type == -1)
1655         return ((QVariant *)data)->data();
1656     else
1657         return (void *)&data;
1658 }
1659
1660 void MetaCallArgument::initAsType(int callType)
1661 {
1662     if (type != 0) { cleanup(); type = 0; }
1663     if (callType == 0) return;
1664
1665     if (callType == qMetaTypeId<QScriptValue>()) {
1666         new (&data) QScriptValue();
1667         type = callType;
1668     } else if (callType == QMetaType::Int ||
1669                callType == QMetaType::UInt ||
1670                callType == QMetaType::Bool ||
1671                callType == QMetaType::Double ||
1672                callType == QMetaType::Float) {
1673         type = callType;
1674     } else if (callType == QMetaType::QObjectStar) {
1675         *((QObject **)&data) = 0;
1676         type = callType;
1677     } else if (callType == QMetaType::QString) {
1678         new (&data) QString();
1679         type = callType;
1680     } else if (callType == qMetaTypeId<QVariant>()) {
1681         type = callType;
1682         new (&data) QVariant();
1683     } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1684         type = callType;
1685         new (&data) QList<QObject *>();
1686     } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1687         type = callType;
1688         new (&data) v8::Handle<v8::Value>();
1689     } else {
1690         type = -1;
1691         new (&data) QVariant(callType, (void *)0);
1692     }
1693 }
1694
1695 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1696 {
1697     if (type != 0) { cleanup(); type = 0; }
1698
1699     if (callType == qMetaTypeId<QScriptValue>()) {
1700         new (&data) QScriptValue();
1701         type = qMetaTypeId<QScriptValue>();
1702     } else if (callType == QMetaType::Int) {
1703         *((int *)&data) = int(value->Int32Value());
1704         type = callType;
1705     } else if (callType == QMetaType::UInt) {
1706         *((uint *)&data) = uint(value->Uint32Value());
1707         type = callType;
1708     } else if (callType == QMetaType::Bool) {
1709         *((bool *)&data) = value->BooleanValue();
1710         type = callType;
1711     } else if (callType == QMetaType::Double) {
1712         *((double *)&data) = double(value->NumberValue());
1713         type = callType;
1714     } else if (callType == QMetaType::Float) {
1715         *((float *)&data) = float(value->NumberValue());
1716         type = callType;
1717     } else if (callType == QMetaType::QString) {
1718         if (value->IsNull() || value->IsUndefined())
1719             new (&data) QString();
1720         else
1721             new (&data) QString(engine->toString(value->ToString()));
1722         type = callType;
1723     } else if (callType == QMetaType::QObjectStar) {
1724         *((QObject **)&data) = engine->toQObject(value);
1725         type = callType;
1726     } else if (callType == qMetaTypeId<QVariant>()) {
1727         new (&data) QVariant(engine->toVariant(value, -1));
1728         type = callType;
1729     } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1730         QList<QObject *> *list = new (&data) QList<QObject *>(); 
1731         if (value->IsArray()) {
1732             v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1733             uint32_t length = array->Length();
1734             for (uint32_t ii = 0; ii < length; ++ii) 
1735                 list->append(engine->toQObject(array->Get(ii)));
1736         } else {
1737             list->append(engine->toQObject(value));
1738         }
1739         type = callType;
1740     } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1741         new (&data) v8::Handle<v8::Value>(value);
1742         type = callType;
1743     } else {
1744         new (&data) QVariant();
1745         type = -1;
1746
1747         QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
1748         QVariant v = engine->toVariant(value, -1);
1749
1750         if (v.userType() == callType) {
1751             *((QVariant *)&data) = v;
1752         } else if (v.canConvert((QVariant::Type)callType)) {
1753             *((QVariant *)&data) = v;
1754             ((QVariant *)&data)->convert((QVariant::Type)callType);
1755         } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) {
1756             QObject *obj = ep->toQObject(v);
1757             
1758             if (obj) {
1759                 const QMetaObject *objMo = obj->metaObject();
1760                 while (objMo && objMo != mo) objMo = objMo->superClass();
1761                 if (!objMo) obj = 0;
1762             }
1763
1764             *((QVariant *)&data) = QVariant(callType, &obj);
1765         } else {
1766             *((QVariant *)&data) = QVariant(callType, (void *)0);
1767         }
1768     }
1769 }
1770
1771 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1772 {
1773     if (type == qMetaTypeId<QScriptValue>()) {
1774         return v8::Undefined();
1775     } else if (type == QMetaType::Int) {
1776         return v8::Integer::New(*((int *)&data));
1777     } else if (type == QMetaType::UInt) {
1778         return v8::Integer::NewFromUnsigned(*((uint *)&data));
1779     } else if (type == QMetaType::Bool) {
1780         return v8::Boolean::New(*((bool *)&data));
1781     } else if (type == QMetaType::Double) {
1782         return v8::Number::New(*((double *)&data));
1783     } else if (type == QMetaType::Float) {
1784         return v8::Number::New(*((float *)&data));
1785     } else if (type == QMetaType::QString) {
1786         return engine->toString(*((QString *)&data));
1787     } else if (type == QMetaType::QObjectStar) {
1788         QObject *object = *((QObject **)&data);
1789         if (object)
1790             QDeclarativeData::get(object, true)->setImplicitDestructible();
1791         return engine->newQObject(object);
1792     } else if (type == qMetaTypeId<QList<QObject *> >()) {
1793         // XXX aakenned Can this be more optimal?  Just use Array as a prototype and 
1794         // implement directly against QList<QObject*>?
1795         QList<QObject *> &list = *(QList<QObject *>*)&data;
1796         v8::Local<v8::Array> array = v8::Array::New(list.count());
1797         for (int ii = 0; ii < list.count(); ++ii) 
1798             array->Set(ii, engine->newQObject(list.at(ii)));
1799         return array;
1800     } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
1801         return *(v8::Handle<v8::Value>*)&data;
1802     } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1803         QVariant value = *((QVariant *)&data);
1804         v8::Handle<v8::Value> rv = engine->fromVariant(value);
1805         if (QObject *object = engine->toQObject(rv)) 
1806             QDeclarativeData::get(object, true)->setImplicitDestructible();
1807         return rv;
1808     } else {
1809         return v8::Undefined();
1810     }
1811 }