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());
210 m_toStringString = QHashedV8String(m_toStringSymbol);
211 m_destroyString = QHashedV8String(m_destroySymbol);
214 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
215 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
216 ft->InstanceTemplate()->SetHasExternalResource(true);
217 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
220 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
221 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin);
222 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
223 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
224 v8::Handle<v8::Value> args[] = { invokeFn };
225 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
226 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
230 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
231 prototype->Set(v8::String::New("connect"), V8FUNCTION(Connect, engine));
232 prototype->Set(v8::String::New("disconnect"), V8FUNCTION(Disconnect, engine));
236 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
238 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
241 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
243 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
244 return r?r->object:0;
247 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
248 QObject *QV8QObjectWrapper::QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
250 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
251 return static_cast<QV8QObjectResource *>(r)->object;
254 // Load value properties
255 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
256 const QDeclarativePropertyCache::Data &property)
258 Q_ASSERT(!property.isFunction());
260 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
261 if (property.propType == QMetaType:: metatype) { \
262 cpptype type = cpptype(); \
263 void *args[] = { &type, 0 }; \
264 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \
265 return constructor(type); \
268 if (property.isQObject()) {
270 void *args[] = { &rv, 0 };
271 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
272 return engine->newQObject(rv);
273 } else if (property.isQList()) {
274 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
275 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
276 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
277 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
278 else PROPERTY_LOAD(QString, QString, engine->toString)
279 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
280 else PROPERTY_LOAD(Float, float, v8::Number::New)
281 else PROPERTY_LOAD(Double, double, v8::Number::New)
282 else if(property.isV8Handle()) {
283 QDeclarativeV8Handle handle;
284 void *args[] = { &handle, 0 };
285 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
286 return reinterpret_cast<v8::Handle<v8::Value> &>(handle);
287 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)) {
288 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
289 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
291 return engine->newValueType(object, property.coreIndex, valueType);
294 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
295 return engine->fromVariant(var);
298 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
299 v8::Handle<v8::Value> *objectHandle,
300 const QHashedV8String &property,
301 QV8QObjectWrapper::RevisionMode revisionMode)
303 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
304 // will be a faster way of creating QObject method objects.
305 struct MethodClosure {
306 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
307 v8::Handle<v8::Value> *objectHandle,
309 v8::Handle<v8::Value> argv[] = {
310 objectHandle?*objectHandle:engine->newQObject(object),
311 v8::Integer::New(index)
313 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
315 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
316 v8::Handle<v8::Value> *objectHandle,
318 v8::Handle<v8::Value> argv[] = {
319 objectHandle?*objectHandle:engine->newQObject(object),
320 v8::Integer::New(index),
321 v8::Context::GetCallingQmlGlobal()
323 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
327 if (engine->qobjectWrapper()->m_toStringString == property) {
328 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
329 } else if (engine->qobjectWrapper()->m_destroyString == property) {
330 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
333 QDeclarativePropertyCache::Data local;
334 QDeclarativePropertyCache::Data *result = 0;
336 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
337 if (ddata && ddata->propertyCache)
338 result = ddata->propertyCache->property(property);
340 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
344 return v8::Handle<v8::Value>();
346 QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
348 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
349 QDeclarativeData *ddata = QDeclarativeData::get(object);
350 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
351 return v8::Handle<v8::Value>();
354 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
356 if (result->isFunction()) {
357 if (result->flags & QDeclarativePropertyCache::Data::IsVMEFunction) {
358 return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
359 } else if (result->flags & QDeclarativePropertyCache::Data::IsV8Function) {
360 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
362 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
366 if (ep && ep->captureProperties && !result->isConstant()) {
367 if (result->coreIndex == 0)
368 ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
370 ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
373 return LoadProperty(engine, object, *result);
376 // Setter for writable properties. Shared between the interceptor and fast property accessor
377 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property,
378 v8::Handle<v8::Value> value)
380 QDeclarativeBinding *newBinding = 0;
382 if (value->IsFunction()) {
383 QDeclarativeContextData *context = engine->callingContext();
384 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
386 v8::Local<v8::StackTrace> trace =
387 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
388 v8::StackTrace::kScriptName));
389 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
390 int lineNumber = frame->GetLineNumber();
391 QString url = engine->toString(frame->GetScriptName());
393 QDeclarativePropertyCache::ValueTypeData valueTypeData;
394 newBinding = new QDeclarativeBinding(&function, object, context);
395 newBinding->setSourceLocation(url, lineNumber);
396 newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context));
397 newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
400 QDeclarativeAbstractBinding *oldBinding =
401 QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
403 oldBinding->destroy();
405 #define PROPERTY_STORE(cpptype, value) \
409 void *argv[] = { &o, 0, &status, &flags }; \
410 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
413 if (value->IsNull() && property->isQObject()) {
414 PROPERTY_STORE(QObject*, 0);
415 } else if (value->IsUndefined() && property->isResettable()) {
417 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
418 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
419 PROPERTY_STORE(QVariant, QVariant());
420 } else if (value->IsUndefined()) {
421 QString error = QLatin1String("Cannot assign [undefined] to ") +
422 QLatin1String(QMetaType::typeName(property->propType));
423 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
424 } else if (value->IsFunction()) {
425 // this is handled by the binding creation above
426 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
427 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
428 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
429 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
430 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
431 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
432 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
433 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
434 } else if (property->propType == QMetaType::QString && value->IsString()) {
435 PROPERTY_STORE(QString, engine->toString(value->ToString()));
438 if (property->isQList())
439 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
441 v = engine->toVariant(value, property->propType);
443 QDeclarativeContextData *context = engine->callingContext();
445 if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
446 const char *valueType = 0;
447 if (v.userType() == QVariant::Invalid) valueType = "null";
448 else valueType = QMetaType::typeName(v.userType());
450 QString error = QLatin1String("Cannot assign ") +
451 QLatin1String(valueType) +
452 QLatin1String(" to ") +
453 QLatin1String(QMetaType::typeName(property->propType));
454 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
459 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
460 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
462 if (engine->qobjectWrapper()->m_toStringString == property ||
463 engine->qobjectWrapper()->m_destroyString == property)
466 QDeclarativePropertyCache::Data local;
467 QDeclarativePropertyCache::Data *result = 0;
468 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
473 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
474 QDeclarativeData *ddata = QDeclarativeData::get(object);
475 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
479 if (!result->isWritable() && !result->isQList()) {
480 QString error = QLatin1String("Cannot assign to read-only property \"") +
481 engine->toString(property.string()) + QLatin1Char('\"');
482 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
486 StoreProperty(engine, object, result, value);
491 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
492 const v8::AccessorInfo &info)
494 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
495 v8::Handle<v8::Value> This = info.This();
497 if (!resource || resource->object.isNull()) return v8::Undefined();
499 QObject *object = resource->object;
501 QHashedV8String propertystring(property);
503 QV8Engine *v8engine = resource->engine;
504 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
505 QV8QObjectWrapper::IgnoreRevision);
506 if (!result.IsEmpty())
509 if (QV8Engine::startsWithUpper(property)) {
510 // Check for attached properties
511 QDeclarativeContextData *context = v8engine->callingContext();
512 QDeclarativeTypeNameCache::Data *data =
513 context && (context->imports)?context->imports->data(propertystring):0;
517 return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums);
518 } else if (data->typeNamespace) {
519 return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
524 return v8::Undefined();
527 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
528 v8::Local<v8::Value> value,
529 const v8::AccessorInfo &info)
531 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
533 if (!resource || resource->object.isNull())
536 QObject *object = resource->object;
538 QHashedV8String propertystring(property);
540 QV8Engine *v8engine = resource->engine;
541 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
544 QString error = QLatin1String("Cannot assign to non-existent property \"") +
545 v8engine->toString(property) + QLatin1Char('\"');
546 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
553 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
554 const v8::AccessorInfo &info)
556 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
558 if (!resource || resource->object.isNull())
559 return v8::Handle<v8::Integer>();
561 QV8Engine *engine = resource->engine;
562 QObject *object = resource->object;
564 QHashedV8String propertystring(property);
566 QDeclarativePropertyCache::Data local;
567 QDeclarativePropertyCache::Data *result = 0;
568 result = QDeclarativePropertyCache::property(engine->engine(), object, propertystring, local);
571 return v8::Handle<v8::Integer>();
572 else if (!result->isWritable() && !result->isQList())
573 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
575 return v8::Integer::New(v8::DontDelete);
578 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
580 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
582 if (!resource || resource->object.isNull())
583 return v8::Array::New();
585 QObject *object = resource->object;
589 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine());
591 QDeclarativePropertyCache *cache = 0;
592 QDeclarativeData *ddata = QDeclarativeData::get(object);
594 cache = ddata->propertyCache;
597 cache = ep->cache(object);
599 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
601 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
602 const QMetaObject *mo = object->metaObject();
603 int pc = mo->propertyCount();
604 int po = mo->propertyOffset();
605 for (int i=po; i<pc; ++i)
606 result << QString::fromUtf8(mo->property(i).name());
609 result = cache->propertyNames();
612 v8::Local<v8::Array> rv = v8::Array::New(result.count());
614 for (int ii = 0; ii < result.count(); ++ii)
615 rv->Set(ii, resource->engine->toString(result.at(ii)));
620 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
621 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
622 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
623 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
624 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
625 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
626 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
628 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
629 const v8::AccessorInfo& info)
631 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
633 if (!resource || resource->object.isNull())
636 QObject *object = resource->object;
638 uint32_t data = info.Data()->Uint32Value();
639 int index = data & 0x7FFF; // So that we can use the same data for Setter and Getter
641 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
643 Q_ASSERT(ddata->propertyCache);
645 QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index);
648 Q_ASSERT(pdata->isWritable() || pdata->isQList());
650 StoreProperty(resource->engine, object, pdata, value);
653 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
654 const v8::AccessorInfo& info)
656 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
658 if (!resource || resource->object.isNull())
661 QV8Engine *v8engine = resource->engine;
663 QString error = QLatin1String("Cannot assign to read-only property \"") +
664 v8engine->toString(property) + QLatin1Char('\"');
665 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
668 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
670 Q_ASSERT(handle->IsObject());
672 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(handle->ToObject());
676 QObject *object = resource->object;
678 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
680 ddata->v8object.Clear();
681 if (!object->parent() && !ddata->indestructible)
682 object->deleteLater();
686 qPersistentDispose(handle);
689 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
691 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
692 instance->v8object.Clear();
693 qPersistentDispose(handle);
696 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
700 Q_ASSERT(QDeclarativeData::get(object, false));
701 Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
704 if (constructor.IsEmpty()) {
705 v8::Local<v8::FunctionTemplate> ft;
707 QString toString = QLatin1String("toString");
708 QString destroy = QLatin1String("destroy");
710 // XXX TODO: Enables fast property accessors. These more than double the property access
711 // performance, but the cost of setting up this structure hasn't been measured so
712 // its not guarenteed that this is a win overall. We need to try and measure the cost.
713 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
714 Data *property = *iter;
715 if (property->isFunction() ||
716 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x7FFF ||
717 property->coreIndex == 0)
720 v8::AccessorGetter fastgetter = 0;
721 v8::AccessorSetter fastsetter = FastValueSetter;
722 if (!property->isWritable())
723 fastsetter = FastValueSetterReadOnly;
725 if (property->isQObject())
726 fastgetter = QObjectValueGetter;
727 else if (property->propType == QMetaType::Int || property->isEnum())
728 fastgetter = IntValueGetter;
729 else if (property->propType == QMetaType::Bool)
730 fastgetter = BoolValueGetter;
731 else if (property->propType == QMetaType::QString)
732 fastgetter = QStringValueGetter;
733 else if (property->propType == QMetaType::UInt)
734 fastgetter = UIntValueGetter;
735 else if (property->propType == QMetaType::Float)
736 fastgetter = FloatValueGetter;
737 else if (property->propType == QMetaType::Double)
738 fastgetter = DoubleValueGetter;
741 int notifyIndex = property->notifyIndex;
742 if (property->isConstant()) notifyIndex = 0;
743 else if (notifyIndex == -1) notifyIndex = 0x7FFF;
744 uint32_t data = (property->notifyIndex & 0x7FFF) << 16 | property->coreIndex;
746 QString name = iter.key();
747 if (name == toString || name == destroy)
751 ft = v8::FunctionTemplate::New();
752 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
753 QV8QObjectWrapper::Setter,
754 QV8QObjectWrapper::Query,
756 QV8QObjectWrapper::Enumerator);
757 ft->InstanceTemplate()->SetHasExternalResource(true);
760 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
761 v8::Integer::NewFromUnsigned(data));
766 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
768 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
769 QV8QObjectWrapper::Setter,
770 QV8QObjectWrapper::Query,
772 QV8QObjectWrapper::Enumerator);
773 ft->InstanceTemplate()->SetHasExternalResource(true);
774 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
778 v8::Local<v8::Object> result = constructor->NewInstance();
779 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
780 result->SetExternalResource(r);
784 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
786 v8::Local<v8::Object> rv;
788 if (!ddata->propertyCache && engine->engine()) {
789 ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
790 if (ddata->propertyCache) ddata->propertyCache->addref();
793 if (ddata->propertyCache) {
794 rv = ddata->propertyCache->newQObject(object, engine);
796 // XXX NewInstance() should be optimized
797 rv = m_constructor->NewInstance();
798 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
799 rv->SetExternalResource(r);
806 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
807 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
809 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
810 persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that
811 "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this
813 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create
814 an entry in the m_taintedObject hash where we store the handle and mark the object as
815 "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
816 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
817 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has
820 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
825 if (QObjectPrivate::get(object)->wasDeleted)
826 return v8::Undefined();
828 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
831 return v8::Undefined();
833 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
834 // We own the v8object
835 return v8::Local<v8::Object>::New(ddata->v8object);
836 } else if (ddata->v8object.IsEmpty() &&
837 (ddata->v8objectid == m_id || // We own the QObject
838 ddata->v8objectid == 0 || // No one owns the QObject
839 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
841 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
842 ddata->v8object = qPersistentNew<v8::Object>(rv);
843 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
844 ddata->v8objectid = m_id;
848 // If this object is tainted, we have to check to see if it is in our
849 // tainted object list
850 TaintedHash::Iterator iter =
851 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
852 bool found = iter != m_taintedObjects.end();
854 // If our tainted handle doesn't exist or has been collected, and there isn't
855 // a handle in the ddata, we can assume ownership of the ddata->v8object
856 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
857 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
858 ddata->v8object = qPersistentNew<v8::Object>(rv);
859 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
860 ddata->v8objectid = m_id;
864 m_taintedObjects.erase(iter);
869 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
870 iter = m_taintedObjects.insert(object, instance);
871 ddata->hasTaintedV8Object = true;
874 if ((*iter)->v8object.IsEmpty()) {
875 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
876 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
877 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
880 return v8::Local<v8::Object>::New((*iter)->v8object);
884 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
886 v8::ScriptOrigin origin = function->GetScriptOrigin();
887 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
889 // This is one of our special QObject method wrappers
890 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
891 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
893 if (data->IsArray()) {
894 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
895 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
898 // In theory this can't fall through, but I suppose V8 might run out of memory or something
901 return qMakePair((QObject *)0, -1);
904 struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
906 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
907 ~QV8QObjectConnectionList();
911 : needsDestroy(false) {}
912 Connection(const Connection &other)
913 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
914 Connection &operator=(const Connection &other) {
915 thisObject = other.thisObject;
916 function = other.function;
917 needsDestroy = other.needsDestroy;
921 v8::Persistent<v8::Object> thisObject;
922 v8::Persistent<v8::Function> function;
925 qPersistentDispose(thisObject);
926 qPersistentDispose(function);
932 struct ConnectionList : public QList<Connection> {
933 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
934 int connectionsInUse;
935 bool connectionsNeedClean;
940 typedef QHash<int, ConnectionList> SlotHash;
945 virtual void objectDestroyed(QObject *);
946 virtual int qt_metacall(QMetaObject::Call, int, void **);
949 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
950 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
954 QV8QObjectConnectionList::~QV8QObjectConnectionList()
956 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
957 QList<Connection> &connections = *iter;
958 for (int ii = 0; ii < connections.count(); ++ii) {
959 qPersistentDispose(connections[ii].thisObject);
960 qPersistentDispose(connections[ii].function);
966 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
968 engine->qobjectWrapper()->m_connections.remove(object);
976 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
978 if (method == QMetaObject::InvokeMetaMethod) {
979 SlotHash::Iterator iter = slotHash.find(index);
980 if (iter == slotHash.end())
982 ConnectionList &connectionList = *iter;
983 if (connectionList.isEmpty())
988 connectionList.connectionsInUse++;
990 QList<Connection> connections = connectionList;
992 QMetaMethod method = data()->metaObject()->method(index);
993 Q_ASSERT(method.methodType() == QMetaMethod::Signal);
994 // XXX TODO: We should figure out a way to cache the parameter types to avoid resolving
996 QList<QByteArray> params = method.parameterTypes();
998 v8::HandleScope handle_scope;
999 v8::Context::Scope scope(engine->context());
1001 QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
1002 int argCount = params.count();
1004 for (int ii = 0; ii < argCount; ++ii) {
1005 int type = QMetaType::type(params.at(ii).constData());
1006 if (type == qMetaTypeId<QVariant>()) {
1007 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1009 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1013 for (int ii = 0; ii < connections.count(); ++ii) {
1014 Connection &connection = connections[ii];
1015 if (connection.needsDestroy)
1017 if (connection.thisObject.IsEmpty()) {
1018 connection.function->Call(engine->global(), argCount, args.data());
1020 connection.function->Call(connection.thisObject, argCount, args.data());
1024 connectionList.connectionsInUse--;
1025 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1026 for (QList<Connection>::Iterator iter = connectionList.begin();
1027 iter != connectionList.end(); ) {
1028 if (iter->needsDestroy) {
1030 iter = connectionList.erase(iter);
1038 if (inUse == 0 && needsDestroy)
1045 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1047 if (args.Length() == 0)
1048 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1050 QV8Engine *engine = V8ENGINE();
1052 if (!args.This()->IsFunction())
1053 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1055 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1056 QObject *signalObject = signalInfo.first;
1057 int signalIndex = signalInfo.second;
1059 if (signalIndex == -1)
1060 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1063 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1065 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1066 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1068 v8::Local<v8::Value> functionValue;
1069 v8::Local<v8::Value> functionThisValue;
1071 if (args.Length() == 1) {
1072 functionValue = args[0];
1074 functionThisValue = args[0];
1075 functionValue = args[1];
1078 if (!functionValue->IsFunction())
1079 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1081 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1082 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1084 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1085 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1086 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1087 if (iter == connections.end())
1088 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1090 QV8QObjectConnectionList *connectionList = *iter;
1091 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1092 if (slotIter == connectionList->slotHash.end()) {
1093 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1094 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1097 QV8QObjectConnectionList::Connection connection;
1098 if (!functionThisValue.IsEmpty())
1099 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1100 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1102 slotIter->append(connection);
1104 return v8::Undefined();
1107 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1109 if (args.Length() == 0)
1110 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1112 QV8Engine *engine = V8ENGINE();
1114 if (!args.This()->IsFunction())
1115 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1117 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1118 QObject *signalObject = signalInfo.first;
1119 int signalIndex = signalInfo.second;
1121 if (signalIndex == -1)
1122 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1125 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1127 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1128 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1130 v8::Local<v8::Value> functionValue;
1131 v8::Local<v8::Value> functionThisValue;
1133 if (args.Length() == 1) {
1134 functionValue = args[0];
1136 functionThisValue = args[0];
1137 functionValue = args[1];
1140 if (!functionValue->IsFunction())
1141 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1143 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1144 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1146 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1147 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1148 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1149 if (iter == connectionsList.end())
1150 return v8::Undefined(); // Nothing to disconnect from
1152 QV8QObjectConnectionList *connectionList = *iter;
1153 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1154 if (slotIter == connectionList->slotHash.end())
1155 return v8::Undefined(); // Nothing to disconnect from
1157 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1159 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1160 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1162 if (functionData.second != -1) {
1163 // This is a QObject function wrapper
1164 for (int ii = 0; ii < connections.count(); ++ii) {
1165 QV8QObjectConnectionList::Connection &connection = connections[ii];
1167 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1168 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1170 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1171 if (connectedFunctionData == functionData) {
1173 if (connections.connectionsInUse) {
1174 connection.needsDestroy = true;
1176 connection.dispose();
1177 connections.removeAt(ii);
1179 return v8::Undefined();
1185 // This is a normal JS function
1186 for (int ii = 0; ii < connections.count(); ++ii) {
1187 QV8QObjectConnectionList::Connection &connection = connections[ii];
1188 if (connection.function->StrictEquals(function) &&
1189 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1190 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1192 if (connections.connectionsInUse) {
1193 connection.needsDestroy = true;
1195 connection.dispose();
1196 connections.removeAt(ii);
1198 return v8::Undefined();
1203 return v8::Undefined();
1207 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1209 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1211 Only searches for real properties of \a object (including methods), not attached properties etc.
1215 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1217 Set the \a property of \a object to \a value.
1219 Returns true if the property was "set" - even if this results in an exception being thrown -
1220 and false if the object has no such property.
1222 Only searches for real properties of \a object (including methods), not attached properties etc.
1228 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1229 int Length() const { return _length; }
1230 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1234 v8::Handle<v8::Object> *_args;
1238 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1239 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1243 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1244 args[0].initAsType(returnType);
1246 for (int ii = 0; ii < argCount; ++ii)
1247 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1249 QVarLengthArray<void *, 9> argData(args.count());
1250 for (int ii = 0; ii < args.count(); ++ii)
1251 argData[ii] = args[ii].dataPtr();
1253 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1255 return args[0].toValue(engine);
1257 } else if (returnType != 0) {
1259 MetaCallArgument arg;
1260 arg.initAsType(returnType);
1262 void *args[] = { arg.dataPtr() };
1264 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1266 return arg.toValue(engine);
1270 void *args[] = { 0 };
1271 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1272 return v8::Undefined();
1277 static int EnumType(const QMetaObject *meta, const QString &strname)
1279 QByteArray str = strname.toUtf8();
1282 int scopeIdx = str.lastIndexOf("::");
1283 if (scopeIdx != -1) {
1284 scope = str.left(scopeIdx);
1285 name = str.mid(scopeIdx + 2);
1289 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1290 QMetaEnum m = meta->enumerator(i);
1291 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1292 return QVariant::Int;
1294 return QVariant::Invalid;
1298 Returns the match score for converting \a actual to be of type \a conversionType. A
1299 zero score means "perfect match" whereas a higher score is worse.
1301 The conversion table is copied out of the QtScript callQtMethod() function.
1303 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
1304 const QByteArray &conversionTypeName)
1306 if (actual->IsNumber()) {
1307 switch (conversionType) {
1308 case QMetaType::Double:
1310 case QMetaType::Float:
1312 case QMetaType::LongLong:
1313 case QMetaType::ULongLong:
1315 case QMetaType::Long:
1316 case QMetaType::ULong:
1318 case QMetaType::Int:
1319 case QMetaType::UInt:
1321 case QMetaType::Short:
1322 case QMetaType::UShort:
1325 case QMetaType::Char:
1326 case QMetaType::UChar:
1331 } else if (actual->IsString()) {
1332 switch (conversionType) {
1333 case QMetaType::QString:
1338 } else if (actual->IsBoolean()) {
1339 switch (conversionType) {
1340 case QMetaType::Bool:
1345 } else if (actual->IsDate()) {
1346 switch (conversionType) {
1347 case QMetaType::QDateTime:
1349 case QMetaType::QDate:
1351 case QMetaType::QTime:
1356 } else if (actual->IsRegExp()) {
1357 switch (conversionType) {
1358 case QMetaType::QRegExp:
1363 } else if (actual->IsArray()) {
1364 switch (conversionType) {
1365 case QMetaType::QStringList:
1366 case QMetaType::QVariantList:
1371 } else if (actual->IsNull()) {
1372 switch (conversionType) {
1373 case QMetaType::VoidStar:
1374 case QMetaType::QObjectStar:
1377 if (!conversionTypeName.endsWith('*'))
1382 } else if (actual->IsObject()) {
1383 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1385 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1386 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1387 switch (conversionType) {
1388 case QMetaType::QObjectStar:
1393 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1394 if (conversionType == qMetaTypeId<QVariant>())
1396 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1409 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1415 int classInfoCount, classInfoData;
1416 int methodCount, methodData;
1419 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1422 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1424 QByteArray sig = m.signature();
1425 int paren = sig.indexOf('(');
1429 return sig.left(paren);
1433 Returns the next related method, if one, or 0.
1435 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object,
1436 const QDeclarativePropertyCache::Data *current,
1437 QDeclarativePropertyCache::Data &dummy)
1439 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1440 if (current->relatedIndex == -1)
1444 return cache->method(current->relatedIndex);
1446 const QMetaObject *mo = object->metaObject();
1447 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1449 while (methodOffset > current->relatedIndex) {
1450 mo = mo->superClass();
1451 methodOffset -= QMetaObject_methods(mo);
1454 QMetaMethod method = mo->method(current->relatedIndex);
1457 // Look for overloaded methods
1458 QByteArray methodName = QMetaMethod_name(method);
1459 for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1460 if (methodName == QMetaMethod_name(mo->method(ii))) {
1461 dummy.relatedIndex = ii;
1470 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
1471 QV8Engine *engine, CallArgs &callArgs)
1473 if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
1475 QMetaMethod m = object->metaObject()->method(data.coreIndex);
1476 QList<QByteArray> argTypeNames = m.parameterTypes();
1477 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1480 for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1481 argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1482 if (argTypes[ii] == QVariant::Invalid)
1483 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1484 if (argTypes[ii] == QVariant::Invalid) {
1485 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1486 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1487 return v8::Handle<v8::Value>();
1491 if (argTypes.count() > callArgs.Length()) {
1492 QString error = QLatin1String("Insufficient arguments");
1493 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1494 return v8::Handle<v8::Value>();
1497 return CallMethod(object, data.coreIndex, data.propType, argTypes.count(),
1498 argTypes.data(), engine, callArgs);
1502 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1508 Resolve the overloaded method to call. The algorithm works conceptually like this:
1509 1. Resolve the set of overloads it is *possible* to call.
1510 Impossible overloads include those that have too many parameters or have parameters
1512 2. Filter the set of overloads to only contain those with the closest number of
1514 For example, if we are called with 3 parameters and there are 2 overloads that
1515 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1516 3. Find the best remaining overload based on its match score.
1517 If two or more overloads have the same match score, call the last one. The match
1518 score is constructed by adding the matchScore() result for each of the parameters.
1520 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data,
1521 QV8Engine *engine, CallArgs &callArgs)
1523 int argumentCount = callArgs.Length();
1525 const QDeclarativePropertyCache::Data *best = 0;
1526 int bestParameterScore = INT_MAX;
1527 int bestMatchScore = INT_MAX;
1529 QDeclarativePropertyCache::Data dummy;
1530 const QDeclarativePropertyCache::Data *attempt = &data;
1533 QList<QByteArray> methodArgTypeNames;
1535 if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1536 methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1538 int methodArgumentCount = methodArgTypeNames.count();
1540 if (methodArgumentCount > argumentCount)
1541 continue; // We don't have sufficient arguments to call this method
1543 int methodParameterScore = argumentCount - methodArgumentCount;
1544 if (methodParameterScore > bestParameterScore)
1545 continue; // We already have a better option
1547 int methodMatchScore = 0;
1548 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1550 bool unknownArgument = false;
1551 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1552 methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1553 if (methodArgTypes[ii] == QVariant::Invalid)
1554 methodArgTypes[ii] = EnumType(object->metaObject(),
1555 QString::fromLatin1(methodArgTypeNames.at(ii)));
1556 if (methodArgTypes[ii] == QVariant::Invalid) {
1557 unknownArgument = true;
1560 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1562 if (unknownArgument)
1563 continue; // We don't understand all the parameters
1565 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1567 bestParameterScore = methodParameterScore;
1568 bestMatchScore = methodMatchScore;
1571 if (bestParameterScore == 0 && bestMatchScore == 0)
1572 break; // We can't get better than that
1574 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1577 return CallPrecise(object, *best, engine, callArgs);
1579 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1580 const QDeclarativePropertyCache::Data *candidate = &data;
1582 error += QLatin1String("\n ") +
1583 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1584 candidate = RelatedMethod(object, candidate, dummy);
1587 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1588 return v8::Handle<v8::Value>();
1592 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1596 QString objectName = object->objectName();
1598 result += QString::fromUtf8(object->metaObject()->className());
1599 result += QLatin1String("(0x");
1600 result += QString::number((quintptr)object,16);
1602 if (!objectName.isEmpty()) {
1603 result += QLatin1String(", \"");
1604 result += objectName;
1605 result += QLatin1Char('\"');
1608 result += QLatin1Char(')');
1610 result = QLatin1String("null");
1613 return engine->toString(result);
1616 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1618 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1619 if (!ddata || ddata->indestructible) {
1620 const char *error = "Invalid attempt to destroy() an indestructible object";
1621 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1622 return v8::Undefined();
1627 delay = args->Get(0)->Uint32Value();
1630 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1632 object->deleteLater();
1634 return v8::Undefined();
1637 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1639 // object, index, qmlglobal, argCount, args
1640 Q_ASSERT(args.Length() == 5);
1641 Q_ASSERT(args[0]->IsObject());
1643 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1646 return v8::Undefined();
1648 int argCount = args[3]->Int32Value();
1649 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1651 // Special hack to return info about this closure.
1652 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1653 v8::Local<v8::Array> data = v8::Array::New(2);
1654 data->Set(0, args[0]);
1655 data->Set(1, args[1]);
1659 QObject *object = resource->object;
1660 int index = args[1]->Int32Value();
1663 return v8::Undefined();
1666 // Builtin functions
1667 if (index == QOBJECT_TOSTRING_INDEX) {
1668 return ToString(resource->engine, object, argCount, arguments);
1669 } else if (index == QOBJECT_DESTROY_INDEX) {
1670 return Destroy(resource->engine, object, argCount, arguments);
1672 return v8::Undefined();
1676 QDeclarativePropertyCache::Data method;
1678 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1679 if (ddata->propertyCache) {
1680 QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1682 return v8::Undefined();
1687 if (method.coreIndex == -1) {
1688 QMetaMethod mm = object->metaObject()->method(index);
1689 method.load(object->metaObject()->method(index));
1691 if (method.coreIndex == -1)
1692 return v8::Undefined();
1695 if (method.flags & QDeclarativePropertyCache::Data::IsV8Function) {
1696 v8::Handle<v8::Value> rv;
1697 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1699 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1700 resource->engine->contextWrapper()->context(qmlglobal),
1702 QDeclarativeV8Function *funcptr = &func;
1704 void *args[] = { 0, &funcptr };
1705 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1707 if (rv.IsEmpty()) return v8::Undefined();
1711 CallArgs callArgs(argCount, &arguments);
1712 if (method.relatedIndex == -1) {
1713 return CallPrecise(object, method, resource->engine, callArgs);
1715 return CallOverloaded(object, method, resource->engine, callArgs);
1719 MetaCallArgument::MetaCallArgument()
1720 : type(QVariant::Invalid), isObjectType(false)
1724 MetaCallArgument::~MetaCallArgument()
1729 void MetaCallArgument::cleanup()
1731 if (type == QMetaType::QString) {
1732 ((QString *)&data)->~QString();
1733 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1734 ((QVariant *)&data)->~QVariant();
1735 } else if (type == qMetaTypeId<QScriptValue>()) {
1736 ((QScriptValue *)&data)->~QScriptValue();
1737 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1738 ((QList<QObject *> *)&data)->~QList<QObject *>();
1742 void *MetaCallArgument::dataPtr()
1745 return ((QVariant *)data)->data();
1747 return (void *)&data;
1750 void MetaCallArgument::initAsType(int callType)
1752 if (type != 0) { cleanup(); type = 0; }
1753 if (callType == 0) return;
1755 if (callType == qMetaTypeId<QScriptValue>()) {
1756 new (&data) QScriptValue();
1758 } else if (callType == QMetaType::Int ||
1759 callType == QMetaType::UInt ||
1760 callType == QMetaType::Bool ||
1761 callType == QMetaType::Double ||
1762 callType == QMetaType::Float) {
1764 } else if (callType == QMetaType::QObjectStar) {
1765 *((QObject **)&data) = 0;
1767 } else if (callType == QMetaType::QString) {
1768 new (&data) QString();
1770 } else if (callType == qMetaTypeId<QVariant>()) {
1772 new (&data) QVariant();
1773 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1775 new (&data) QList<QObject *>();
1776 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1778 new (&data) v8::Handle<v8::Value>();
1781 new (&data) QVariant(callType, (void *)0);
1785 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1787 if (type != 0) { cleanup(); type = 0; }
1789 if (callType == qMetaTypeId<QScriptValue>()) {
1790 new (&data) QScriptValue();
1791 type = qMetaTypeId<QScriptValue>();
1792 } else if (callType == QMetaType::Int) {
1793 *((int *)&data) = int(value->Int32Value());
1795 } else if (callType == QMetaType::UInt) {
1796 *((uint *)&data) = uint(value->Uint32Value());
1798 } else if (callType == QMetaType::Bool) {
1799 *((bool *)&data) = value->BooleanValue();
1801 } else if (callType == QMetaType::Double) {
1802 *((double *)&data) = double(value->NumberValue());
1804 } else if (callType == QMetaType::Float) {
1805 *((float *)&data) = float(value->NumberValue());
1807 } else if (callType == QMetaType::QString) {
1808 if (value->IsNull() || value->IsUndefined())
1809 new (&data) QString();
1811 new (&data) QString(engine->toString(value->ToString()));
1813 } else if (callType == QMetaType::QObjectStar) {
1814 *((QObject **)&data) = engine->toQObject(value);
1816 } else if (callType == qMetaTypeId<QVariant>()) {
1817 new (&data) QVariant(engine->toVariant(value, -1));
1819 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1820 QList<QObject *> *list = new (&data) QList<QObject *>();
1821 if (value->IsArray()) {
1822 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1823 uint32_t length = array->Length();
1824 for (uint32_t ii = 0; ii < length; ++ii)
1825 list->append(engine->toQObject(array->Get(ii)));
1827 list->append(engine->toQObject(value));
1830 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1831 new (&data) v8::Handle<v8::Value>(value);
1834 new (&data) QVariant();
1837 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
1838 QVariant v = engine->toVariant(value, -1);
1840 if (v.userType() == callType) {
1841 *((QVariant *)&data) = v;
1842 } else if (v.canConvert((QVariant::Type)callType)) {
1843 *((QVariant *)&data) = v;
1844 ((QVariant *)&data)->convert((QVariant::Type)callType);
1845 } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) {
1846 QObject *obj = ep->toQObject(v);
1849 const QMetaObject *objMo = obj->metaObject();
1850 while (objMo && objMo != mo) objMo = objMo->superClass();
1851 if (!objMo) obj = 0;
1854 *((QVariant *)&data) = QVariant(callType, &obj);
1856 *((QVariant *)&data) = QVariant(callType, (void *)0);
1861 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1863 if (type == qMetaTypeId<QScriptValue>()) {
1864 return v8::Undefined();
1865 } else if (type == QMetaType::Int) {
1866 return v8::Integer::New(*((int *)&data));
1867 } else if (type == QMetaType::UInt) {
1868 return v8::Integer::NewFromUnsigned(*((uint *)&data));
1869 } else if (type == QMetaType::Bool) {
1870 return v8::Boolean::New(*((bool *)&data));
1871 } else if (type == QMetaType::Double) {
1872 return v8::Number::New(*((double *)&data));
1873 } else if (type == QMetaType::Float) {
1874 return v8::Number::New(*((float *)&data));
1875 } else if (type == QMetaType::QString) {
1876 return engine->toString(*((QString *)&data));
1877 } else if (type == QMetaType::QObjectStar) {
1878 QObject *object = *((QObject **)&data);
1880 QDeclarativeData::get(object, true)->setImplicitDestructible();
1881 return engine->newQObject(object);
1882 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1883 // XXX Can this be made more by using Array as a prototype and implementing
1884 // directly against QList<QObject*>?
1885 QList<QObject *> &list = *(QList<QObject *>*)&data;
1886 v8::Local<v8::Array> array = v8::Array::New(list.count());
1887 for (int ii = 0; ii < list.count(); ++ii)
1888 array->Set(ii, engine->newQObject(list.at(ii)));
1890 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
1891 return *(v8::Handle<v8::Value>*)&data;
1892 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1893 QVariant value = *((QVariant *)&data);
1894 v8::Handle<v8::Value> rv = engine->fromVariant(value);
1895 if (QObject *object = engine->toQObject(rv))
1896 QDeclarativeData::get(object, true)->setImplicitDestructible();
1899 return v8::Undefined();