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;
690 if (property->isQObject())
691 fastgetter = QObjectValueGetter;
692 else if (property->propType == QMetaType::Int || property->isEnum())
693 fastgetter = IntValueGetter;
694 else if (property->propType == QMetaType::Bool)
695 fastgetter = BoolValueGetter;
696 else if (property->propType == QMetaType::QString)
697 fastgetter = QStringValueGetter;
698 else if (property->propType == QMetaType::UInt)
699 fastgetter = UIntValueGetter;
700 else if (property->propType == QMetaType::Float)
701 fastgetter = FloatValueGetter;
702 else if (property->propType == QMetaType::Double)
703 fastgetter = DoubleValueGetter;
706 int notifyIndex = property->notifyIndex;
707 if (property->isConstant()) notifyIndex = 0;
708 else if (notifyIndex == -1) notifyIndex = 0x7FFF;
709 uint32_t data = (property->notifyIndex & 0x7FFF) << 16 | property->coreIndex;
711 QString name = iter.key();
712 if (name == toString || name == destroy)
716 ft = v8::FunctionTemplate::New();
717 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
718 QV8QObjectWrapper::Setter,
719 QV8QObjectWrapper::Query,
721 QV8QObjectWrapper::Enumerator);
722 ft->InstanceTemplate()->SetHasExternalResource(true);
725 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, FastValueSetter,
726 v8::Integer::NewFromUnsigned(data));
731 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
733 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
734 QV8QObjectWrapper::Setter,
735 QV8QObjectWrapper::Query,
737 QV8QObjectWrapper::Enumerator);
738 ft->InstanceTemplate()->SetHasExternalResource(true);
739 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
743 v8::Local<v8::Object> result = constructor->NewInstance();
744 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
745 result->SetExternalResource(r);
749 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
751 v8::Local<v8::Object> rv;
753 if (!ddata->propertyCache && engine->engine()) {
754 ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
755 if (ddata->propertyCache) ddata->propertyCache->addref();
758 if (ddata->propertyCache) {
759 rv = ddata->propertyCache->newQObject(object, engine);
761 // XXX NewInstance() should be optimized
762 rv = m_constructor->NewInstance();
763 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
764 rv->SetExternalResource(r);
771 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
772 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
774 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
775 persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that
776 "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this
778 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create
779 an entry in the m_taintedObject hash where we store the handle and mark the object as
780 "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
781 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
782 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has
785 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
790 if (QObjectPrivate::get(object)->wasDeleted)
791 return v8::Undefined();
793 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
796 return v8::Undefined();
798 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
799 // We own the v8object
800 return v8::Local<v8::Object>::New(ddata->v8object);
801 } else if (ddata->v8object.IsEmpty() &&
802 (ddata->v8objectid == m_id || // We own the QObject
803 ddata->v8objectid == 0 || // No one owns the QObject
804 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
806 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
807 ddata->v8object = qPersistentNew<v8::Object>(rv);
808 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
809 ddata->v8objectid = m_id;
813 // If this object is tainted, we have to check to see if it is in our
814 // tainted object list
815 TaintedHash::Iterator iter =
816 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
817 bool found = iter != m_taintedObjects.end();
819 // If our tainted handle doesn't exist or has been collected, and there isn't
820 // a handle in the ddata, we can assume ownership of the ddata->v8object
821 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
822 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
823 ddata->v8object = qPersistentNew<v8::Object>(rv);
824 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
825 ddata->v8objectid = m_id;
829 m_taintedObjects.erase(iter);
834 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
835 iter = m_taintedObjects.insert(object, instance);
836 ddata->hasTaintedV8Object = true;
839 if ((*iter)->v8object.IsEmpty()) {
840 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
841 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
842 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
845 return v8::Local<v8::Object>::New((*iter)->v8object);
849 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
851 v8::ScriptOrigin origin = function->GetScriptOrigin();
852 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
854 // This is one of our special QObject method wrappers
855 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
856 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
858 if (data->IsArray()) {
859 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
860 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
863 // In theory this can't fall through, but I suppose V8 might run out of memory or something
866 return qMakePair((QObject *)0, -1);
869 struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
871 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
872 ~QV8QObjectConnectionList();
876 : needsDestroy(false) {}
877 Connection(const Connection &other)
878 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
879 Connection &operator=(const Connection &other) {
880 thisObject = other.thisObject;
881 function = other.function;
882 needsDestroy = other.needsDestroy;
886 v8::Persistent<v8::Object> thisObject;
887 v8::Persistent<v8::Function> function;
890 qPersistentDispose(thisObject);
891 qPersistentDispose(function);
897 struct ConnectionList : public QList<Connection> {
898 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
899 int connectionsInUse;
900 bool connectionsNeedClean;
905 typedef QHash<int, ConnectionList> SlotHash;
910 virtual void objectDestroyed(QObject *);
911 virtual int qt_metacall(QMetaObject::Call, int, void **);
914 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
915 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
919 QV8QObjectConnectionList::~QV8QObjectConnectionList()
921 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
922 QList<Connection> &connections = *iter;
923 for (int ii = 0; ii < connections.count(); ++ii) {
924 qPersistentDispose(connections[ii].thisObject);
925 qPersistentDispose(connections[ii].function);
931 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
933 engine->qobjectWrapper()->m_connections.remove(object);
941 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
943 if (method == QMetaObject::InvokeMetaMethod) {
944 SlotHash::Iterator iter = slotHash.find(index);
945 if (iter == slotHash.end())
947 ConnectionList &connectionList = *iter;
948 if (connectionList.isEmpty())
953 connectionList.connectionsInUse++;
955 QList<Connection> connections = connectionList;
957 QMetaMethod method = data()->metaObject()->method(index);
958 Q_ASSERT(method.methodType() == QMetaMethod::Signal);
959 // XXX TODO: We should figure out a way to cache the parameter types to avoid resolving
961 QList<QByteArray> params = method.parameterTypes();
963 v8::HandleScope handle_scope;
964 v8::Context::Scope scope(engine->context());
966 QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
967 int argCount = params.count();
969 for (int ii = 0; ii < argCount; ++ii) {
970 int type = QMetaType::type(params.at(ii).constData());
971 if (type == qMetaTypeId<QVariant>()) {
972 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
974 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
978 for (int ii = 0; ii < connections.count(); ++ii) {
979 Connection &connection = connections[ii];
980 if (connection.needsDestroy)
982 if (connection.thisObject.IsEmpty()) {
983 connection.function->Call(engine->global(), argCount, args.data());
985 connection.function->Call(connection.thisObject, argCount, args.data());
989 connectionList.connectionsInUse--;
990 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
991 for (QList<Connection>::Iterator iter = connectionList.begin();
992 iter != connectionList.end(); ) {
993 if (iter->needsDestroy) {
995 iter = connectionList.erase(iter);
1003 if (inUse == 0 && needsDestroy)
1010 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1012 if (args.Length() == 0)
1013 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1015 QV8Engine *engine = V8ENGINE();
1017 if (!args.This()->IsFunction())
1018 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1020 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1021 QObject *signalObject = signalInfo.first;
1022 int signalIndex = signalInfo.second;
1024 if (signalIndex == -1)
1025 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1028 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1030 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1031 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1033 v8::Local<v8::Value> functionValue;
1034 v8::Local<v8::Value> functionThisValue;
1036 if (args.Length() == 1) {
1037 functionValue = args[0];
1039 functionThisValue = args[0];
1040 functionValue = args[1];
1043 if (!functionValue->IsFunction())
1044 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1046 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1047 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1049 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1050 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1051 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1052 if (iter == connections.end())
1053 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1055 QV8QObjectConnectionList *connectionList = *iter;
1056 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1057 if (slotIter == connectionList->slotHash.end()) {
1058 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1059 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1062 QV8QObjectConnectionList::Connection connection;
1063 if (!functionThisValue.IsEmpty())
1064 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1065 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1067 slotIter->append(connection);
1069 return v8::Undefined();
1072 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1074 if (args.Length() == 0)
1075 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1077 QV8Engine *engine = V8ENGINE();
1079 if (!args.This()->IsFunction())
1080 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1082 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1083 QObject *signalObject = signalInfo.first;
1084 int signalIndex = signalInfo.second;
1086 if (signalIndex == -1)
1087 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1090 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1092 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1093 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1095 v8::Local<v8::Value> functionValue;
1096 v8::Local<v8::Value> functionThisValue;
1098 if (args.Length() == 1) {
1099 functionValue = args[0];
1101 functionThisValue = args[0];
1102 functionValue = args[1];
1105 if (!functionValue->IsFunction())
1106 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1108 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1109 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1111 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1112 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1113 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1114 if (iter == connectionsList.end())
1115 return v8::Undefined(); // Nothing to disconnect from
1117 QV8QObjectConnectionList *connectionList = *iter;
1118 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1119 if (slotIter == connectionList->slotHash.end())
1120 return v8::Undefined(); // Nothing to disconnect from
1122 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1124 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1125 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1127 if (functionData.second != -1) {
1128 // This is a QObject function wrapper
1129 for (int ii = 0; ii < connections.count(); ++ii) {
1130 QV8QObjectConnectionList::Connection &connection = connections[ii];
1132 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1133 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1135 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1136 if (connectedFunctionData == functionData) {
1138 if (connections.connectionsInUse) {
1139 connection.needsDestroy = true;
1141 connection.dispose();
1142 connections.removeAt(ii);
1144 return v8::Undefined();
1150 // This is a normal JS function
1151 for (int ii = 0; ii < connections.count(); ++ii) {
1152 QV8QObjectConnectionList::Connection &connection = connections[ii];
1153 if (connection.function->StrictEquals(function) &&
1154 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1155 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1157 if (connections.connectionsInUse) {
1158 connection.needsDestroy = true;
1160 connection.dispose();
1161 connections.removeAt(ii);
1163 return v8::Undefined();
1168 return v8::Undefined();
1172 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1174 Only searches for real properties of \a object (including methods), not attached properties etc.
1176 v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, v8::Handle<v8::String> property,
1177 QV8QObjectWrapper::RevisionMode revisionMode)
1179 return GetProperty(m_engine, object, 0, property, revisionMode);
1183 Set the \a property of \a object to \a value.
1185 Returns true if the property was "set" - even if this results in an exception being thrown -
1186 and false if the object has no such property.
1188 Only searches for real properties of \a object (including methods), not attached properties etc.
1190 bool QV8QObjectWrapper::setProperty(QObject *object, v8::Handle<v8::String> property,
1191 v8::Handle<v8::Value> value, RevisionMode revisionMode)
1193 return SetProperty(m_engine, object, property, value, revisionMode);
1199 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1200 int Length() const { return _length; }
1201 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1205 v8::Handle<v8::Object> *_args;
1209 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1210 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1214 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1215 args[0].initAsType(returnType);
1217 for (int ii = 0; ii < argCount; ++ii)
1218 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1220 QVarLengthArray<void *, 9> argData(args.count());
1221 for (int ii = 0; ii < args.count(); ++ii)
1222 argData[ii] = args[ii].dataPtr();
1224 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1226 return args[0].toValue(engine);
1228 } else if (returnType != 0) {
1230 MetaCallArgument arg;
1231 arg.initAsType(returnType);
1233 void *args[] = { arg.dataPtr() };
1235 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1237 return arg.toValue(engine);
1241 void *args[] = { 0 };
1242 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1243 return v8::Undefined();
1248 static int EnumType(const QMetaObject *meta, const QString &strname)
1250 QByteArray str = strname.toUtf8();
1253 int scopeIdx = str.lastIndexOf("::");
1254 if (scopeIdx != -1) {
1255 scope = str.left(scopeIdx);
1256 name = str.mid(scopeIdx + 2);
1260 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1261 QMetaEnum m = meta->enumerator(i);
1262 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1263 return QVariant::Int;
1265 return QVariant::Invalid;
1269 Returns the match score for converting \a actual to be of type \a conversionType. A
1270 zero score means "perfect match" whereas a higher score is worse.
1272 The conversion table is copied out of the QtScript callQtMethod() function.
1274 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
1275 const QByteArray &conversionTypeName)
1277 if (actual->IsNumber()) {
1278 switch (conversionType) {
1279 case QMetaType::Double:
1281 case QMetaType::Float:
1283 case QMetaType::LongLong:
1284 case QMetaType::ULongLong:
1286 case QMetaType::Long:
1287 case QMetaType::ULong:
1289 case QMetaType::Int:
1290 case QMetaType::UInt:
1292 case QMetaType::Short:
1293 case QMetaType::UShort:
1296 case QMetaType::Char:
1297 case QMetaType::UChar:
1302 } else if (actual->IsString()) {
1303 switch (conversionType) {
1304 case QMetaType::QString:
1309 } else if (actual->IsBoolean()) {
1310 switch (conversionType) {
1311 case QMetaType::Bool:
1316 } else if (actual->IsDate()) {
1317 switch (conversionType) {
1318 case QMetaType::QDateTime:
1320 case QMetaType::QDate:
1322 case QMetaType::QTime:
1327 } else if (actual->IsRegExp()) {
1328 switch (conversionType) {
1329 case QMetaType::QRegExp:
1334 } else if (actual->IsArray()) {
1335 switch (conversionType) {
1336 case QMetaType::QStringList:
1337 case QMetaType::QVariantList:
1342 } else if (actual->IsNull()) {
1343 switch (conversionType) {
1344 case QMetaType::VoidStar:
1345 case QMetaType::QObjectStar:
1348 if (!conversionTypeName.endsWith('*'))
1353 } else if (actual->IsObject()) {
1354 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1356 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1357 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1358 switch (conversionType) {
1359 case QMetaType::QObjectStar:
1364 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1365 if (conversionType == qMetaTypeId<QVariant>())
1367 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1380 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1386 int classInfoCount, classInfoData;
1387 int methodCount, methodData;
1390 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1393 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1395 QByteArray sig = m.signature();
1396 int paren = sig.indexOf('(');
1400 return sig.left(paren);
1404 Returns the next related method, if one, or 0.
1406 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object,
1407 const QDeclarativePropertyCache::Data *current,
1408 QDeclarativePropertyCache::Data &dummy)
1410 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1411 if (current->relatedIndex == -1)
1415 return cache->method(current->relatedIndex);
1417 const QMetaObject *mo = object->metaObject();
1418 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1420 while (methodOffset > current->relatedIndex) {
1421 mo = mo->superClass();
1422 methodOffset -= QMetaObject_methods(mo);
1425 QMetaMethod method = mo->method(current->relatedIndex);
1428 // Look for overloaded methods
1429 QByteArray methodName = QMetaMethod_name(method);
1430 for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1431 if (methodName == QMetaMethod_name(mo->method(ii))) {
1432 dummy.relatedIndex = ii;
1441 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
1442 QV8Engine *engine, CallArgs &callArgs)
1444 if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
1446 QMetaMethod m = object->metaObject()->method(data.coreIndex);
1447 QList<QByteArray> argTypeNames = m.parameterTypes();
1448 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1451 for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1452 argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1453 if (argTypes[ii] == QVariant::Invalid)
1454 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1455 if (argTypes[ii] == QVariant::Invalid) {
1456 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1457 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1458 return v8::Handle<v8::Value>();
1462 if (argTypes.count() > callArgs.Length()) {
1463 QString error = QLatin1String("Insufficient arguments");
1464 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1465 return v8::Handle<v8::Value>();
1468 return CallMethod(object, data.coreIndex, data.propType, argTypes.count(),
1469 argTypes.data(), engine, callArgs);
1473 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1479 Resolve the overloaded method to call. The algorithm works conceptually like this:
1480 1. Resolve the set of overloads it is *possible* to call.
1481 Impossible overloads include those that have too many parameters or have parameters
1483 2. Filter the set of overloads to only contain those with the closest number of
1485 For example, if we are called with 3 parameters and there are 2 overloads that
1486 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1487 3. Find the best remaining overload based on its match score.
1488 If two or more overloads have the same match score, call the last one. The match
1489 score is constructed by adding the matchScore() result for each of the parameters.
1491 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data,
1492 QV8Engine *engine, CallArgs &callArgs)
1494 int argumentCount = callArgs.Length();
1496 const QDeclarativePropertyCache::Data *best = 0;
1497 int bestParameterScore = INT_MAX;
1498 int bestMatchScore = INT_MAX;
1500 QDeclarativePropertyCache::Data dummy;
1501 const QDeclarativePropertyCache::Data *attempt = &data;
1504 QList<QByteArray> methodArgTypeNames;
1506 if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1507 methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1509 int methodArgumentCount = methodArgTypeNames.count();
1511 if (methodArgumentCount > argumentCount)
1512 continue; // We don't have sufficient arguments to call this method
1514 int methodParameterScore = argumentCount - methodArgumentCount;
1515 if (methodParameterScore > bestParameterScore)
1516 continue; // We already have a better option
1518 int methodMatchScore = 0;
1519 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1521 bool unknownArgument = false;
1522 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1523 methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1524 if (methodArgTypes[ii] == QVariant::Invalid)
1525 methodArgTypes[ii] = EnumType(object->metaObject(),
1526 QString::fromLatin1(methodArgTypeNames.at(ii)));
1527 if (methodArgTypes[ii] == QVariant::Invalid) {
1528 unknownArgument = true;
1531 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1533 if (unknownArgument)
1534 continue; // We don't understand all the parameters
1536 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1538 bestParameterScore = methodParameterScore;
1539 bestMatchScore = methodMatchScore;
1542 if (bestParameterScore == 0 && bestMatchScore == 0)
1543 break; // We can't get better than that
1545 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1548 return CallPrecise(object, *best, engine, callArgs);
1550 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1551 const QDeclarativePropertyCache::Data *candidate = &data;
1553 error += QLatin1String("\n ") +
1554 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1555 candidate = RelatedMethod(object, candidate, dummy);
1558 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1559 return v8::Handle<v8::Value>();
1563 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1567 QString objectName = object->objectName();
1569 result += QString::fromUtf8(object->metaObject()->className());
1570 result += QLatin1String("(0x");
1571 result += QString::number((quintptr)object,16);
1573 if (!objectName.isEmpty()) {
1574 result += QLatin1String(", \"");
1575 result += objectName;
1576 result += QLatin1Char('\"');
1579 result += QLatin1Char(')');
1581 result = QLatin1String("null");
1584 return engine->toString(result);
1587 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1589 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1590 if (!ddata || ddata->indestructible) {
1591 const char *error = "Invalid attempt to destroy() an indestructible object";
1592 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1593 return v8::Undefined();
1598 delay = args->Get(0)->Uint32Value();
1601 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1603 object->deleteLater();
1605 return v8::Undefined();
1608 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1610 // object, index, qmlglobal, argCount, args
1611 Q_ASSERT(args.Length() == 5);
1612 Q_ASSERT(args[0]->IsObject());
1614 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1617 return v8::Undefined();
1619 int argCount = args[3]->Int32Value();
1620 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1622 // Special hack to return info about this closure.
1623 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1624 v8::Local<v8::Array> data = v8::Array::New(2);
1625 data->Set(0, args[0]);
1626 data->Set(1, args[1]);
1630 QObject *object = resource->object;
1631 int index = args[1]->Int32Value();
1634 return v8::Undefined();
1637 // Builtin functions
1638 if (index == QOBJECT_TOSTRING_INDEX) {
1639 return ToString(resource->engine, object, argCount, arguments);
1640 } else if (index == QOBJECT_DESTROY_INDEX) {
1641 return Destroy(resource->engine, object, argCount, arguments);
1643 return v8::Undefined();
1647 QDeclarativePropertyCache::Data method;
1649 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1650 if (ddata->propertyCache) {
1651 QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1653 return v8::Undefined();
1658 if (method.coreIndex == -1) {
1659 QMetaMethod mm = object->metaObject()->method(index);
1660 method.load(object->metaObject()->method(index));
1662 if (method.coreIndex == -1)
1663 return v8::Undefined();
1666 if (method.flags & QDeclarativePropertyCache::Data::IsV8Function) {
1667 v8::Handle<v8::Value> rv;
1668 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1670 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1671 resource->engine->contextWrapper()->context(qmlglobal),
1673 QDeclarativeV8Function *funcptr = &func;
1675 void *args[] = { 0, &funcptr };
1676 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1678 if (rv.IsEmpty()) return v8::Undefined();
1682 CallArgs callArgs(argCount, &arguments);
1683 if (method.relatedIndex == -1) {
1684 return CallPrecise(object, method, resource->engine, callArgs);
1686 return CallOverloaded(object, method, resource->engine, callArgs);
1690 MetaCallArgument::MetaCallArgument()
1691 : type(QVariant::Invalid), isObjectType(false)
1695 MetaCallArgument::~MetaCallArgument()
1700 void MetaCallArgument::cleanup()
1702 if (type == QMetaType::QString) {
1703 ((QString *)&data)->~QString();
1704 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1705 ((QVariant *)&data)->~QVariant();
1706 } else if (type == qMetaTypeId<QScriptValue>()) {
1707 ((QScriptValue *)&data)->~QScriptValue();
1708 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1709 ((QList<QObject *> *)&data)->~QList<QObject *>();
1713 void *MetaCallArgument::dataPtr()
1716 return ((QVariant *)data)->data();
1718 return (void *)&data;
1721 void MetaCallArgument::initAsType(int callType)
1723 if (type != 0) { cleanup(); type = 0; }
1724 if (callType == 0) return;
1726 if (callType == qMetaTypeId<QScriptValue>()) {
1727 new (&data) QScriptValue();
1729 } else if (callType == QMetaType::Int ||
1730 callType == QMetaType::UInt ||
1731 callType == QMetaType::Bool ||
1732 callType == QMetaType::Double ||
1733 callType == QMetaType::Float) {
1735 } else if (callType == QMetaType::QObjectStar) {
1736 *((QObject **)&data) = 0;
1738 } else if (callType == QMetaType::QString) {
1739 new (&data) QString();
1741 } else if (callType == qMetaTypeId<QVariant>()) {
1743 new (&data) QVariant();
1744 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1746 new (&data) QList<QObject *>();
1747 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1749 new (&data) v8::Handle<v8::Value>();
1752 new (&data) QVariant(callType, (void *)0);
1756 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1758 if (type != 0) { cleanup(); type = 0; }
1760 if (callType == qMetaTypeId<QScriptValue>()) {
1761 new (&data) QScriptValue();
1762 type = qMetaTypeId<QScriptValue>();
1763 } else if (callType == QMetaType::Int) {
1764 *((int *)&data) = int(value->Int32Value());
1766 } else if (callType == QMetaType::UInt) {
1767 *((uint *)&data) = uint(value->Uint32Value());
1769 } else if (callType == QMetaType::Bool) {
1770 *((bool *)&data) = value->BooleanValue();
1772 } else if (callType == QMetaType::Double) {
1773 *((double *)&data) = double(value->NumberValue());
1775 } else if (callType == QMetaType::Float) {
1776 *((float *)&data) = float(value->NumberValue());
1778 } else if (callType == QMetaType::QString) {
1779 if (value->IsNull() || value->IsUndefined())
1780 new (&data) QString();
1782 new (&data) QString(engine->toString(value->ToString()));
1784 } else if (callType == QMetaType::QObjectStar) {
1785 *((QObject **)&data) = engine->toQObject(value);
1787 } else if (callType == qMetaTypeId<QVariant>()) {
1788 new (&data) QVariant(engine->toVariant(value, -1));
1790 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1791 QList<QObject *> *list = new (&data) QList<QObject *>();
1792 if (value->IsArray()) {
1793 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1794 uint32_t length = array->Length();
1795 for (uint32_t ii = 0; ii < length; ++ii)
1796 list->append(engine->toQObject(array->Get(ii)));
1798 list->append(engine->toQObject(value));
1801 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1802 new (&data) v8::Handle<v8::Value>(value);
1805 new (&data) QVariant();
1808 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
1809 QVariant v = engine->toVariant(value, -1);
1811 if (v.userType() == callType) {
1812 *((QVariant *)&data) = v;
1813 } else if (v.canConvert((QVariant::Type)callType)) {
1814 *((QVariant *)&data) = v;
1815 ((QVariant *)&data)->convert((QVariant::Type)callType);
1816 } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) {
1817 QObject *obj = ep->toQObject(v);
1820 const QMetaObject *objMo = obj->metaObject();
1821 while (objMo && objMo != mo) objMo = objMo->superClass();
1822 if (!objMo) obj = 0;
1825 *((QVariant *)&data) = QVariant(callType, &obj);
1827 *((QVariant *)&data) = QVariant(callType, (void *)0);
1832 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1834 if (type == qMetaTypeId<QScriptValue>()) {
1835 return v8::Undefined();
1836 } else if (type == QMetaType::Int) {
1837 return v8::Integer::New(*((int *)&data));
1838 } else if (type == QMetaType::UInt) {
1839 return v8::Integer::NewFromUnsigned(*((uint *)&data));
1840 } else if (type == QMetaType::Bool) {
1841 return v8::Boolean::New(*((bool *)&data));
1842 } else if (type == QMetaType::Double) {
1843 return v8::Number::New(*((double *)&data));
1844 } else if (type == QMetaType::Float) {
1845 return v8::Number::New(*((float *)&data));
1846 } else if (type == QMetaType::QString) {
1847 return engine->toString(*((QString *)&data));
1848 } else if (type == QMetaType::QObjectStar) {
1849 QObject *object = *((QObject **)&data);
1851 QDeclarativeData::get(object, true)->setImplicitDestructible();
1852 return engine->newQObject(object);
1853 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1854 // XXX Can this be made more by using Array as a prototype and implementing
1855 // directly against QList<QObject*>?
1856 QList<QObject *> &list = *(QList<QObject *>*)&data;
1857 v8::Local<v8::Array> array = v8::Array::New(list.count());
1858 for (int ii = 0; ii < list.count(); ++ii)
1859 array->Set(ii, engine->newQObject(list.at(ii)));
1861 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
1862 return *(v8::Handle<v8::Value>*)&data;
1863 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1864 QVariant value = *((QVariant *)&data);
1865 v8::Handle<v8::Value> rv = engine->fromVariant(value);
1866 if (QObject *object = engine->toQObject(rv))
1867 QDeclarativeData::get(object, true)->setImplicitDestructible();
1870 return v8::Undefined();