1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qv8qobjectwrapper_p.h"
43 #include "qv8contextwrapper_p.h"
44 #include "qv8engine_p.h"
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>
52 #include <QtScript/qscriptvalue.h>
53 #include <QtCore/qvarlengtharray.h>
54 #include <QtCore/qtimer.h>
55 #include <QtCore/qatomic.h>
59 Q_DECLARE_METATYPE(QScriptValue);
60 Q_DECLARE_METATYPE(QDeclarativeV8Handle);
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"
70 #define QOBJECT_TOSTRING_INDEX -2
71 #define QOBJECT_DESTROY_INDEX -3
73 // XXX TODO: Need to review all calls to QDeclarativeEngine *engine() to confirm QObjects work
74 // correctly in a worker thread
76 class QV8QObjectResource : public QV8ObjectResource
78 V8_RESOURCE_TYPE(QObjectType);
81 QV8QObjectResource(QV8Engine *engine, QObject *object);
83 QDeclarativeGuard<QObject> object;
86 class QV8QObjectInstance : public QDeclarativeGuard<QObject>
89 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
90 : QDeclarativeGuard<QObject>(o), wrapper(w)
96 qPersistentDispose(v8object);
99 virtual void objectDestroyed(QObject *o)
102 wrapper->m_taintedObjects.remove(o);
106 v8::Persistent<v8::Object> v8object;
107 QV8QObjectWrapper *wrapper;
111 struct MetaCallArgument {
112 inline MetaCallArgument();
113 inline ~MetaCallArgument();
114 inline void *dataPtr();
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 *);
121 MetaCallArgument(const MetaCallArgument &);
123 inline void cleanup();
125 char data[4 * sizeof(void *)];
131 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
132 : QV8ObjectResource(engine), object(object)
136 static QAtomicInt objectIdCounter(1);
138 QV8QObjectWrapper::QV8QObjectWrapper()
139 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
143 QV8QObjectWrapper::~QV8QObjectWrapper()
145 for (TaintedHash::Iterator iter = m_taintedObjects.begin();
146 iter != m_taintedObjects.end();
148 (*iter)->wrapper = 0;
150 m_taintedObjects.clear();
153 void QV8QObjectWrapper::destroy()
155 qDeleteAll(m_connections);
156 m_connections.clear();
158 qPersistentDispose(m_hiddenObject);
159 qPersistentDispose(m_destroySymbol);
160 qPersistentDispose(m_toStringSymbol);
161 qPersistentDispose(m_methodConstructor);
162 qPersistentDispose(m_constructor);
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) \
168 v8::Handle<v8::Object> This = info.This(); \
169 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(This); \
171 if (!resource || resource->object.isNull()) return v8::Undefined(); \
173 QObject *object = resource->object; \
175 uint32_t data = info.Data()->Uint32Value(); \
176 int index = data & 0x7FFF; \
177 int notify = (data & 0x7FFF0000) >> 16; \
178 if (notify == 0x7FFF) notify = -1; \
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); \
186 cpptype value = defaultvalue; \
187 void *args[] = { &value, 0 }; \
188 QMetaObject::metacall(object, QMetaObject::ReadProperty, index, args); \
190 return constructor(value); \
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); "\
202 void QV8QObjectWrapper::init(QV8Engine *engine)
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());
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());
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);
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));
233 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
235 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
238 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
240 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
241 return r?r->object:0;
244 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
245 QObject *QV8QObjectWrapper::QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
247 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
248 return static_cast<QV8QObjectResource *>(r)->object;
251 // Load value properties
252 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
253 const QDeclarativePropertyCache::Data &property)
255 Q_ASSERT(!property.isFunction());
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); \
265 if (property.isQObject()) {
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];
288 return engine->newValueType(object, property.coreIndex, valueType);
291 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
292 return engine->fromVariant(var);
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)
300 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
301 // will be a faster way of creating QObject method objects.
302 struct MethodClosure {
303 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
304 v8::Handle<v8::Value> *objectHandle,
306 v8::Handle<v8::Value> argv[] = {
307 objectHandle?*objectHandle:engine->newQObject(object),
308 v8::Integer::New(index)
310 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
312 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
313 v8::Handle<v8::Value> *objectHandle,
315 v8::Handle<v8::Value> argv[] = {
316 objectHandle?*objectHandle:engine->newQObject(object),
317 v8::Integer::New(index),
318 v8::Context::GetCallingQmlGlobal()
320 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
324 if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property)) {
325 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
326 } else if (engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property)) {
327 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
330 QDeclarativePropertyCache::Data local;
331 QDeclarativePropertyCache::Data *result = 0;
332 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
335 return v8::Handle<v8::Value>();
337 QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
339 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
340 QDeclarativeData *ddata = QDeclarativeData::get(object);
341 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
342 return v8::Handle<v8::Value>();
345 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
347 if (result->isFunction()) {
348 if (result->flags & QDeclarativePropertyCache::Data::IsVMEFunction) {
349 return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
350 } else if (result->flags & QDeclarativePropertyCache::Data::IsV8Function) {
351 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
353 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
357 if (ep && ep->captureProperties && !result->isConstant()) {
358 if (result->coreIndex == 0)
359 ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
361 ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
364 return LoadProperty(engine, object, *result);
367 // Setter for writable properties. Shared between the interceptor and fast property accessor
368 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property,
369 v8::Handle<v8::Value> value)
371 QDeclarativeBinding *newBinding = 0;
373 if (value->IsFunction()) {
374 QDeclarativeContextData *context = engine->callingContext();
375 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
377 v8::Local<v8::StackTrace> trace =
378 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
379 v8::StackTrace::kScriptName));
380 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
381 int lineNumber = frame->GetLineNumber();
382 QString url = engine->toString(frame->GetScriptName());
384 QDeclarativePropertyCache::ValueTypeData valueTypeData;
385 newBinding = new QDeclarativeBinding(&function, object, context);
386 newBinding->setSourceLocation(url, lineNumber);
387 newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context));
388 newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
391 QDeclarativeAbstractBinding *oldBinding =
392 QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
394 oldBinding->destroy();
396 #define PROPERTY_STORE(cpptype, value) \
400 void *argv[] = { &o, 0, &status, &flags }; \
401 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
404 if (value->IsNull() && property->isQObject()) {
405 PROPERTY_STORE(QObject*, 0);
406 } else if (value->IsUndefined() && property->isResettable()) {
408 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
409 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
410 PROPERTY_STORE(QVariant, QVariant());
411 } else if (value->IsUndefined()) {
412 QString error = QLatin1String("Cannot assign [undefined] to ") +
413 QLatin1String(QMetaType::typeName(property->propType));
414 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
415 } else if (value->IsFunction()) {
416 // this is handled by the binding creation above
417 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
418 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
419 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
420 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
421 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
422 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
423 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
424 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
425 } else if (property->propType == QMetaType::QString && value->IsString()) {
426 PROPERTY_STORE(QString, engine->toString(value->ToString()));
429 if (property->isQList())
430 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
432 v = engine->toVariant(value, property->propType);
434 QDeclarativeContextData *context = engine->callingContext();
436 if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
437 const char *valueType = 0;
438 if (v.userType() == QVariant::Invalid) valueType = "null";
439 else valueType = QMetaType::typeName(v.userType());
441 QString error = QLatin1String("Cannot assign ") +
442 QLatin1String(valueType) +
443 QLatin1String(" to ") +
444 QLatin1String(QMetaType::typeName(property->propType));
445 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
450 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, v8::Handle<v8::String> property,
451 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
453 if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property) ||
454 engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property))
457 QDeclarativePropertyCache::Data local;
458 QDeclarativePropertyCache::Data *result = 0;
459 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
464 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
465 QDeclarativeData *ddata = QDeclarativeData::get(object);
466 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
470 if (!result->isWritable() && !result->isQList()) {
471 QString error = QLatin1String("Cannot assign to read-only property \"") +
472 engine->toString(property) + QLatin1Char('\"');
473 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
477 StoreProperty(engine, object, result, value);
482 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
483 const v8::AccessorInfo &info)
485 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
486 v8::Handle<v8::Value> This = info.This();
488 if (!resource || resource->object.isNull()) return v8::Undefined();
490 QObject *object = resource->object;
492 QV8Engine *v8engine = resource->engine;
493 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, property, QV8QObjectWrapper::IgnoreRevision);
494 if (!result.IsEmpty())
497 if (QV8Engine::startsWithUpper(property)) {
498 // Check for attached properties
499 QDeclarativeContextData *context = v8engine->callingContext();
500 QDeclarativeTypeNameCache::Data *data = context && (context->imports)?context->imports->data(property):0;
504 return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums);
505 } else if (data->typeNamespace) {
506 return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
511 return v8::Undefined();
514 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
515 v8::Local<v8::Value> value,
516 const v8::AccessorInfo &info)
518 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
520 if (!resource || resource->object.isNull())
523 QObject *object = resource->object;
525 QV8Engine *v8engine = resource->engine;
526 bool result = SetProperty(v8engine, object, property, value, QV8QObjectWrapper::IgnoreRevision);
529 QString error = QLatin1String("Cannot assign to non-existent property \"") +
530 v8engine->toString(property) + QLatin1Char('\"');
531 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
538 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
539 const v8::AccessorInfo &info)
541 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
543 if (!resource || resource->object.isNull())
544 return v8::Handle<v8::Integer>();
546 QV8Engine *engine = resource->engine;
547 QObject *object = resource->object;
549 QDeclarativePropertyCache::Data local;
550 QDeclarativePropertyCache::Data *result = 0;
551 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
554 return v8::Handle<v8::Integer>();
555 else if (!result->isWritable() && !result->isQList())
556 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
558 return v8::Integer::New(v8::DontDelete);
561 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
563 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
565 if (!resource || resource->object.isNull())
566 return v8::Array::New();
568 QObject *object = resource->object;
572 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine());
574 QDeclarativePropertyCache *cache = 0;
575 QDeclarativeData *ddata = QDeclarativeData::get(object);
577 cache = ddata->propertyCache;
580 cache = ep->cache(object);
582 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
584 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
585 const QMetaObject *mo = object->metaObject();
586 int pc = mo->propertyCount();
587 int po = mo->propertyOffset();
588 for (int i=po; i<pc; ++i)
589 result << QString::fromUtf8(mo->property(i).name());
592 result = cache->propertyNames();
595 v8::Local<v8::Array> rv = v8::Array::New(result.count());
597 for (int ii = 0; ii < result.count(); ++ii)
598 rv->Set(ii, resource->engine->toString(result.at(ii)));
603 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
604 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
605 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
606 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
607 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
608 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
609 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
611 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
612 const v8::AccessorInfo& info)
614 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
616 if (!resource || resource->object.isNull())
619 QObject *object = resource->object;
621 uint32_t data = info.Data()->Uint32Value();
622 int index = data & 0x7FFF; // So that we can use the same data for Setter and Getter
624 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
626 Q_ASSERT(ddata->propertyCache);
628 QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index);
631 Q_ASSERT(pdata->isWritable() || pdata->isQList());
633 StoreProperty(resource->engine, object, pdata, value);
636 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
638 Q_ASSERT(handle->IsObject());
640 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(handle->ToObject());
644 QObject *object = resource->object;
646 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
648 ddata->v8object.Clear();
649 if (!object->parent() && !ddata->indestructible)
650 object->deleteLater();
654 qPersistentDispose(handle);
657 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
659 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
660 instance->v8object.Clear();
661 qPersistentDispose(handle);
664 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
668 Q_ASSERT(QDeclarativeData::get(object, false));
669 Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
672 if (constructor.IsEmpty()) {
673 v8::Local<v8::FunctionTemplate> ft;
675 QString toString = QLatin1String("toString");
676 QString destroy = QLatin1String("destroy");
678 // XXX TODO: Enables fast property accessors. These more than double the property access
679 // performance, but the cost of setting up this structure hasn't been measured so
680 // its not guarenteed that this is a win overall. We need to try and measure the cost.
681 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
682 Data *property = *iter;
683 if (property->isFunction() || !property->isWritable() ||
684 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x7FFF ||
685 property->coreIndex == 0)
688 v8::AccessorGetter fastgetter = 0;
691 if (property->isQObject())
692 fastgetter = QObjectValueGetter;
693 else if (property->propType == QMetaType::Int || property->isEnum())
694 fastgetter = IntValueGetter;
695 else if (property->propType == QMetaType::Bool)
696 fastgetter = BoolValueGetter;
697 else if (property->propType == QMetaType::QString)
698 fastgetter = QStringValueGetter;
699 else if (property->propType == QMetaType::UInt)
700 fastgetter = UIntValueGetter;
701 else if (property->propType == QMetaType::Float)
702 fastgetter = FloatValueGetter;
703 else if (property->propType == QMetaType::Double)
704 fastgetter = DoubleValueGetter;
707 int notifyIndex = property->notifyIndex;
708 if (property->isConstant()) notifyIndex = 0;
709 else if (notifyIndex == -1) notifyIndex = 0x7FFF;
710 uint32_t data = (property->notifyIndex & 0x7FFF) << 16 | property->coreIndex;
712 QString name = iter.key();
713 if (name == toString || name == destroy)
717 ft = v8::FunctionTemplate::New();
718 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
719 QV8QObjectWrapper::Setter,
720 QV8QObjectWrapper::Query,
722 QV8QObjectWrapper::Enumerator);
723 ft->InstanceTemplate()->SetHasExternalResource(true);
726 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, FastValueSetter,
727 v8::Integer::NewFromUnsigned(data));
732 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
734 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
735 QV8QObjectWrapper::Setter,
736 QV8QObjectWrapper::Query,
738 QV8QObjectWrapper::Enumerator);
739 ft->InstanceTemplate()->SetHasExternalResource(true);
740 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
744 v8::Local<v8::Object> result = constructor->NewInstance();
745 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
746 result->SetExternalResource(r);
750 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
752 v8::Local<v8::Object> rv;
754 if (ddata->propertyCache) {
755 rv = ddata->propertyCache->newQObject(object, engine);
757 // XXX NewInstance() should be optimized
758 rv = m_constructor->NewInstance();
759 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
760 rv->SetExternalResource(r);
767 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
768 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
770 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
771 persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that
772 "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this
774 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create
775 an entry in the m_taintedObject hash where we store the handle and mark the object as
776 "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
777 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
778 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has
781 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
786 if (QObjectPrivate::get(object)->wasDeleted)
787 return v8::Undefined();
789 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
792 return v8::Undefined();
794 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
795 // We own the v8object
796 return v8::Local<v8::Object>::New(ddata->v8object);
797 } else if (ddata->v8object.IsEmpty() &&
798 (ddata->v8objectid == m_id || // We own the QObject
799 ddata->v8objectid == 0 || // No one owns the QObject
800 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
802 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
803 ddata->v8object = qPersistentNew<v8::Object>(rv);
804 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
805 ddata->v8objectid = m_id;
809 // If this object is tainted, we have to check to see if it is in our
810 // tainted object list
811 TaintedHash::Iterator iter =
812 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
813 bool found = iter != m_taintedObjects.end();
815 // If our tainted handle doesn't exist or has been collected, and there isn't
816 // a handle in the ddata, we can assume ownership of the ddata->v8object
817 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
818 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
819 ddata->v8object = qPersistentNew<v8::Object>(rv);
820 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
821 ddata->v8objectid = m_id;
825 m_taintedObjects.erase(iter);
830 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
831 iter = m_taintedObjects.insert(object, instance);
832 ddata->hasTaintedV8Object = true;
835 if ((*iter)->v8object.IsEmpty()) {
836 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
837 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
838 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
841 return v8::Local<v8::Object>::New((*iter)->v8object);
845 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
847 v8::ScriptOrigin origin = function->GetScriptOrigin();
848 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
850 // This is one of our special QObject method wrappers
851 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
852 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
854 if (data->IsArray()) {
855 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
856 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
859 // In theory this can't fall through, but I suppose V8 might run out of memory or something
862 return qMakePair((QObject *)0, -1);
865 struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
867 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
868 ~QV8QObjectConnectionList();
872 : needsDestroy(false) {}
873 Connection(const Connection &other)
874 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
875 Connection &operator=(const Connection &other) {
876 thisObject = other.thisObject;
877 function = other.function;
878 needsDestroy = other.needsDestroy;
882 v8::Persistent<v8::Object> thisObject;
883 v8::Persistent<v8::Function> function;
886 qPersistentDispose(thisObject);
887 qPersistentDispose(function);
893 struct ConnectionList : public QList<Connection> {
894 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
895 int connectionsInUse;
896 bool connectionsNeedClean;
901 typedef QHash<int, ConnectionList> SlotHash;
906 virtual void objectDestroyed(QObject *);
907 virtual int qt_metacall(QMetaObject::Call, int, void **);
910 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
911 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
915 QV8QObjectConnectionList::~QV8QObjectConnectionList()
917 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
918 QList<Connection> &connections = *iter;
919 for (int ii = 0; ii < connections.count(); ++ii) {
920 qPersistentDispose(connections[ii].thisObject);
921 qPersistentDispose(connections[ii].function);
927 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
929 engine->qobjectWrapper()->m_connections.remove(object);
937 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
939 if (method == QMetaObject::InvokeMetaMethod) {
940 SlotHash::Iterator iter = slotHash.find(index);
941 if (iter == slotHash.end())
943 ConnectionList &connectionList = *iter;
944 if (connectionList.isEmpty())
949 connectionList.connectionsInUse++;
951 QList<Connection> connections = connectionList;
953 QMetaMethod method = data()->metaObject()->method(index);
954 Q_ASSERT(method.methodType() == QMetaMethod::Signal);
955 // XXX TODO: We should figure out a way to cache the parameter types to avoid resolving
957 QList<QByteArray> params = method.parameterTypes();
959 v8::HandleScope handle_scope;
960 v8::Context::Scope scope(engine->context());
962 QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
963 int argCount = params.count();
965 for (int ii = 0; ii < argCount; ++ii) {
966 int type = QMetaType::type(params.at(ii).constData());
967 if (type == qMetaTypeId<QVariant>()) {
968 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
970 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
974 for (int ii = 0; ii < connections.count(); ++ii) {
975 Connection &connection = connections[ii];
976 if (connection.needsDestroy)
978 if (connection.thisObject.IsEmpty()) {
979 connection.function->Call(engine->global(), argCount, args.data());
981 connection.function->Call(connection.thisObject, argCount, args.data());
985 connectionList.connectionsInUse--;
986 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
987 for (QList<Connection>::Iterator iter = connectionList.begin();
988 iter != connectionList.end(); ) {
989 if (iter->needsDestroy) {
991 iter = connectionList.erase(iter);
999 if (inUse == 0 && needsDestroy)
1006 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1008 if (args.Length() == 0)
1009 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1011 QV8Engine *engine = V8ENGINE();
1013 if (!args.This()->IsFunction())
1014 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1016 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1017 QObject *signalObject = signalInfo.first;
1018 int signalIndex = signalInfo.second;
1020 if (signalIndex == -1)
1021 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1024 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1026 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1027 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1029 v8::Local<v8::Value> functionValue;
1030 v8::Local<v8::Value> functionThisValue;
1032 if (args.Length() == 1) {
1033 functionValue = args[0];
1035 functionThisValue = args[0];
1036 functionValue = args[1];
1039 if (!functionValue->IsFunction())
1040 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1042 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1043 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1045 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1046 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1047 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1048 if (iter == connections.end())
1049 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1051 QV8QObjectConnectionList *connectionList = *iter;
1052 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1053 if (slotIter == connectionList->slotHash.end()) {
1054 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1055 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1058 QV8QObjectConnectionList::Connection connection;
1059 if (!functionThisValue.IsEmpty())
1060 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1061 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1063 slotIter->append(connection);
1065 return v8::Undefined();
1068 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1070 if (args.Length() == 0)
1071 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1073 QV8Engine *engine = V8ENGINE();
1075 if (!args.This()->IsFunction())
1076 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1078 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1079 QObject *signalObject = signalInfo.first;
1080 int signalIndex = signalInfo.second;
1082 if (signalIndex == -1)
1083 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1086 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1088 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1089 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1091 v8::Local<v8::Value> functionValue;
1092 v8::Local<v8::Value> functionThisValue;
1094 if (args.Length() == 1) {
1095 functionValue = args[0];
1097 functionThisValue = args[0];
1098 functionValue = args[1];
1101 if (!functionValue->IsFunction())
1102 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1104 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1105 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1107 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1108 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1109 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1110 if (iter == connectionsList.end())
1111 return v8::Undefined(); // Nothing to disconnect from
1113 QV8QObjectConnectionList *connectionList = *iter;
1114 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1115 if (slotIter == connectionList->slotHash.end())
1116 return v8::Undefined(); // Nothing to disconnect from
1118 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1120 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1121 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1123 if (functionData.second != -1) {
1124 // This is a QObject function wrapper
1125 for (int ii = 0; ii < connections.count(); ++ii) {
1126 QV8QObjectConnectionList::Connection &connection = connections[ii];
1128 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1129 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1131 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1132 if (connectedFunctionData == functionData) {
1134 if (connections.connectionsInUse) {
1135 connection.needsDestroy = true;
1137 connection.dispose();
1138 connections.removeAt(ii);
1140 return v8::Undefined();
1146 // This is a normal JS function
1147 for (int ii = 0; ii < connections.count(); ++ii) {
1148 QV8QObjectConnectionList::Connection &connection = connections[ii];
1149 if (connection.function->StrictEquals(function) &&
1150 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1151 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1153 if (connections.connectionsInUse) {
1154 connection.needsDestroy = true;
1156 connection.dispose();
1157 connections.removeAt(ii);
1159 return v8::Undefined();
1164 return v8::Undefined();
1168 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1170 Only searches for real properties of \a object (including methods), not attached properties etc.
1172 v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, v8::Handle<v8::String> property,
1173 QV8QObjectWrapper::RevisionMode revisionMode)
1175 return GetProperty(m_engine, object, 0, property, revisionMode);
1179 Set the \a property of \a object to \a value.
1181 Returns true if the property was "set" - even if this results in an exception being thrown -
1182 and false if the object has no such property.
1184 Only searches for real properties of \a object (including methods), not attached properties etc.
1186 bool QV8QObjectWrapper::setProperty(QObject *object, v8::Handle<v8::String> property,
1187 v8::Handle<v8::Value> value, RevisionMode revisionMode)
1189 return SetProperty(m_engine, object, property, value, revisionMode);
1195 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1196 int Length() const { return _length; }
1197 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1201 v8::Handle<v8::Object> *_args;
1205 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1206 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1210 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1211 args[0].initAsType(returnType);
1213 for (int ii = 0; ii < argCount; ++ii)
1214 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1216 QVarLengthArray<void *, 9> argData(args.count());
1217 for (int ii = 0; ii < args.count(); ++ii)
1218 argData[ii] = args[ii].dataPtr();
1220 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1222 return args[0].toValue(engine);
1224 } else if (returnType != 0) {
1226 MetaCallArgument arg;
1227 arg.initAsType(returnType);
1229 void *args[] = { arg.dataPtr() };
1231 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1233 return arg.toValue(engine);
1237 void *args[] = { 0 };
1238 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1239 return v8::Undefined();
1244 static int EnumType(const QMetaObject *meta, const QString &strname)
1246 QByteArray str = strname.toUtf8();
1249 int scopeIdx = str.lastIndexOf("::");
1250 if (scopeIdx != -1) {
1251 scope = str.left(scopeIdx);
1252 name = str.mid(scopeIdx + 2);
1256 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1257 QMetaEnum m = meta->enumerator(i);
1258 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1259 return QVariant::Int;
1261 return QVariant::Invalid;
1265 Returns the match score for converting \a actual to be of type \a conversionType. A
1266 zero score means "perfect match" whereas a higher score is worse.
1268 The conversion table is copied out of the QtScript callQtMethod() function.
1270 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
1271 const QByteArray &conversionTypeName)
1273 if (actual->IsNumber()) {
1274 switch (conversionType) {
1275 case QMetaType::Double:
1277 case QMetaType::Float:
1279 case QMetaType::LongLong:
1280 case QMetaType::ULongLong:
1282 case QMetaType::Long:
1283 case QMetaType::ULong:
1285 case QMetaType::Int:
1286 case QMetaType::UInt:
1288 case QMetaType::Short:
1289 case QMetaType::UShort:
1292 case QMetaType::Char:
1293 case QMetaType::UChar:
1298 } else if (actual->IsString()) {
1299 switch (conversionType) {
1300 case QMetaType::QString:
1305 } else if (actual->IsBoolean()) {
1306 switch (conversionType) {
1307 case QMetaType::Bool:
1312 } else if (actual->IsDate()) {
1313 switch (conversionType) {
1314 case QMetaType::QDateTime:
1316 case QMetaType::QDate:
1318 case QMetaType::QTime:
1323 } else if (actual->IsRegExp()) {
1324 switch (conversionType) {
1325 case QMetaType::QRegExp:
1330 } else if (actual->IsArray()) {
1331 switch (conversionType) {
1332 case QMetaType::QStringList:
1333 case QMetaType::QVariantList:
1338 } else if (actual->IsNull()) {
1339 switch (conversionType) {
1340 case QMetaType::VoidStar:
1341 case QMetaType::QObjectStar:
1344 if (!conversionTypeName.endsWith('*'))
1349 } else if (actual->IsObject()) {
1350 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1352 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1353 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1354 switch (conversionType) {
1355 case QMetaType::QObjectStar:
1360 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1361 if (conversionType == qMetaTypeId<QVariant>())
1363 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1376 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1382 int classInfoCount, classInfoData;
1383 int methodCount, methodData;
1386 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1389 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1391 QByteArray sig = m.signature();
1392 int paren = sig.indexOf('(');
1396 return sig.left(paren);
1400 Returns the next related method, if one, or 0.
1402 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object,
1403 const QDeclarativePropertyCache::Data *current,
1404 QDeclarativePropertyCache::Data &dummy)
1406 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1407 if (current->relatedIndex == -1)
1411 return cache->method(current->relatedIndex);
1413 const QMetaObject *mo = object->metaObject();
1414 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1416 while (methodOffset > current->relatedIndex) {
1417 mo = mo->superClass();
1418 methodOffset -= QMetaObject_methods(mo);
1421 QMetaMethod method = mo->method(current->relatedIndex);
1424 // Look for overloaded methods
1425 QByteArray methodName = QMetaMethod_name(method);
1426 for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1427 if (methodName == QMetaMethod_name(mo->method(ii))) {
1428 dummy.relatedIndex = ii;
1437 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
1438 QV8Engine *engine, CallArgs &callArgs)
1440 if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
1442 QMetaMethod m = object->metaObject()->method(data.coreIndex);
1443 QList<QByteArray> argTypeNames = m.parameterTypes();
1444 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1447 for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1448 argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1449 if (argTypes[ii] == QVariant::Invalid)
1450 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1451 if (argTypes[ii] == QVariant::Invalid) {
1452 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1453 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1454 return v8::Handle<v8::Value>();
1458 if (argTypes.count() > callArgs.Length()) {
1459 QString error = QLatin1String("Insufficient arguments");
1460 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1461 return v8::Handle<v8::Value>();
1464 return CallMethod(object, data.coreIndex, data.propType, argTypes.count(),
1465 argTypes.data(), engine, callArgs);
1469 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1475 Resolve the overloaded method to call. The algorithm works conceptually like this:
1476 1. Resolve the set of overloads it is *possible* to call.
1477 Impossible overloads include those that have too many parameters or have parameters
1479 2. Filter the set of overloads to only contain those with the closest number of
1481 For example, if we are called with 3 parameters and there are 2 overloads that
1482 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1483 3. Find the best remaining overload based on its match score.
1484 If two or more overloads have the same match score, call the last one. The match
1485 score is constructed by adding the matchScore() result for each of the parameters.
1487 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data,
1488 QV8Engine *engine, CallArgs &callArgs)
1490 int argumentCount = callArgs.Length();
1492 const QDeclarativePropertyCache::Data *best = 0;
1493 int bestParameterScore = INT_MAX;
1494 int bestMatchScore = INT_MAX;
1496 QDeclarativePropertyCache::Data dummy;
1497 const QDeclarativePropertyCache::Data *attempt = &data;
1500 QList<QByteArray> methodArgTypeNames;
1502 if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1503 methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1505 int methodArgumentCount = methodArgTypeNames.count();
1507 if (methodArgumentCount > argumentCount)
1508 continue; // We don't have sufficient arguments to call this method
1510 int methodParameterScore = argumentCount - methodArgumentCount;
1511 if (methodParameterScore > bestParameterScore)
1512 continue; // We already have a better option
1514 int methodMatchScore = 0;
1515 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1517 bool unknownArgument = false;
1518 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1519 methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1520 if (methodArgTypes[ii] == QVariant::Invalid)
1521 methodArgTypes[ii] = EnumType(object->metaObject(),
1522 QString::fromLatin1(methodArgTypeNames.at(ii)));
1523 if (methodArgTypes[ii] == QVariant::Invalid) {
1524 unknownArgument = true;
1527 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1529 if (unknownArgument)
1530 continue; // We don't understand all the parameters
1532 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1534 bestParameterScore = methodParameterScore;
1535 bestMatchScore = methodMatchScore;
1538 if (bestParameterScore == 0 && bestMatchScore == 0)
1539 break; // We can't get better than that
1541 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1544 return CallPrecise(object, *best, engine, callArgs);
1546 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1547 const QDeclarativePropertyCache::Data *candidate = &data;
1549 error += QLatin1String("\n ") +
1550 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1551 candidate = RelatedMethod(object, candidate, dummy);
1554 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1555 return v8::Handle<v8::Value>();
1559 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1563 QString objectName = object->objectName();
1565 result += QString::fromUtf8(object->metaObject()->className());
1566 result += QLatin1String("(0x");
1567 result += QString::number((quintptr)object,16);
1569 if (!objectName.isEmpty()) {
1570 result += QLatin1String(", \"");
1571 result += objectName;
1572 result += QLatin1Char('\"');
1575 result += QLatin1Char(')');
1577 result = QLatin1String("null");
1580 return engine->toString(result);
1583 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1585 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1586 if (!ddata || ddata->indestructible) {
1587 const char *error = "Invalid attempt to destroy() an indestructible object";
1588 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1589 return v8::Undefined();
1594 delay = args->Get(0)->Uint32Value();
1597 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1599 object->deleteLater();
1601 return v8::Undefined();
1604 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1606 // object, index, qmlglobal, argCount, args
1607 Q_ASSERT(args.Length() == 5);
1608 Q_ASSERT(args[0]->IsObject());
1610 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1613 return v8::Undefined();
1615 int argCount = args[3]->Int32Value();
1616 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1618 // Special hack to return info about this closure.
1619 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1620 v8::Local<v8::Array> data = v8::Array::New(2);
1621 data->Set(0, args[0]);
1622 data->Set(1, args[1]);
1626 QObject *object = resource->object;
1627 int index = args[1]->Int32Value();
1630 return v8::Undefined();
1633 // Builtin functions
1634 if (index == QOBJECT_TOSTRING_INDEX) {
1635 return ToString(resource->engine, object, argCount, arguments);
1636 } else if (index == QOBJECT_DESTROY_INDEX) {
1637 return Destroy(resource->engine, object, argCount, arguments);
1639 return v8::Undefined();
1643 QDeclarativePropertyCache::Data method;
1645 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1646 if (ddata->propertyCache) {
1647 QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1649 return v8::Undefined();
1654 if (method.coreIndex == -1) {
1655 QMetaMethod mm = object->metaObject()->method(index);
1656 method.load(object->metaObject()->method(index));
1658 if (method.coreIndex == -1)
1659 return v8::Undefined();
1662 if (method.flags & QDeclarativePropertyCache::Data::IsV8Function) {
1663 v8::Handle<v8::Value> rv;
1664 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1666 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1667 resource->engine->contextWrapper()->context(qmlglobal),
1669 QDeclarativeV8Function *funcptr = &func;
1671 void *args[] = { 0, &funcptr };
1672 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1674 if (rv.IsEmpty()) return v8::Undefined();
1678 CallArgs callArgs(argCount, &arguments);
1679 if (method.relatedIndex == -1) {
1680 return CallPrecise(object, method, resource->engine, callArgs);
1682 return CallOverloaded(object, method, resource->engine, callArgs);
1686 MetaCallArgument::MetaCallArgument()
1687 : type(QVariant::Invalid), isObjectType(false)
1691 MetaCallArgument::~MetaCallArgument()
1696 void MetaCallArgument::cleanup()
1698 if (type == QMetaType::QString) {
1699 ((QString *)&data)->~QString();
1700 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1701 ((QVariant *)&data)->~QVariant();
1702 } else if (type == qMetaTypeId<QScriptValue>()) {
1703 ((QScriptValue *)&data)->~QScriptValue();
1704 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1705 ((QList<QObject *> *)&data)->~QList<QObject *>();
1709 void *MetaCallArgument::dataPtr()
1712 return ((QVariant *)data)->data();
1714 return (void *)&data;
1717 void MetaCallArgument::initAsType(int callType)
1719 if (type != 0) { cleanup(); type = 0; }
1720 if (callType == 0) return;
1722 if (callType == qMetaTypeId<QScriptValue>()) {
1723 new (&data) QScriptValue();
1725 } else if (callType == QMetaType::Int ||
1726 callType == QMetaType::UInt ||
1727 callType == QMetaType::Bool ||
1728 callType == QMetaType::Double ||
1729 callType == QMetaType::Float) {
1731 } else if (callType == QMetaType::QObjectStar) {
1732 *((QObject **)&data) = 0;
1734 } else if (callType == QMetaType::QString) {
1735 new (&data) QString();
1737 } else if (callType == qMetaTypeId<QVariant>()) {
1739 new (&data) QVariant();
1740 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1742 new (&data) QList<QObject *>();
1743 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1745 new (&data) v8::Handle<v8::Value>();
1748 new (&data) QVariant(callType, (void *)0);
1752 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1754 if (type != 0) { cleanup(); type = 0; }
1756 if (callType == qMetaTypeId<QScriptValue>()) {
1757 new (&data) QScriptValue();
1758 type = qMetaTypeId<QScriptValue>();
1759 } else if (callType == QMetaType::Int) {
1760 *((int *)&data) = int(value->Int32Value());
1762 } else if (callType == QMetaType::UInt) {
1763 *((uint *)&data) = uint(value->Uint32Value());
1765 } else if (callType == QMetaType::Bool) {
1766 *((bool *)&data) = value->BooleanValue();
1768 } else if (callType == QMetaType::Double) {
1769 *((double *)&data) = double(value->NumberValue());
1771 } else if (callType == QMetaType::Float) {
1772 *((float *)&data) = float(value->NumberValue());
1774 } else if (callType == QMetaType::QString) {
1775 if (value->IsNull() || value->IsUndefined())
1776 new (&data) QString();
1778 new (&data) QString(engine->toString(value->ToString()));
1780 } else if (callType == QMetaType::QObjectStar) {
1781 *((QObject **)&data) = engine->toQObject(value);
1783 } else if (callType == qMetaTypeId<QVariant>()) {
1784 new (&data) QVariant(engine->toVariant(value, -1));
1786 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1787 QList<QObject *> *list = new (&data) QList<QObject *>();
1788 if (value->IsArray()) {
1789 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1790 uint32_t length = array->Length();
1791 for (uint32_t ii = 0; ii < length; ++ii)
1792 list->append(engine->toQObject(array->Get(ii)));
1794 list->append(engine->toQObject(value));
1797 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1798 new (&data) v8::Handle<v8::Value>(value);
1801 new (&data) QVariant();
1804 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
1805 QVariant v = engine->toVariant(value, -1);
1807 if (v.userType() == callType) {
1808 *((QVariant *)&data) = v;
1809 } else if (v.canConvert((QVariant::Type)callType)) {
1810 *((QVariant *)&data) = v;
1811 ((QVariant *)&data)->convert((QVariant::Type)callType);
1812 } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) {
1813 QObject *obj = ep->toQObject(v);
1816 const QMetaObject *objMo = obj->metaObject();
1817 while (objMo && objMo != mo) objMo = objMo->superClass();
1818 if (!objMo) obj = 0;
1821 *((QVariant *)&data) = QVariant(callType, &obj);
1823 *((QVariant *)&data) = QVariant(callType, (void *)0);
1828 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1830 if (type == qMetaTypeId<QScriptValue>()) {
1831 return v8::Undefined();
1832 } else if (type == QMetaType::Int) {
1833 return v8::Integer::New(*((int *)&data));
1834 } else if (type == QMetaType::UInt) {
1835 return v8::Integer::NewFromUnsigned(*((uint *)&data));
1836 } else if (type == QMetaType::Bool) {
1837 return v8::Boolean::New(*((bool *)&data));
1838 } else if (type == QMetaType::Double) {
1839 return v8::Number::New(*((double *)&data));
1840 } else if (type == QMetaType::Float) {
1841 return v8::Number::New(*((float *)&data));
1842 } else if (type == QMetaType::QString) {
1843 return engine->toString(*((QString *)&data));
1844 } else if (type == QMetaType::QObjectStar) {
1845 QObject *object = *((QObject **)&data);
1847 QDeclarativeData::get(object, true)->setImplicitDestructible();
1848 return engine->newQObject(object);
1849 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1850 // XXX Can this be made more by using Array as a prototype and implementing
1851 // directly against QList<QObject*>?
1852 QList<QObject *> &list = *(QList<QObject *>*)&data;
1853 v8::Local<v8::Array> array = v8::Array::New(list.count());
1854 for (int ii = 0; ii < list.count(); ++ii)
1855 array->Set(ii, engine->newQObject(list.at(ii)));
1857 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
1858 return *(v8::Handle<v8::Value>*)&data;
1859 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1860 QVariant value = *((QVariant *)&data);
1861 v8::Handle<v8::Value> rv = engine->fromVariant(value);
1862 if (QObject *object = engine->toQObject(rv))
1863 QDeclarativeData::get(object, true)->setImplicitDestructible();
1866 return v8::Undefined();