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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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>
51 #include <private/qjsvalue_p.h>
52 #include <private/qscript_impl_p.h>
54 #include <QtDeclarative/qjsvalue.h>
55 #include <QtCore/qvarlengtharray.h>
56 #include <QtCore/qtimer.h>
57 #include <QtCore/qatomic.h>
61 Q_DECLARE_METATYPE(QJSValue);
62 Q_DECLARE_METATYPE(QDeclarativeV8Handle);
65 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
66 // The code in this file does not violate strict aliasing, but GCC thinks it does
67 // so turn off the warnings for us to have a clean build
68 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
72 #define QOBJECT_TOSTRING_INDEX -2
73 #define QOBJECT_DESTROY_INDEX -3
75 // XXX TODO: Need to review all calls to QDeclarativeEngine *engine() to confirm QObjects work
76 // correctly in a worker thread
78 class QV8QObjectResource : public QV8ObjectResource
80 V8_RESOURCE_TYPE(QObjectType);
83 QV8QObjectResource(QV8Engine *engine, QObject *object);
85 QDeclarativeGuard<QObject> object;
88 class QV8QObjectInstance : public QDeclarativeGuard<QObject>
91 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
92 : QDeclarativeGuard<QObject>(o), wrapper(w)
98 qPersistentDispose(v8object);
101 virtual void objectDestroyed(QObject *o)
104 wrapper->m_taintedObjects.remove(o);
108 v8::Persistent<v8::Object> v8object;
109 QV8QObjectWrapper *wrapper;
113 struct MetaCallArgument {
114 inline MetaCallArgument();
115 inline ~MetaCallArgument();
116 inline void *dataPtr();
118 inline void initAsType(int type);
119 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
120 inline v8::Handle<v8::Value> toValue(QV8Engine *);
123 MetaCallArgument(const MetaCallArgument &);
125 inline void cleanup();
134 char allocData[sizeof(QVariant)];
137 // Pointers to allocData
140 QVariant *qvariantPtr;
141 QList<QObject *> *qlistPtr;
142 QJSValue *qjsValuePtr;
143 QDeclarativeV8Handle *handlePtr;
150 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
151 : QV8ObjectResource(engine), object(object)
155 static QAtomicInt objectIdCounter(1);
157 QV8QObjectWrapper::QV8QObjectWrapper()
158 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
162 QV8QObjectWrapper::~QV8QObjectWrapper()
164 for (TaintedHash::Iterator iter = m_taintedObjects.begin();
165 iter != m_taintedObjects.end();
167 (*iter)->wrapper = 0;
169 m_taintedObjects.clear();
172 void QV8QObjectWrapper::destroy()
174 qDeleteAll(m_connections);
175 m_connections.clear();
177 qPersistentDispose(m_hiddenObject);
178 qPersistentDispose(m_destroySymbol);
179 qPersistentDispose(m_toStringSymbol);
180 qPersistentDispose(m_methodConstructor);
181 qPersistentDispose(m_constructor);
184 #define FAST_VALUE_GETTER(name, cpptype, defaultvalue, constructor) \
185 static v8::Handle<v8::Value> name ## ValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) \
187 v8::Handle<v8::Object> This = info.This(); \
188 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
190 if (resource->object.isNull()) return v8::Undefined(); \
192 QObject *object = resource->object; \
194 uint32_t data = info.Data()->Uint32Value(); \
195 int index = data & 0x7FFF; \
196 int notify = (data & 0x0FFF0000) >> 16; \
197 if (notify == 0x0FFF) notify = -1; \
199 QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
200 if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
201 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
202 ep->capturedProperties << CapturedProperty(object, index, notify); \
205 cpptype value = defaultvalue; \
206 void *args[] = { &value, 0 }; \
207 QMetaObject::metacall(object, QMetaObject::ReadProperty, index, args); \
209 return constructor(value); \
211 static v8::Handle<v8::Value> name ## ValueGetterDirect(v8::Local<v8::String>, const v8::AccessorInfo &info) \
213 v8::Handle<v8::Object> This = info.This(); \
214 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
216 if (resource->object.isNull()) return v8::Undefined(); \
218 QObject *object = resource->object; \
220 uint32_t data = info.Data()->Uint32Value(); \
221 int index = data & 0x7FFF; \
222 int notify = (data & 0x0FFF0000) >> 16; \
223 if (notify == 0x0FFF) notify = -1; \
225 QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
226 if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
227 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
228 ep->capturedProperties << CapturedProperty(object, index, notify); \
231 cpptype value = defaultvalue; \
232 void *args[] = { &value, 0 }; \
233 object->qt_metacall(QMetaObject::ReadProperty, index, args); \
235 return constructor(value); \
238 #define CREATE_FUNCTION \
239 "(function(method) { "\
240 "return (function(object, data, qmlglobal) { "\
241 "return (function() { "\
242 "return method(object, data, qmlglobal, arguments.length, arguments); "\
248 static quint32 toStringHash = -1;
249 static quint32 destroyHash = -1;
251 void QV8QObjectWrapper::init(QV8Engine *engine)
255 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
256 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
257 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
259 m_toStringString = QHashedV8String(m_toStringSymbol);
260 m_destroyString = QHashedV8String(m_destroySymbol);
262 toStringHash = m_toStringString.hash();
263 destroyHash = m_destroyString.hash();
266 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
267 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
268 ft->InstanceTemplate()->SetHasExternalResource(true);
269 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
272 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
273 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin);
274 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
275 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
276 v8::Handle<v8::Value> args[] = { invokeFn };
277 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
278 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
282 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
283 prototype->Set(v8::String::New("connect"), V8FUNCTION(Connect, engine));
284 prototype->Set(v8::String::New("disconnect"), V8FUNCTION(Disconnect, engine));
288 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
290 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
293 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
295 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
296 return r?r->object:0;
299 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
300 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
302 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
303 return static_cast<QV8QObjectResource *>(r)->object;
306 // Load value properties
307 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
308 const QDeclarativePropertyCache::Data &property)
310 Q_ASSERT(!property.isFunction());
312 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
313 if (property.propType == QMetaType:: metatype) { \
314 cpptype type = cpptype(); \
315 void *args[] = { &type, 0 }; \
316 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \
317 return constructor(type); \
320 if (property.isQObject()) {
322 void *args[] = { &rv, 0 };
323 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
324 return engine->newQObject(rv);
325 } else if (property.isQList()) {
326 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
327 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
328 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
329 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
330 else PROPERTY_LOAD(QString, QString, engine->toString)
331 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
332 else PROPERTY_LOAD(Float, float, v8::Number::New)
333 else PROPERTY_LOAD(Double, double, v8::Number::New)
334 else if(property.isV8Handle()) {
335 QDeclarativeV8Handle handle;
336 void *args[] = { &handle, 0 };
337 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
338 return handle.toHandle();
339 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)
340 && engine->engine()) {
341 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
342 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
344 return engine->newValueType(object, property.coreIndex, valueType);
347 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
348 return engine->fromVariant(var);
353 static v8::Handle<v8::Value> LoadPropertyDirect(QV8Engine *engine, QObject *object,
354 const QDeclarativePropertyCache::Data &property)
356 Q_ASSERT(!property.isFunction());
358 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
359 if (property.propType == QMetaType:: metatype) { \
360 cpptype type = cpptype(); \
361 void *args[] = { &type, 0 }; \
362 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); \
363 return constructor(type); \
366 if (property.isQObject()) {
368 void *args[] = { &rv, 0 };
369 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
370 return engine->newQObject(rv);
371 } else if (property.isQList()) {
372 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
373 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
374 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
375 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
376 else PROPERTY_LOAD(QString, QString, engine->toString)
377 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
378 else PROPERTY_LOAD(Float, float, v8::Number::New)
379 else PROPERTY_LOAD(Double, double, v8::Number::New)
380 else if(property.isV8Handle()) {
381 QDeclarativeV8Handle handle;
382 void *args[] = { &handle, 0 };
383 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
384 return handle.toHandle();
385 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)
386 && engine->engine()) {
387 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
388 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
390 return engine->newValueType(object, property.coreIndex, valueType);
393 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
394 return engine->fromVariant(var);
399 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
400 v8::Handle<v8::Value> *objectHandle,
401 const QHashedV8String &property,
402 QV8QObjectWrapper::RevisionMode revisionMode)
404 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
405 // will be a faster way of creating QObject method objects.
406 struct MethodClosure {
407 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
408 v8::Handle<v8::Value> *objectHandle,
410 v8::Handle<v8::Value> argv[] = {
411 objectHandle?*objectHandle:engine->newQObject(object),
412 v8::Integer::New(index)
414 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
416 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
417 v8::Handle<v8::Value> *objectHandle,
419 v8::Handle<v8::Value> argv[] = {
420 objectHandle?*objectHandle:engine->newQObject(object),
421 v8::Integer::New(index),
422 v8::Context::GetCallingQmlGlobal()
424 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
429 // Comparing the hash first actually makes a measurable difference here, at least on x86
430 quint32 hash = property.hash();
431 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
432 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
433 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
434 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
438 QDeclarativePropertyCache::Data local;
439 QDeclarativePropertyCache::Data *result = 0;
441 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
442 if (ddata && ddata->propertyCache)
443 result = ddata->propertyCache->property(property);
445 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
449 return v8::Handle<v8::Value>();
451 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
452 QDeclarativeData *ddata = QDeclarativeData::get(object);
453 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
454 return v8::Handle<v8::Value>();
457 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
459 if (result->isFunction()) {
460 if (result->isVMEFunction()) {
461 return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
462 } else if (result->isV8Function()) {
463 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
465 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
469 QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
470 if (ep && ep->captureProperties && !result->isConstant()) {
471 if (result->coreIndex == 0)
472 ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
474 ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
477 if (result->isDirect()) {
478 return LoadPropertyDirect(engine, object, *result);
480 return LoadProperty(engine, object, *result);
484 // Setter for writable properties. Shared between the interceptor and fast property accessor
485 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property,
486 v8::Handle<v8::Value> value)
488 QDeclarativeBinding *newBinding = 0;
490 if (value->IsFunction()) {
491 QDeclarativeContextData *context = engine->callingContext();
492 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
494 v8::Local<v8::StackTrace> trace =
495 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
496 v8::StackTrace::kScriptName));
497 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
498 int lineNumber = frame->GetLineNumber();
499 QString url = engine->toString(frame->GetScriptName());
501 QDeclarativePropertyCache::ValueTypeData valueTypeData;
502 newBinding = new QDeclarativeBinding(&function, object, context);
503 newBinding->setSourceLocation(url, lineNumber);
504 newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context));
505 newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
508 QDeclarativeAbstractBinding *oldBinding =
509 QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
511 oldBinding->destroy();
513 #define PROPERTY_STORE(cpptype, value) \
517 void *argv[] = { &o, 0, &status, &flags }; \
518 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
521 if (value->IsNull() && property->isQObject()) {
522 PROPERTY_STORE(QObject*, 0);
523 } else if (value->IsUndefined() && property->isResettable()) {
525 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
526 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
527 PROPERTY_STORE(QVariant, QVariant());
528 } else if (value->IsUndefined()) {
529 QString error = QLatin1String("Cannot assign [undefined] to ") +
530 QLatin1String(QMetaType::typeName(property->propType));
531 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
532 } else if (value->IsFunction()) {
533 // this is handled by the binding creation above
534 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
535 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
536 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
537 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
538 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
539 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
540 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
541 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
542 } else if (property->propType == QMetaType::QString && value->IsString()) {
543 PROPERTY_STORE(QString, engine->toString(value->ToString()));
546 if (property->isQList())
547 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
549 v = engine->toVariant(value, property->propType);
551 QDeclarativeContextData *context = engine->callingContext();
553 if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
554 const char *valueType = 0;
555 if (v.userType() == QVariant::Invalid) valueType = "null";
556 else valueType = QMetaType::typeName(v.userType());
558 QString error = QLatin1String("Cannot assign ") +
559 QLatin1String(valueType) +
560 QLatin1String(" to ") +
561 QLatin1String(QMetaType::typeName(property->propType));
562 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
567 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
568 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
570 if (engine->qobjectWrapper()->m_toStringString == property ||
571 engine->qobjectWrapper()->m_destroyString == property)
574 QDeclarativePropertyCache::Data local;
575 QDeclarativePropertyCache::Data *result = 0;
576 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
581 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
582 QDeclarativeData *ddata = QDeclarativeData::get(object);
583 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
587 if (!result->isWritable() && !result->isQList()) {
588 QString error = QLatin1String("Cannot assign to read-only property \"") +
589 engine->toString(property.string()) + QLatin1Char('\"');
590 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
594 StoreProperty(engine, object, result, value);
599 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
600 const v8::AccessorInfo &info)
602 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
604 if (resource->object.isNull())
605 return v8::Handle<v8::Value>();
607 QObject *object = resource->object;
609 QHashedV8String propertystring(property);
611 QV8Engine *v8engine = resource->engine;
612 v8::Handle<v8::Value> This = info.This();
613 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
614 QV8QObjectWrapper::IgnoreRevision);
615 if (!result.IsEmpty())
618 if (QV8Engine::startsWithUpper(property)) {
619 // Check for attached properties
620 QDeclarativeContextData *context = v8engine->callingContext();
621 QDeclarativeTypeNameCache::Data *data =
622 context && (context->imports)?context->imports->data(propertystring):0;
626 return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums);
627 } else if (data->typeNamespace) {
628 return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
633 return v8::Handle<v8::Value>();
636 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
637 v8::Local<v8::Value> value,
638 const v8::AccessorInfo &info)
640 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
642 if (resource->object.isNull())
645 QObject *object = resource->object;
647 QHashedV8String propertystring(property);
649 QV8Engine *v8engine = resource->engine;
650 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
653 QString error = QLatin1String("Cannot assign to non-existent property \"") +
654 v8engine->toString(property) + QLatin1Char('\"');
655 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
662 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
663 const v8::AccessorInfo &info)
665 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
667 if (resource->object.isNull())
668 return v8::Handle<v8::Integer>();
670 QV8Engine *engine = resource->engine;
671 QObject *object = resource->object;
673 QHashedV8String propertystring(property);
675 QDeclarativePropertyCache::Data local;
676 QDeclarativePropertyCache::Data *result = 0;
677 result = QDeclarativePropertyCache::property(engine->engine(), object, propertystring, local);
680 return v8::Handle<v8::Integer>();
681 else if (!result->isWritable() && !result->isQList())
682 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
684 return v8::Integer::New(v8::DontDelete);
687 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
689 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
691 if (resource->object.isNull())
692 return v8::Array::New();
694 QObject *object = resource->object;
698 QDeclarativeEnginePrivate *ep = resource->engine->engine()
699 ? QDeclarativeEnginePrivate::get(resource->engine->engine())
702 QDeclarativePropertyCache *cache = 0;
703 QDeclarativeData *ddata = QDeclarativeData::get(object);
705 cache = ddata->propertyCache;
708 cache = ep ? ep->cache(object) : 0;
710 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
712 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
713 const QMetaObject *mo = object->metaObject();
714 int pc = mo->propertyCount();
715 int po = mo->propertyOffset();
716 for (int i=po; i<pc; ++i)
717 result << QString::fromUtf8(mo->property(i).name());
720 result = cache->propertyNames();
723 v8::Local<v8::Array> rv = v8::Array::New(result.count());
725 for (int ii = 0; ii < result.count(); ++ii)
726 rv->Set(ii, resource->engine->toString(result.at(ii)));
731 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
732 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
733 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
734 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
735 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
736 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
737 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
739 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
740 const v8::AccessorInfo& info)
742 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
744 if (resource->object.isNull())
747 QObject *object = resource->object;
749 uint32_t data = info.Data()->Uint32Value();
750 int index = data & 0x7FFF; // So that we can use the same data for Setter and Getter
752 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
754 Q_ASSERT(ddata->propertyCache);
756 QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index);
759 Q_ASSERT(pdata->isWritable() || pdata->isQList());
761 StoreProperty(resource->engine, object, pdata, value);
764 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
765 const v8::AccessorInfo& info)
767 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
769 if (resource->object.isNull())
772 QV8Engine *v8engine = resource->engine;
774 QString error = QLatin1String("Cannot assign to read-only property \"") +
775 v8engine->toString(property) + QLatin1Char('\"');
776 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
779 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
781 Q_ASSERT(handle->IsObject());
783 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
787 QObject *object = resource->object;
789 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
791 ddata->v8object.Clear();
792 if (!object->parent() && !ddata->indestructible)
793 object->deleteLater();
797 qPersistentDispose(handle);
800 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
802 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
803 instance->v8object.Clear();
804 qPersistentDispose(handle);
807 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
811 Q_ASSERT(QDeclarativeData::get(object, false));
812 Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
815 if (constructor.IsEmpty()) {
816 v8::Local<v8::FunctionTemplate> ft;
818 QString toString = QLatin1String("toString");
819 QString destroy = QLatin1String("destroy");
821 // XXX TODO: Enables fast property accessors. These more than double the property access
822 // performance, but the cost of setting up this structure hasn't been measured so
823 // its not guarenteed that this is a win overall. We need to try and measure the cost.
824 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
825 Data *property = *iter;
826 if (property->isFunction() ||
827 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x0FFF ||
828 property->coreIndex == 0)
831 v8::AccessorGetter fastgetter = 0;
832 v8::AccessorSetter fastsetter = FastValueSetter;
833 if (!property->isWritable())
834 fastsetter = FastValueSetterReadOnly;
836 if (property->isQObject())
837 fastgetter = property->isDirect()?QObjectValueGetterDirect:QObjectValueGetter;
838 else if (property->propType == QMetaType::Int || property->isEnum())
839 fastgetter = property->isDirect()?IntValueGetterDirect:IntValueGetter;
840 else if (property->propType == QMetaType::Bool)
841 fastgetter = property->isDirect()?BoolValueGetterDirect:BoolValueGetter;
842 else if (property->propType == QMetaType::QString)
843 fastgetter = property->isDirect()?QStringValueGetterDirect:QStringValueGetter;
844 else if (property->propType == QMetaType::UInt)
845 fastgetter = property->isDirect()?UIntValueGetterDirect:UIntValueGetter;
846 else if (property->propType == QMetaType::Float)
847 fastgetter = property->isDirect()?FloatValueGetterDirect:FloatValueGetter;
848 else if (property->propType == QMetaType::Double)
849 fastgetter = property->isDirect()?DoubleValueGetterDirect:DoubleValueGetter;
852 int notifyIndex = property->notifyIndex;
853 if (property->isConstant()) notifyIndex = 0;
854 else if (notifyIndex == -1) notifyIndex = 0x0FFF;
855 uint32_t data = (notifyIndex & 0x0FFF) << 16 | property->coreIndex;
857 QString name = iter.key();
858 if (name == toString || name == destroy)
862 ft = v8::FunctionTemplate::New();
863 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
864 QV8QObjectWrapper::Setter,
865 QV8QObjectWrapper::Query,
867 QV8QObjectWrapper::Enumerator);
868 ft->InstanceTemplate()->SetHasExternalResource(true);
871 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
872 v8::Integer::NewFromUnsigned(data));
877 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
879 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
880 QV8QObjectWrapper::Setter,
881 QV8QObjectWrapper::Query,
883 QV8QObjectWrapper::Enumerator);
884 ft->InstanceTemplate()->SetHasExternalResource(true);
885 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
889 v8::Local<v8::Object> result = constructor->NewInstance();
890 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
891 result->SetExternalResource(r);
895 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
897 v8::Local<v8::Object> rv;
899 if (!ddata->propertyCache && engine->engine()) {
900 ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
901 if (ddata->propertyCache) ddata->propertyCache->addref();
904 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
905 rv = ddata->propertyCache->newQObject(object, engine);
907 // XXX NewInstance() should be optimized
908 rv = m_constructor->NewInstance();
909 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
910 rv->SetExternalResource(r);
917 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
918 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
920 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
921 persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that
922 "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this
924 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create
925 an entry in the m_taintedObject hash where we store the handle and mark the object as
926 "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
927 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
928 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has
931 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
936 if (QObjectPrivate::get(object)->wasDeleted)
937 return v8::Undefined();
939 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
942 return v8::Undefined();
944 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
945 // We own the v8object
946 return v8::Local<v8::Object>::New(ddata->v8object);
947 } else if (ddata->v8object.IsEmpty() &&
948 (ddata->v8objectid == m_id || // We own the QObject
949 ddata->v8objectid == 0 || // No one owns the QObject
950 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
952 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
953 ddata->v8object = qPersistentNew<v8::Object>(rv);
954 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
955 ddata->v8objectid = m_id;
959 // If this object is tainted, we have to check to see if it is in our
960 // tainted object list
961 TaintedHash::Iterator iter =
962 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
963 bool found = iter != m_taintedObjects.end();
965 // If our tainted handle doesn't exist or has been collected, and there isn't
966 // a handle in the ddata, we can assume ownership of the ddata->v8object
967 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
968 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
969 ddata->v8object = qPersistentNew<v8::Object>(rv);
970 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
971 ddata->v8objectid = m_id;
975 m_taintedObjects.erase(iter);
980 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
981 iter = m_taintedObjects.insert(object, instance);
982 ddata->hasTaintedV8Object = true;
985 if ((*iter)->v8object.IsEmpty()) {
986 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
987 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
988 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
991 return v8::Local<v8::Object>::New((*iter)->v8object);
995 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
997 v8::ScriptOrigin origin = function->GetScriptOrigin();
998 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1000 // This is one of our special QObject method wrappers
1001 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1002 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1004 if (data->IsArray()) {
1005 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1006 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1009 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1012 return qMakePair((QObject *)0, -1);
1015 struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
1017 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1018 ~QV8QObjectConnectionList();
1022 : needsDestroy(false) {}
1023 Connection(const Connection &other)
1024 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1025 Connection &operator=(const Connection &other) {
1026 thisObject = other.thisObject;
1027 function = other.function;
1028 needsDestroy = other.needsDestroy;
1032 v8::Persistent<v8::Object> thisObject;
1033 v8::Persistent<v8::Function> function;
1036 qPersistentDispose(thisObject);
1037 qPersistentDispose(function);
1043 struct ConnectionList : public QList<Connection> {
1044 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1045 int connectionsInUse;
1046 bool connectionsNeedClean;
1051 typedef QHash<int, ConnectionList> SlotHash;
1056 virtual void objectDestroyed(QObject *);
1057 virtual int qt_metacall(QMetaObject::Call, int, void **);
1060 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1061 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1065 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1067 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1068 QList<Connection> &connections = *iter;
1069 for (int ii = 0; ii < connections.count(); ++ii) {
1070 qPersistentDispose(connections[ii].thisObject);
1071 qPersistentDispose(connections[ii].function);
1077 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1079 engine->qobjectWrapper()->m_connections.remove(object);
1082 needsDestroy = true;
1087 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1089 if (method == QMetaObject::InvokeMetaMethod) {
1090 SlotHash::Iterator iter = slotHash.find(index);
1091 if (iter == slotHash.end())
1093 ConnectionList &connectionList = *iter;
1094 if (connectionList.isEmpty())
1099 connectionList.connectionsInUse++;
1101 QList<Connection> connections = connectionList;
1103 QMetaMethod method = data()->metaObject()->method(index);
1104 Q_ASSERT(method.methodType() == QMetaMethod::Signal);
1105 // XXX TODO: We should figure out a way to cache the parameter types to avoid resolving
1107 QList<QByteArray> params = method.parameterTypes();
1109 v8::HandleScope handle_scope;
1110 v8::Context::Scope scope(engine->context());
1112 QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
1113 int argCount = params.count();
1115 for (int ii = 0; ii < argCount; ++ii) {
1116 int type = QMetaType::type(params.at(ii).constData());
1117 if (type == qMetaTypeId<QVariant>()) {
1118 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1120 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1124 for (int ii = 0; ii < connections.count(); ++ii) {
1125 Connection &connection = connections[ii];
1126 if (connection.needsDestroy)
1128 if (connection.thisObject.IsEmpty()) {
1129 connection.function->Call(engine->global(), argCount, args.data());
1131 connection.function->Call(connection.thisObject, argCount, args.data());
1135 connectionList.connectionsInUse--;
1136 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1137 for (QList<Connection>::Iterator iter = connectionList.begin();
1138 iter != connectionList.end(); ) {
1139 if (iter->needsDestroy) {
1141 iter = connectionList.erase(iter);
1149 if (inUse == 0 && needsDestroy)
1156 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1158 if (args.Length() == 0)
1159 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1161 QV8Engine *engine = V8ENGINE();
1163 if (!args.This()->IsFunction())
1164 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1166 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1167 QObject *signalObject = signalInfo.first;
1168 int signalIndex = signalInfo.second;
1170 if (signalIndex == -1)
1171 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1174 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1176 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1177 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1179 v8::Local<v8::Value> functionValue;
1180 v8::Local<v8::Value> functionThisValue;
1182 if (args.Length() == 1) {
1183 functionValue = args[0];
1185 functionThisValue = args[0];
1186 functionValue = args[1];
1189 if (!functionValue->IsFunction())
1190 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1192 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1193 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1195 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1196 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1197 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1198 if (iter == connections.end())
1199 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1201 QV8QObjectConnectionList *connectionList = *iter;
1202 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1203 if (slotIter == connectionList->slotHash.end()) {
1204 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1205 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1208 QV8QObjectConnectionList::Connection connection;
1209 if (!functionThisValue.IsEmpty())
1210 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1211 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1213 slotIter->append(connection);
1215 return v8::Undefined();
1218 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1220 if (args.Length() == 0)
1221 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1223 QV8Engine *engine = V8ENGINE();
1225 if (!args.This()->IsFunction())
1226 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1228 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1229 QObject *signalObject = signalInfo.first;
1230 int signalIndex = signalInfo.second;
1232 if (signalIndex == -1)
1233 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1236 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1238 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1239 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1241 v8::Local<v8::Value> functionValue;
1242 v8::Local<v8::Value> functionThisValue;
1244 if (args.Length() == 1) {
1245 functionValue = args[0];
1247 functionThisValue = args[0];
1248 functionValue = args[1];
1251 if (!functionValue->IsFunction())
1252 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1254 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1255 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1257 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1258 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1259 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1260 if (iter == connectionsList.end())
1261 return v8::Undefined(); // Nothing to disconnect from
1263 QV8QObjectConnectionList *connectionList = *iter;
1264 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1265 if (slotIter == connectionList->slotHash.end())
1266 return v8::Undefined(); // Nothing to disconnect from
1268 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1270 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1271 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1273 if (functionData.second != -1) {
1274 // This is a QObject function wrapper
1275 for (int ii = 0; ii < connections.count(); ++ii) {
1276 QV8QObjectConnectionList::Connection &connection = connections[ii];
1278 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1279 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1281 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1282 if (connectedFunctionData == functionData) {
1284 if (connections.connectionsInUse) {
1285 connection.needsDestroy = true;
1287 connection.dispose();
1288 connections.removeAt(ii);
1290 return v8::Undefined();
1296 // This is a normal JS function
1297 for (int ii = 0; ii < connections.count(); ++ii) {
1298 QV8QObjectConnectionList::Connection &connection = connections[ii];
1299 if (connection.function->StrictEquals(function) &&
1300 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1301 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1303 if (connections.connectionsInUse) {
1304 connection.needsDestroy = true;
1306 connection.dispose();
1307 connections.removeAt(ii);
1309 return v8::Undefined();
1314 return v8::Undefined();
1318 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1320 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1322 Only searches for real properties of \a object (including methods), not attached properties etc.
1326 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1328 Set the \a property of \a object to \a value.
1330 Returns true if the property was "set" - even if this results in an exception being thrown -
1331 and false if the object has no such property.
1333 Only searches for real properties of \a object (including methods), not attached properties etc.
1339 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1340 int Length() const { return _length; }
1341 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1345 v8::Handle<v8::Object> *_args;
1349 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1350 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1354 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1355 args[0].initAsType(returnType);
1357 for (int ii = 0; ii < argCount; ++ii)
1358 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1360 QVarLengthArray<void *, 9> argData(args.count());
1361 for (int ii = 0; ii < args.count(); ++ii)
1362 argData[ii] = args[ii].dataPtr();
1364 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1366 return args[0].toValue(engine);
1368 } else if (returnType != 0) {
1370 MetaCallArgument arg;
1371 arg.initAsType(returnType);
1373 void *args[] = { arg.dataPtr() };
1375 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1377 return arg.toValue(engine);
1381 void *args[] = { 0 };
1382 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1383 return v8::Undefined();
1388 static int EnumType(const QMetaObject *meta, const QString &strname)
1390 QByteArray str = strname.toUtf8();
1393 int scopeIdx = str.lastIndexOf("::");
1394 if (scopeIdx != -1) {
1395 scope = str.left(scopeIdx);
1396 name = str.mid(scopeIdx + 2);
1400 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1401 QMetaEnum m = meta->enumerator(i);
1402 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1403 return QVariant::Int;
1405 return QVariant::Invalid;
1409 Returns the match score for converting \a actual to be of type \a conversionType. A
1410 zero score means "perfect match" whereas a higher score is worse.
1412 The conversion table is copied out of the QtScript callQtMethod() function.
1414 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
1415 const QByteArray &conversionTypeName)
1417 if (actual->IsNumber()) {
1418 switch (conversionType) {
1419 case QMetaType::Double:
1421 case QMetaType::Float:
1423 case QMetaType::LongLong:
1424 case QMetaType::ULongLong:
1426 case QMetaType::Long:
1427 case QMetaType::ULong:
1429 case QMetaType::Int:
1430 case QMetaType::UInt:
1432 case QMetaType::Short:
1433 case QMetaType::UShort:
1436 case QMetaType::Char:
1437 case QMetaType::UChar:
1442 } else if (actual->IsString()) {
1443 switch (conversionType) {
1444 case QMetaType::QString:
1449 } else if (actual->IsBoolean()) {
1450 switch (conversionType) {
1451 case QMetaType::Bool:
1456 } else if (actual->IsDate()) {
1457 switch (conversionType) {
1458 case QMetaType::QDateTime:
1460 case QMetaType::QDate:
1462 case QMetaType::QTime:
1467 } else if (actual->IsRegExp()) {
1468 switch (conversionType) {
1469 case QMetaType::QRegExp:
1474 } else if (actual->IsArray()) {
1475 switch (conversionType) {
1476 case QMetaType::QStringList:
1477 case QMetaType::QVariantList:
1482 } else if (actual->IsNull()) {
1483 switch (conversionType) {
1484 case QMetaType::VoidStar:
1485 case QMetaType::QObjectStar:
1488 if (!conversionTypeName.endsWith('*'))
1493 } else if (actual->IsObject()) {
1494 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1496 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1497 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1498 switch (conversionType) {
1499 case QMetaType::QObjectStar:
1504 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1505 if (conversionType == qMetaTypeId<QVariant>())
1507 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1520 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1526 int classInfoCount, classInfoData;
1527 int methodCount, methodData;
1530 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1533 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1535 QByteArray sig = m.signature();
1536 int paren = sig.indexOf('(');
1540 return sig.left(paren);
1544 Returns the next related method, if one, or 0.
1546 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object,
1547 const QDeclarativePropertyCache::Data *current,
1548 QDeclarativePropertyCache::Data &dummy)
1550 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1551 if (current->relatedIndex == -1)
1555 return cache->method(current->relatedIndex);
1557 const QMetaObject *mo = object->metaObject();
1558 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1560 while (methodOffset > current->relatedIndex) {
1561 mo = mo->superClass();
1562 methodOffset -= QMetaObject_methods(mo);
1565 QMetaMethod method = mo->method(current->relatedIndex);
1568 // Look for overloaded methods
1569 QByteArray methodName = QMetaMethod_name(method);
1570 for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1571 if (methodName == QMetaMethod_name(mo->method(ii))) {
1572 dummy.relatedIndex = ii;
1581 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
1582 QV8Engine *engine, CallArgs &callArgs)
1584 if (data.hasArguments()) {
1586 QMetaMethod m = object->metaObject()->method(data.coreIndex);
1587 QList<QByteArray> argTypeNames = m.parameterTypes();
1588 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1591 for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1592 argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1593 if (argTypes[ii] == QVariant::Invalid)
1594 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1595 if (argTypes[ii] == QVariant::Invalid) {
1596 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1597 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1598 return v8::Handle<v8::Value>();
1602 if (argTypes.count() > callArgs.Length()) {
1603 QString error = QLatin1String("Insufficient arguments");
1604 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1605 return v8::Handle<v8::Value>();
1608 return CallMethod(object, data.coreIndex, data.propType, argTypes.count(),
1609 argTypes.data(), engine, callArgs);
1613 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1619 Resolve the overloaded method to call. The algorithm works conceptually like this:
1620 1. Resolve the set of overloads it is *possible* to call.
1621 Impossible overloads include those that have too many parameters or have parameters
1623 2. Filter the set of overloads to only contain those with the closest number of
1625 For example, if we are called with 3 parameters and there are 2 overloads that
1626 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1627 3. Find the best remaining overload based on its match score.
1628 If two or more overloads have the same match score, call the last one. The match
1629 score is constructed by adding the matchScore() result for each of the parameters.
1631 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data,
1632 QV8Engine *engine, CallArgs &callArgs)
1634 int argumentCount = callArgs.Length();
1636 const QDeclarativePropertyCache::Data *best = 0;
1637 int bestParameterScore = INT_MAX;
1638 int bestMatchScore = INT_MAX;
1640 QDeclarativePropertyCache::Data dummy;
1641 const QDeclarativePropertyCache::Data *attempt = &data;
1644 QList<QByteArray> methodArgTypeNames;
1646 if (attempt->hasArguments())
1647 methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1649 int methodArgumentCount = methodArgTypeNames.count();
1651 if (methodArgumentCount > argumentCount)
1652 continue; // We don't have sufficient arguments to call this method
1654 int methodParameterScore = argumentCount - methodArgumentCount;
1655 if (methodParameterScore > bestParameterScore)
1656 continue; // We already have a better option
1658 int methodMatchScore = 0;
1659 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1661 bool unknownArgument = false;
1662 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1663 methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1664 if (methodArgTypes[ii] == QVariant::Invalid)
1665 methodArgTypes[ii] = EnumType(object->metaObject(),
1666 QString::fromLatin1(methodArgTypeNames.at(ii)));
1667 if (methodArgTypes[ii] == QVariant::Invalid) {
1668 unknownArgument = true;
1671 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1673 if (unknownArgument)
1674 continue; // We don't understand all the parameters
1676 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1678 bestParameterScore = methodParameterScore;
1679 bestMatchScore = methodMatchScore;
1682 if (bestParameterScore == 0 && bestMatchScore == 0)
1683 break; // We can't get better than that
1685 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1688 return CallPrecise(object, *best, engine, callArgs);
1690 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1691 const QDeclarativePropertyCache::Data *candidate = &data;
1693 error += QLatin1String("\n ") +
1694 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1695 candidate = RelatedMethod(object, candidate, dummy);
1698 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1699 return v8::Handle<v8::Value>();
1703 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1707 QString objectName = object->objectName();
1709 result += QString::fromUtf8(object->metaObject()->className());
1710 result += QLatin1String("(0x");
1711 result += QString::number((quintptr)object,16);
1713 if (!objectName.isEmpty()) {
1714 result += QLatin1String(", \"");
1715 result += objectName;
1716 result += QLatin1Char('\"');
1719 result += QLatin1Char(')');
1721 result = QLatin1String("null");
1724 return engine->toString(result);
1727 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1729 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1730 if (!ddata || ddata->indestructible) {
1731 const char *error = "Invalid attempt to destroy() an indestructible object";
1732 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1733 return v8::Undefined();
1738 delay = args->Get(0)->Uint32Value();
1741 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1743 object->deleteLater();
1745 return v8::Undefined();
1748 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1750 // object, index, qmlglobal, argCount, args
1751 Q_ASSERT(args.Length() == 5);
1752 Q_ASSERT(args[0]->IsObject());
1754 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1757 return v8::Undefined();
1759 int argCount = args[3]->Int32Value();
1760 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1762 // Special hack to return info about this closure.
1763 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1764 v8::Local<v8::Array> data = v8::Array::New(2);
1765 data->Set(0, args[0]);
1766 data->Set(1, args[1]);
1770 QObject *object = resource->object;
1771 int index = args[1]->Int32Value();
1774 return v8::Undefined();
1777 // Builtin functions
1778 if (index == QOBJECT_TOSTRING_INDEX) {
1779 return ToString(resource->engine, object, argCount, arguments);
1780 } else if (index == QOBJECT_DESTROY_INDEX) {
1781 return Destroy(resource->engine, object, argCount, arguments);
1783 return v8::Undefined();
1787 QDeclarativePropertyCache::Data method;
1789 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1790 if (ddata->propertyCache) {
1791 QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1793 return v8::Undefined();
1798 if (method.coreIndex == -1) {
1799 QMetaMethod mm = object->metaObject()->method(index);
1800 method.load(object->metaObject()->method(index));
1802 if (method.coreIndex == -1)
1803 return v8::Undefined();
1806 if (method.isV8Function()) {
1807 v8::Handle<v8::Value> rv;
1808 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1810 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1811 resource->engine->contextWrapper()->context(qmlglobal),
1813 QDeclarativeV8Function *funcptr = &func;
1815 void *args[] = { 0, &funcptr };
1816 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1818 if (rv.IsEmpty()) return v8::Undefined();
1822 CallArgs callArgs(argCount, &arguments);
1823 if (method.relatedIndex == -1) {
1824 return CallPrecise(object, method, resource->engine, callArgs);
1826 return CallOverloaded(object, method, resource->engine, callArgs);
1830 MetaCallArgument::MetaCallArgument()
1831 : type(QVariant::Invalid)
1835 MetaCallArgument::~MetaCallArgument()
1840 void MetaCallArgument::cleanup()
1842 if (type == QMetaType::QString) {
1843 qstringPtr->~QString();
1844 } else if (type == -1 || type == QMetaType::QVariant) {
1845 qvariantPtr->~QVariant();
1846 } else if (type == qMetaTypeId<QJSValue>()) {
1847 qjsValuePtr->~QJSValue();
1848 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1849 qlistPtr->~QList<QObject *>();
1853 void *MetaCallArgument::dataPtr()
1856 return qvariantPtr->data();
1858 return (void *)&allocData;
1861 void MetaCallArgument::initAsType(int callType)
1863 if (type != 0) { cleanup(); type = 0; }
1864 if (callType == 0) return;
1866 if (callType == qMetaTypeId<QJSValue>()) {
1867 qjsValuePtr = new (&allocData) QJSValue();
1869 } else if (callType == QMetaType::Int ||
1870 callType == QMetaType::UInt ||
1871 callType == QMetaType::Bool ||
1872 callType == QMetaType::Double ||
1873 callType == QMetaType::Float) {
1875 } else if (callType == QMetaType::QObjectStar) {
1878 } else if (callType == QMetaType::QString) {
1879 qstringPtr = new (&allocData) QString();
1881 } else if (callType == QMetaType::QVariant) {
1883 qvariantPtr = new (&allocData) QVariant();
1884 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1886 qlistPtr = new (&allocData) QList<QObject *>();
1887 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1889 handlePtr = new (&allocData) QDeclarativeV8Handle;
1892 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
1896 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1898 if (type != 0) { cleanup(); type = 0; }
1900 if (callType == qMetaTypeId<QJSValue>()) {
1901 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
1902 type = qMetaTypeId<QJSValue>();
1903 } else if (callType == QMetaType::Int) {
1904 intValue = quint32(value->Int32Value());
1906 } else if (callType == QMetaType::UInt) {
1907 intValue = quint32(value->Uint32Value());
1909 } else if (callType == QMetaType::Bool) {
1910 boolValue = value->BooleanValue();
1912 } else if (callType == QMetaType::Double) {
1913 doubleValue = double(value->NumberValue());
1915 } else if (callType == QMetaType::Float) {
1916 floatValue = float(value->NumberValue());
1918 } else if (callType == QMetaType::QString) {
1919 if (value->IsNull() || value->IsUndefined())
1920 qstringPtr = new (&allocData) QString();
1922 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
1924 } else if (callType == QMetaType::QObjectStar) {
1925 qobjectPtr = engine->toQObject(value);
1927 } else if (callType == qMetaTypeId<QVariant>()) {
1928 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
1930 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1931 qlistPtr = new (&allocData) QList<QObject *>();
1932 if (value->IsArray()) {
1933 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1934 uint32_t length = array->Length();
1935 for (uint32_t ii = 0; ii < length; ++ii)
1936 qlistPtr->append(engine->toQObject(array->Get(ii)));
1938 qlistPtr->append(engine->toQObject(value));
1941 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1942 handlePtr = new (&allocData) QDeclarativeV8Handle(QDeclarativeV8Handle::fromHandle(value));
1945 qvariantPtr = new (&allocData) QVariant();
1948 QDeclarativeEnginePrivate *ep = engine->engine() ? QDeclarativeEnginePrivate::get(engine->engine()) : 0;
1949 QVariant v = engine->toVariant(value, -1);
1951 if (v.userType() == callType) {
1953 } else if (v.canConvert((QVariant::Type)callType)) {
1955 qvariantPtr->convert((QVariant::Type)callType);
1956 } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
1957 QObject *obj = ep->toQObject(v);
1960 const QMetaObject *objMo = obj->metaObject();
1961 while (objMo && objMo != mo) objMo = objMo->superClass();
1962 if (!objMo) obj = 0;
1965 *qvariantPtr = QVariant(callType, &obj);
1967 *qvariantPtr = QVariant(callType, (void *)0);
1972 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1974 if (type == qMetaTypeId<QJSValue>()) {
1975 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
1976 } else if (type == QMetaType::Int) {
1977 return v8::Integer::New(int(intValue));
1978 } else if (type == QMetaType::UInt) {
1979 return v8::Integer::NewFromUnsigned(intValue);
1980 } else if (type == QMetaType::Bool) {
1981 return v8::Boolean::New(boolValue);
1982 } else if (type == QMetaType::Double) {
1983 return v8::Number::New(doubleValue);
1984 } else if (type == QMetaType::Float) {
1985 return v8::Number::New(floatValue);
1986 } else if (type == QMetaType::QString) {
1987 return engine->toString(*qstringPtr);
1988 } else if (type == QMetaType::QObjectStar) {
1989 QObject *object = qobjectPtr;
1991 QDeclarativeData::get(object, true)->setImplicitDestructible();
1992 return engine->newQObject(object);
1993 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1994 // XXX Can this be made more by using Array as a prototype and implementing
1995 // directly against QList<QObject*>?
1996 QList<QObject *> &list = *qlistPtr;
1997 v8::Local<v8::Array> array = v8::Array::New(list.count());
1998 for (int ii = 0; ii < list.count(); ++ii)
1999 array->Set(ii, engine->newQObject(list.at(ii)));
2001 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
2002 return handlePtr->toHandle();
2003 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2004 QVariant value = *qvariantPtr;
2005 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2006 if (QObject *object = engine->toQObject(rv))
2007 QDeclarativeData::get(object, true)->setImplicitDestructible();
2010 return v8::Undefined();