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