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>
59 Q_DECLARE_METATYPE(QJSValue);
60 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;
112 class QV8SignalHandlerResource : public QV8ObjectResource
114 V8_RESOURCE_TYPE(SignalHandlerType)
116 QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index);
118 QDeclarativeGuard<QObject> object;
124 template<typename A, typename B, typename C, typename D, typename E>
126 template<typename Z, typename X>
128 static const size_t Size = sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X);
131 static const size_t Size = SMax<A, SMax<B, SMax<C, SMax<D, E> > > >::Size;
134 struct MetaCallArgument {
135 inline MetaCallArgument();
136 inline ~MetaCallArgument();
137 inline void *dataPtr();
139 inline void initAsType(int type);
140 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
141 inline v8::Handle<v8::Value> toValue(QV8Engine *);
144 MetaCallArgument(const MetaCallArgument &);
146 inline void cleanup();
155 char allocData[MaxSizeOf5<QVariant,
159 QDeclarativeV8Handle>::Size];
160 qint64 q_for_alignment;
163 // Pointers to allocData
166 QVariant *qvariantPtr;
167 QList<QObject *> *qlistPtr;
168 QJSValue *qjsValuePtr;
169 QDeclarativeV8Handle *handlePtr;
176 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
177 : QV8ObjectResource(engine), object(object)
181 QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index)
182 : QV8ObjectResource(engine), object(object), index(index)
186 static QAtomicInt objectIdCounter(1);
188 QV8QObjectWrapper::QV8QObjectWrapper()
189 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
193 QV8QObjectWrapper::~QV8QObjectWrapper()
195 for (TaintedHash::Iterator iter = m_taintedObjects.begin();
196 iter != m_taintedObjects.end();
198 (*iter)->wrapper = 0;
200 m_taintedObjects.clear();
203 void QV8QObjectWrapper::destroy()
205 qDeleteAll(m_connections);
206 m_connections.clear();
208 qPersistentDispose(m_hiddenObject);
209 qPersistentDispose(m_destroySymbol);
210 qPersistentDispose(m_toStringSymbol);
211 qPersistentDispose(m_signalHandlerConstructor);
212 qPersistentDispose(m_methodConstructor);
213 qPersistentDispose(m_constructor);
216 #define FAST_VALUE_GETTER(name, cpptype, defaultvalue, constructor) \
217 static v8::Handle<v8::Value> name ## ValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) \
219 v8::Handle<v8::Object> This = info.This(); \
220 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
222 if (resource->object.isNull()) return v8::Undefined(); \
224 QObject *object = resource->object; \
226 uint32_t data = info.Data()->Uint32Value(); \
227 int index = data & 0x7FFF; \
228 int notify = (data & 0x0FFF0000) >> 16; \
229 if (notify == 0x0FFF) notify = -1; \
231 QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
232 if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
233 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
234 ep->capturedProperties << CapturedProperty(object, index, notify); \
237 cpptype value = defaultvalue; \
238 void *args[] = { &value, 0 }; \
239 QMetaObject::metacall(object, QMetaObject::ReadProperty, index, args); \
241 return constructor(value); \
243 static v8::Handle<v8::Value> name ## ValueGetterDirect(v8::Local<v8::String>, const v8::AccessorInfo &info) \
245 v8::Handle<v8::Object> This = info.This(); \
246 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
248 if (resource->object.isNull()) return v8::Undefined(); \
250 QObject *object = resource->object; \
252 uint32_t data = info.Data()->Uint32Value(); \
253 int index = data & 0x7FFF; \
254 int notify = (data & 0x0FFF0000) >> 16; \
255 if (notify == 0x0FFF) notify = -1; \
257 QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
258 if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
259 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
260 ep->capturedProperties << CapturedProperty(object, index, notify); \
263 cpptype value = defaultvalue; \
264 void *args[] = { &value, 0 }; \
265 object->qt_metacall(QMetaObject::ReadProperty, index, args); \
267 return constructor(value); \
270 #define CREATE_FUNCTION \
271 "(function(method) { "\
272 "return (function(object, data, qmlglobal) { "\
273 "return (function() { "\
274 "return method(object, data, qmlglobal, arguments.length, arguments); "\
280 static quint32 toStringHash = -1;
281 static quint32 destroyHash = -1;
283 void QV8QObjectWrapper::init(QV8Engine *engine)
287 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
288 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
289 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
291 m_toStringString = QHashedV8String(m_toStringSymbol);
292 m_destroyString = QHashedV8String(m_destroySymbol);
294 toStringHash = m_toStringString.hash();
295 destroyHash = m_destroyString.hash();
298 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
299 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
300 ft->InstanceTemplate()->SetHasExternalResource(true);
301 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
304 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
305 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin);
306 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
307 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
308 v8::Handle<v8::Value> args[] = { invokeFn };
309 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
310 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
313 v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine);
314 v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine);
317 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
318 ft->InstanceTemplate()->SetHasExternalResource(true);
319 ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum);
320 ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
321 m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
325 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
326 prototype->Set(v8::String::New("connect"), connect, v8::DontEnum);
327 prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
331 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
333 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
336 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
338 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
339 return r?r->object:0;
342 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
343 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
345 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
346 return static_cast<QV8QObjectResource *>(r)->object;
349 // Load value properties
350 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
351 const QDeclarativePropertyData &property)
353 Q_ASSERT(!property.isFunction());
355 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
356 if (property.propType == QMetaType:: metatype) { \
357 cpptype type = cpptype(); \
358 void *args[] = { &type, 0 }; \
359 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \
360 return constructor(type); \
363 if (property.isQObject()) {
365 void *args[] = { &rv, 0 };
366 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
367 return engine->newQObject(rv);
368 } else if (property.isQList()) {
369 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
370 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
371 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
372 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
373 else PROPERTY_LOAD(QString, QString, engine->toString)
374 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
375 else PROPERTY_LOAD(Float, float, v8::Number::New)
376 else PROPERTY_LOAD(Double, double, v8::Number::New)
377 else if(property.isV8Handle()) {
378 QDeclarativeV8Handle handle;
379 void *args[] = { &handle, 0 };
380 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
381 return handle.toHandle();
382 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)
383 && engine->engine()) {
384 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
385 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
387 return engine->newValueType(object, property.coreIndex, valueType);
390 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
391 return engine->fromVariant(var);
396 static v8::Handle<v8::Value> LoadPropertyDirect(QV8Engine *engine, QObject *object,
397 const QDeclarativePropertyData &property)
399 Q_ASSERT(!property.isFunction());
401 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
402 if (property.propType == QMetaType:: metatype) { \
403 cpptype type = cpptype(); \
404 void *args[] = { &type, 0 }; \
405 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); \
406 return constructor(type); \
409 if (property.isQObject()) {
411 void *args[] = { &rv, 0 };
412 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
413 return engine->newQObject(rv);
414 } else if (property.isQList()) {
415 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
416 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
417 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
418 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
419 else PROPERTY_LOAD(QString, QString, engine->toString)
420 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
421 else PROPERTY_LOAD(Float, float, v8::Number::New)
422 else PROPERTY_LOAD(Double, double, v8::Number::New)
423 else if(property.isV8Handle()) {
424 QDeclarativeV8Handle handle;
425 void *args[] = { &handle, 0 };
426 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
427 return handle.toHandle();
428 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)
429 && engine->engine()) {
430 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
431 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
433 return engine->newValueType(object, property.coreIndex, valueType);
436 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
437 return engine->fromVariant(var);
442 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
443 v8::Handle<v8::Value> *objectHandle,
444 const QHashedV8String &property,
445 QV8QObjectWrapper::RevisionMode revisionMode)
447 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
448 // will be a faster way of creating QObject method objects.
449 struct MethodClosure {
450 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
451 v8::Handle<v8::Value> *objectHandle,
453 v8::Handle<v8::Value> argv[] = {
454 objectHandle?*objectHandle:engine->newQObject(object),
455 v8::Integer::New(index)
457 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
459 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
460 v8::Handle<v8::Value> *objectHandle,
462 v8::Handle<v8::Value> argv[] = {
463 objectHandle?*objectHandle:engine->newQObject(object),
464 v8::Integer::New(index),
465 v8::Context::GetCallingQmlGlobal()
467 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
472 // Comparing the hash first actually makes a measurable difference here, at least on x86
473 quint32 hash = property.hash();
474 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
475 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
476 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
477 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
481 QDeclarativePropertyData local;
482 QDeclarativePropertyData *result = 0;
484 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
485 if (ddata && ddata->propertyCache)
486 result = ddata->propertyCache->property(property);
488 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
492 return v8::Handle<v8::Value>();
494 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
495 QDeclarativeData *ddata = QDeclarativeData::get(object);
496 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
497 return v8::Handle<v8::Value>();
500 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
502 if (result->isFunction()) {
503 if (result->isVMEFunction()) {
504 return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
505 } else if (result->isV8Function()) {
506 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
507 } else if (result->isSignalHandler()) {
508 v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
509 QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
510 handler->SetExternalResource(r);
513 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
517 QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
518 if (ep && ep->captureProperties && !result->isConstant()) {
519 if (result->coreIndex == 0)
520 ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
522 ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
525 if (result->isVMEProperty())
526 return static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject*>(object->metaObject()))->vmeProperty(result->coreIndex);
528 if (result->isDirect()) {
529 return LoadPropertyDirect(engine, object, *result);
531 return LoadProperty(engine, object, *result);
535 // Setter for writable properties. Shared between the interceptor and fast property accessor
536 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyData *property,
537 v8::Handle<v8::Value> value)
539 QDeclarativeBinding *newBinding = 0;
541 if (value->IsFunction()) {
542 QDeclarativeContextData *context = engine->callingContext();
543 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
545 v8::Local<v8::StackTrace> trace =
546 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
547 v8::StackTrace::kScriptName));
548 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
549 int lineNumber = frame->GetLineNumber();
550 QString url = engine->toString(frame->GetScriptName());
552 newBinding = new QDeclarativeBinding(&function, object, context);
553 newBinding->setSourceLocation(url, lineNumber);
554 newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, object, context));
555 newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
558 QDeclarativeAbstractBinding *oldBinding =
559 QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
561 oldBinding->destroy();
563 #define PROPERTY_STORE(cpptype, value) \
567 void *argv[] = { &o, 0, &status, &flags }; \
568 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
571 if (value->IsNull() && property->isQObject()) {
572 PROPERTY_STORE(QObject*, 0);
573 } else if (value->IsUndefined() && property->isResettable()) {
575 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
576 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
577 PROPERTY_STORE(QVariant, QVariant());
578 } else if (value->IsUndefined()) {
579 QString error = QLatin1String("Cannot assign [undefined] to ") +
580 QLatin1String(QMetaType::typeName(property->propType));
581 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
582 } else if (value->IsFunction()) {
583 // this is handled by the binding creation above
584 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
585 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
586 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
587 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
588 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
589 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
590 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
591 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
592 } else if (property->propType == QMetaType::QString && value->IsString()) {
593 PROPERTY_STORE(QString, engine->toString(value->ToString()));
594 } else if (property->isVMEProperty()) {
595 static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
598 if (property->isQList())
599 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
601 v = engine->toVariant(value, property->propType);
603 QDeclarativeContextData *context = engine->callingContext();
605 if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
606 const char *valueType = 0;
607 if (v.userType() == QVariant::Invalid) valueType = "null";
608 else valueType = QMetaType::typeName(v.userType());
610 QString error = QLatin1String("Cannot assign ") +
611 QLatin1String(valueType) +
612 QLatin1String(" to ") +
613 QLatin1String(QMetaType::typeName(property->propType));
614 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
619 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
620 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
622 if (engine->qobjectWrapper()->m_toStringString == property ||
623 engine->qobjectWrapper()->m_destroyString == property)
626 QDeclarativePropertyData local;
627 QDeclarativePropertyData *result = 0;
628 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
633 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
634 QDeclarativeData *ddata = QDeclarativeData::get(object);
635 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
639 if (!result->isWritable() && !result->isQList()) {
640 QString error = QLatin1String("Cannot assign to read-only property \"") +
641 engine->toString(property.string()) + QLatin1Char('\"');
642 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
646 StoreProperty(engine, object, result, value);
651 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
652 const v8::AccessorInfo &info)
654 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
656 if (resource->object.isNull())
657 return v8::Handle<v8::Value>();
659 QObject *object = resource->object;
661 QHashedV8String propertystring(property);
663 QV8Engine *v8engine = resource->engine;
664 v8::Handle<v8::Value> This = info.This();
665 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
666 QV8QObjectWrapper::IgnoreRevision);
667 if (!result.IsEmpty())
670 if (QV8Engine::startsWithUpper(property)) {
671 // Check for attached properties
672 QDeclarativeContextData *context = v8engine->callingContext();
674 if (context && context->imports) {
675 QDeclarativeTypeNameCache::Result r = context->imports->query(propertystring);
678 if (r.scriptIndex != -1) {
679 return v8::Undefined();
681 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
682 } else if (r.importNamespace) {
683 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
684 QV8TypeWrapper::ExcludeEnums);
686 Q_ASSERT(!"Unreachable");
691 return v8::Handle<v8::Value>();
694 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
695 v8::Local<v8::Value> value,
696 const v8::AccessorInfo &info)
698 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
700 if (resource->object.isNull())
703 QObject *object = resource->object;
705 QHashedV8String propertystring(property);
707 QV8Engine *v8engine = resource->engine;
708 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
711 QString error = QLatin1String("Cannot assign to non-existent property \"") +
712 v8engine->toString(property) + QLatin1Char('\"');
713 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
720 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
721 const v8::AccessorInfo &info)
723 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
725 if (resource->object.isNull())
726 return v8::Handle<v8::Integer>();
728 QV8Engine *engine = resource->engine;
729 QObject *object = resource->object;
731 QHashedV8String propertystring(property);
733 QDeclarativePropertyData local;
734 QDeclarativePropertyData *result = 0;
735 result = QDeclarativePropertyCache::property(engine->engine(), object, propertystring, local);
738 return v8::Handle<v8::Integer>();
739 else if (!result->isWritable() && !result->isQList())
740 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
742 return v8::Integer::New(v8::DontDelete);
745 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
747 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
749 if (resource->object.isNull())
750 return v8::Array::New();
752 QObject *object = resource->object;
756 QDeclarativeEnginePrivate *ep = resource->engine->engine()
757 ? QDeclarativeEnginePrivate::get(resource->engine->engine())
760 QDeclarativePropertyCache *cache = 0;
761 QDeclarativeData *ddata = QDeclarativeData::get(object);
763 cache = ddata->propertyCache;
766 cache = ep ? ep->cache(object) : 0;
768 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
770 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
771 const QMetaObject *mo = object->metaObject();
772 int pc = mo->propertyCount();
773 int po = mo->propertyOffset();
774 for (int i=po; i<pc; ++i)
775 result << QString::fromUtf8(mo->property(i).name());
778 result = cache->propertyNames();
781 v8::Local<v8::Array> rv = v8::Array::New(result.count());
783 for (int ii = 0; ii < result.count(); ++ii)
784 rv->Set(ii, resource->engine->toString(result.at(ii)));
789 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
790 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
791 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
792 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
793 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
794 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
795 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
797 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
798 const v8::AccessorInfo& info)
800 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
802 if (resource->object.isNull())
805 QObject *object = resource->object;
807 uint32_t data = info.Data()->Uint32Value();
808 int index = data & 0x7FFF; // So that we can use the same data for Setter and Getter
810 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
812 Q_ASSERT(ddata->propertyCache);
814 QDeclarativePropertyData *pdata = ddata->propertyCache->property(index);
817 Q_ASSERT(pdata->isWritable() || pdata->isQList());
819 StoreProperty(resource->engine, object, pdata, value);
822 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
823 const v8::AccessorInfo& info)
825 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
827 if (resource->object.isNull())
830 QV8Engine *v8engine = resource->engine;
832 QString error = QLatin1String("Cannot assign to read-only property \"") +
833 v8engine->toString(property) + QLatin1Char('\"');
834 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
837 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
839 Q_ASSERT(handle->IsObject());
841 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
845 QObject *object = resource->object;
847 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
849 ddata->v8object.Clear();
850 if (!object->parent() && !ddata->indestructible)
851 object->deleteLater();
855 qPersistentDispose(handle);
858 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
860 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
861 instance->v8object.Clear();
862 qPersistentDispose(handle);
865 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
868 Q_ASSERT(this->engine);
870 Q_ASSERT(QDeclarativeData::get(object, false));
871 Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
874 if (constructor.IsEmpty()) {
875 v8::Local<v8::FunctionTemplate> ft;
877 QString toString = QLatin1String("toString");
878 QString destroy = QLatin1String("destroy");
880 // XXX TODO: Enables fast property accessors. These more than double the property access
881 // performance, but the cost of setting up this structure hasn't been measured so
882 // its not guarenteed that this is a win overall. We need to try and measure the cost.
883 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
884 QDeclarativePropertyData *property = *iter;
885 if (property->isFunction() ||
886 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x0FFF ||
887 property->coreIndex == 0)
890 v8::AccessorGetter fastgetter = 0;
891 v8::AccessorSetter fastsetter = FastValueSetter;
892 if (!property->isWritable())
893 fastsetter = FastValueSetterReadOnly;
895 if (property->isQObject())
896 fastgetter = property->isDirect()?QObjectValueGetterDirect:QObjectValueGetter;
897 else if (property->propType == QMetaType::Int || property->isEnum())
898 fastgetter = property->isDirect()?IntValueGetterDirect:IntValueGetter;
899 else if (property->propType == QMetaType::Bool)
900 fastgetter = property->isDirect()?BoolValueGetterDirect:BoolValueGetter;
901 else if (property->propType == QMetaType::QString)
902 fastgetter = property->isDirect()?QStringValueGetterDirect:QStringValueGetter;
903 else if (property->propType == QMetaType::UInt)
904 fastgetter = property->isDirect()?UIntValueGetterDirect:UIntValueGetter;
905 else if (property->propType == QMetaType::Float)
906 fastgetter = property->isDirect()?FloatValueGetterDirect:FloatValueGetter;
907 else if (property->propType == QMetaType::Double)
908 fastgetter = property->isDirect()?DoubleValueGetterDirect:DoubleValueGetter;
911 int notifyIndex = property->notifyIndex;
912 if (property->isConstant()) notifyIndex = 0;
913 else if (notifyIndex == -1) notifyIndex = 0x0FFF;
914 uint32_t data = (notifyIndex & 0x0FFF) << 16 | property->coreIndex;
916 QString name = iter.key();
917 if (name == toString || name == destroy)
921 ft = v8::FunctionTemplate::New();
922 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
923 QV8QObjectWrapper::Setter,
924 QV8QObjectWrapper::Query,
926 QV8QObjectWrapper::Enumerator);
927 ft->InstanceTemplate()->SetHasExternalResource(true);
930 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
931 v8::Integer::NewFromUnsigned(data));
936 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
938 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
939 QV8QObjectWrapper::Setter,
940 QV8QObjectWrapper::Query,
942 QV8QObjectWrapper::Enumerator);
943 ft->InstanceTemplate()->SetHasExternalResource(true);
944 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
947 QDeclarativeCleanup::addToEngine(this->engine);
950 v8::Local<v8::Object> result = constructor->NewInstance();
951 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
952 result->SetExternalResource(r);
956 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
958 v8::Local<v8::Object> rv;
960 if (!ddata->propertyCache && engine->engine()) {
961 ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
962 if (ddata->propertyCache) ddata->propertyCache->addref();
965 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
966 rv = ddata->propertyCache->newQObject(object, engine);
968 // XXX NewInstance() should be optimized
969 rv = m_constructor->NewInstance();
970 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
971 rv->SetExternalResource(r);
978 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
979 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
981 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
982 persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that
983 "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this
985 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create
986 an entry in the m_taintedObject hash where we store the handle and mark the object as
987 "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
988 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
989 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has
992 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
997 if (QObjectPrivate::get(object)->wasDeleted)
998 return v8::Undefined();
1000 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
1003 return v8::Undefined();
1005 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1006 // We own the v8object
1007 return v8::Local<v8::Object>::New(ddata->v8object);
1008 } else if (ddata->v8object.IsEmpty() &&
1009 (ddata->v8objectid == m_id || // We own the QObject
1010 ddata->v8objectid == 0 || // No one owns the QObject
1011 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1013 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1014 ddata->v8object = qPersistentNew<v8::Object>(rv);
1015 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1016 ddata->v8objectid = m_id;
1020 // If this object is tainted, we have to check to see if it is in our
1021 // tainted object list
1022 TaintedHash::Iterator iter =
1023 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1024 bool found = iter != m_taintedObjects.end();
1026 // If our tainted handle doesn't exist or has been collected, and there isn't
1027 // a handle in the ddata, we can assume ownership of the ddata->v8object
1028 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1029 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1030 ddata->v8object = qPersistentNew<v8::Object>(rv);
1031 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1032 ddata->v8objectid = m_id;
1036 m_taintedObjects.erase(iter);
1040 } else if (!found) {
1041 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1042 iter = m_taintedObjects.insert(object, instance);
1043 ddata->hasTaintedV8Object = true;
1046 if ((*iter)->v8object.IsEmpty()) {
1047 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1048 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1049 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1052 return v8::Local<v8::Object>::New((*iter)->v8object);
1056 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1058 if (object->IsFunction())
1059 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1061 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1062 return qMakePair(resource->object.data(), resource->index);
1064 return qMakePair((QObject *)0, -1);
1067 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1069 v8::ScriptOrigin origin = function->GetScriptOrigin();
1070 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1072 // This is one of our special QObject method wrappers
1073 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1074 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1076 if (data->IsArray()) {
1077 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1078 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1081 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1084 return qMakePair((QObject *)0, -1);
1087 class QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
1090 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1091 ~QV8QObjectConnectionList();
1095 : needsDestroy(false) {}
1096 Connection(const Connection &other)
1097 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1098 Connection &operator=(const Connection &other) {
1099 thisObject = other.thisObject;
1100 function = other.function;
1101 needsDestroy = other.needsDestroy;
1105 v8::Persistent<v8::Object> thisObject;
1106 v8::Persistent<v8::Function> function;
1109 qPersistentDispose(thisObject);
1110 qPersistentDispose(function);
1116 struct ConnectionList : public QList<Connection> {
1117 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1118 int connectionsInUse;
1119 bool connectionsNeedClean;
1124 typedef QHash<int, ConnectionList> SlotHash;
1129 virtual void objectDestroyed(QObject *);
1130 virtual int qt_metacall(QMetaObject::Call, int, void **);
1133 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1134 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1138 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1140 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1141 QList<Connection> &connections = *iter;
1142 for (int ii = 0; ii < connections.count(); ++ii) {
1143 qPersistentDispose(connections[ii].thisObject);
1144 qPersistentDispose(connections[ii].function);
1150 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1152 engine->qobjectWrapper()->m_connections.remove(object);
1155 needsDestroy = true;
1160 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1162 if (method == QMetaObject::InvokeMetaMethod) {
1163 SlotHash::Iterator iter = slotHash.find(index);
1164 if (iter == slotHash.end())
1166 ConnectionList &connectionList = *iter;
1167 if (connectionList.isEmpty())
1172 connectionList.connectionsInUse++;
1174 QList<Connection> connections = connectionList;
1176 QVarLengthArray<int, 9> dummy;
1177 int *argsTypes = QDeclarativePropertyCache::methodParameterTypes(data(), index, dummy, 0);
1179 v8::HandleScope handle_scope;
1180 v8::Context::Scope scope(engine->context());
1182 int argCount = argsTypes?argsTypes[0]:0;
1183 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1185 for (int ii = 0; ii < argCount; ++ii) {
1186 int type = argsTypes[ii + 1];
1187 if (type == qMetaTypeId<QVariant>()) {
1188 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1190 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1194 for (int ii = 0; ii < connections.count(); ++ii) {
1195 Connection &connection = connections[ii];
1196 if (connection.needsDestroy)
1198 if (connection.thisObject.IsEmpty()) {
1199 connection.function->Call(engine->global(), argCount, args.data());
1201 connection.function->Call(connection.thisObject, argCount, args.data());
1205 connectionList.connectionsInUse--;
1206 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1207 for (QList<Connection>::Iterator iter = connectionList.begin();
1208 iter != connectionList.end(); ) {
1209 if (iter->needsDestroy) {
1211 iter = connectionList.erase(iter);
1219 if (inUse == 0 && needsDestroy)
1226 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1228 if (args.Length() == 0)
1229 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1231 QV8Engine *engine = V8ENGINE();
1233 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1234 QObject *signalObject = signalInfo.first;
1235 int signalIndex = signalInfo.second;
1237 if (signalIndex == -1)
1238 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1241 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1243 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1244 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1246 v8::Local<v8::Value> functionValue;
1247 v8::Local<v8::Value> functionThisValue;
1249 if (args.Length() == 1) {
1250 functionValue = args[0];
1252 functionThisValue = args[0];
1253 functionValue = args[1];
1256 if (!functionValue->IsFunction())
1257 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1259 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1260 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1262 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1263 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1264 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1265 if (iter == connections.end())
1266 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1268 QV8QObjectConnectionList *connectionList = *iter;
1269 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1270 if (slotIter == connectionList->slotHash.end()) {
1271 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1272 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1275 QV8QObjectConnectionList::Connection connection;
1276 if (!functionThisValue.IsEmpty())
1277 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1278 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1280 slotIter->append(connection);
1282 return v8::Undefined();
1285 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1287 if (args.Length() == 0)
1288 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1290 QV8Engine *engine = V8ENGINE();
1292 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1293 QObject *signalObject = signalInfo.first;
1294 int signalIndex = signalInfo.second;
1296 if (signalIndex == -1)
1297 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1300 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1302 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1303 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1305 v8::Local<v8::Value> functionValue;
1306 v8::Local<v8::Value> functionThisValue;
1308 if (args.Length() == 1) {
1309 functionValue = args[0];
1311 functionThisValue = args[0];
1312 functionValue = args[1];
1315 if (!functionValue->IsFunction())
1316 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1318 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1319 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1321 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1322 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1323 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1324 if (iter == connectionsList.end())
1325 return v8::Undefined(); // Nothing to disconnect from
1327 QV8QObjectConnectionList *connectionList = *iter;
1328 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1329 if (slotIter == connectionList->slotHash.end())
1330 return v8::Undefined(); // Nothing to disconnect from
1332 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1334 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1335 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1337 if (functionData.second != -1) {
1338 // This is a QObject function wrapper
1339 for (int ii = 0; ii < connections.count(); ++ii) {
1340 QV8QObjectConnectionList::Connection &connection = connections[ii];
1342 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1343 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1345 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1346 if (connectedFunctionData == functionData) {
1348 if (connections.connectionsInUse) {
1349 connection.needsDestroy = true;
1351 connection.dispose();
1352 connections.removeAt(ii);
1354 return v8::Undefined();
1360 // This is a normal JS function
1361 for (int ii = 0; ii < connections.count(); ++ii) {
1362 QV8QObjectConnectionList::Connection &connection = connections[ii];
1363 if (connection.function->StrictEquals(function) &&
1364 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1365 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1367 if (connections.connectionsInUse) {
1368 connection.needsDestroy = true;
1370 connection.dispose();
1371 connections.removeAt(ii);
1373 return v8::Undefined();
1378 return v8::Undefined();
1382 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1384 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1386 Only searches for real properties of \a object (including methods), not attached properties etc.
1390 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1392 Set the \a property of \a object to \a value.
1394 Returns true if the property was "set" - even if this results in an exception being thrown -
1395 and false if the object has no such property.
1397 Only searches for real properties of \a object (including methods), not attached properties etc.
1403 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1404 int Length() const { return _length; }
1405 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1409 v8::Handle<v8::Object> *_args;
1413 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1414 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1418 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1419 args[0].initAsType(returnType);
1421 for (int ii = 0; ii < argCount; ++ii)
1422 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1424 QVarLengthArray<void *, 9> argData(args.count());
1425 for (int ii = 0; ii < args.count(); ++ii)
1426 argData[ii] = args[ii].dataPtr();
1428 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1430 return args[0].toValue(engine);
1432 } else if (returnType != 0) {
1434 MetaCallArgument arg;
1435 arg.initAsType(returnType);
1437 void *args[] = { arg.dataPtr() };
1439 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1441 return arg.toValue(engine);
1445 void *args[] = { 0 };
1446 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1447 return v8::Undefined();
1453 Returns the match score for converting \a actual to be of type \a conversionType. A
1454 zero score means "perfect match" whereas a higher score is worse.
1456 The conversion table is copied out of the QtScript callQtMethod() function.
1458 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1460 if (actual->IsNumber()) {
1461 switch (conversionType) {
1462 case QMetaType::Double:
1464 case QMetaType::Float:
1466 case QMetaType::LongLong:
1467 case QMetaType::ULongLong:
1469 case QMetaType::Long:
1470 case QMetaType::ULong:
1472 case QMetaType::Int:
1473 case QMetaType::UInt:
1475 case QMetaType::Short:
1476 case QMetaType::UShort:
1479 case QMetaType::Char:
1480 case QMetaType::UChar:
1485 } else if (actual->IsString()) {
1486 switch (conversionType) {
1487 case QMetaType::QString:
1492 } else if (actual->IsBoolean()) {
1493 switch (conversionType) {
1494 case QMetaType::Bool:
1499 } else if (actual->IsDate()) {
1500 switch (conversionType) {
1501 case QMetaType::QDateTime:
1503 case QMetaType::QDate:
1505 case QMetaType::QTime:
1510 } else if (actual->IsRegExp()) {
1511 switch (conversionType) {
1512 case QMetaType::QRegExp:
1517 } else if (actual->IsArray()) {
1518 switch (conversionType) {
1519 case QMetaType::QStringList:
1520 case QMetaType::QVariantList:
1525 } else if (actual->IsNull()) {
1526 switch (conversionType) {
1527 case QMetaType::VoidStar:
1528 case QMetaType::QObjectStar:
1531 const char *typeName = QMetaType::typeName(conversionType);
1532 if (typeName && typeName[strlen(typeName) - 1] == '*')
1538 } else if (actual->IsObject()) {
1539 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1541 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1542 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1543 switch (conversionType) {
1544 case QMetaType::QObjectStar:
1549 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1550 if (conversionType == qMetaTypeId<QVariant>())
1552 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1565 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1571 int classInfoCount, classInfoData;
1572 int methodCount, methodData;
1575 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1578 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1580 QByteArray sig = m.signature();
1581 int paren = sig.indexOf('(');
1585 return sig.left(paren);
1589 Returns the next related method, if one, or 0.
1591 static const QDeclarativePropertyData * RelatedMethod(QObject *object,
1592 const QDeclarativePropertyData *current,
1593 QDeclarativePropertyData &dummy)
1595 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1596 if (!current->isOverload())
1599 Q_ASSERT(!current->overrideIndexIsProperty);
1602 return cache->method(current->overrideIndex);
1604 const QMetaObject *mo = object->metaObject();
1605 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1607 while (methodOffset > current->overrideIndex) {
1608 mo = mo->superClass();
1609 methodOffset -= QMetaObject_methods(mo);
1612 QMetaMethod method = mo->method(current->overrideIndex);
1615 // Look for overloaded methods
1616 QByteArray methodName = QMetaMethod_name(method);
1617 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1618 if (methodName == QMetaMethod_name(mo->method(ii))) {
1619 dummy.setFlags(dummy.getFlags() | QDeclarativePropertyData::IsOverload);
1620 dummy.overrideIndexIsProperty = 0;
1621 dummy.overrideIndex = ii;
1630 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyData &data,
1631 QV8Engine *engine, CallArgs &callArgs)
1633 if (data.hasArguments()) {
1636 QVarLengthArray<int, 9> dummy;
1637 QByteArray unknownTypeError;
1639 args = QDeclarativePropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1643 QString typeName = QString::fromLatin1(unknownTypeError);
1644 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1645 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1646 return v8::Handle<v8::Value>();
1649 if (args[0] > callArgs.Length()) {
1650 QString error = QLatin1String("Insufficient arguments");
1651 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1652 return v8::Handle<v8::Value>();
1655 return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1659 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1665 Resolve the overloaded method to call. The algorithm works conceptually like this:
1666 1. Resolve the set of overloads it is *possible* to call.
1667 Impossible overloads include those that have too many parameters or have parameters
1669 2. Filter the set of overloads to only contain those with the closest number of
1671 For example, if we are called with 3 parameters and there are 2 overloads that
1672 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1673 3. Find the best remaining overload based on its match score.
1674 If two or more overloads have the same match score, call the last one. The match
1675 score is constructed by adding the matchScore() result for each of the parameters.
1677 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyData &data,
1678 QV8Engine *engine, CallArgs &callArgs)
1680 int argumentCount = callArgs.Length();
1682 const QDeclarativePropertyData *best = 0;
1683 int bestParameterScore = INT_MAX;
1684 int bestMatchScore = INT_MAX;
1686 QDeclarativePropertyData dummy;
1687 const QDeclarativePropertyData *attempt = &data;
1690 QVarLengthArray<int, 9> dummy;
1691 int methodArgumentCount = 0;
1692 int *methodArgTypes = 0;
1693 if (attempt->hasArguments()) {
1694 typedef QDeclarativePropertyCache PC;
1695 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1696 if (!args) // Must be an unknown argument
1699 methodArgumentCount = args[0];
1700 methodArgTypes = args + 1;
1703 if (methodArgumentCount > argumentCount)
1704 continue; // We don't have sufficient arguments to call this method
1706 int methodParameterScore = argumentCount - methodArgumentCount;
1707 if (methodParameterScore > bestParameterScore)
1708 continue; // We already have a better option
1710 int methodMatchScore = 0;
1711 for (int ii = 0; ii < methodArgumentCount; ++ii)
1712 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1714 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1716 bestParameterScore = methodParameterScore;
1717 bestMatchScore = methodMatchScore;
1720 if (bestParameterScore == 0 && bestMatchScore == 0)
1721 break; // We can't get better than that
1723 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1726 return CallPrecise(object, *best, engine, callArgs);
1728 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1729 const QDeclarativePropertyData *candidate = &data;
1731 error += QLatin1String("\n ") +
1732 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1733 candidate = RelatedMethod(object, candidate, dummy);
1736 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1737 return v8::Handle<v8::Value>();
1741 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1745 QString objectName = object->objectName();
1747 result += QString::fromUtf8(object->metaObject()->className());
1748 result += QLatin1String("(0x");
1749 result += QString::number((quintptr)object,16);
1751 if (!objectName.isEmpty()) {
1752 result += QLatin1String(", \"");
1753 result += objectName;
1754 result += QLatin1Char('\"');
1757 result += QLatin1Char(')');
1759 result = QLatin1String("null");
1762 return engine->toString(result);
1765 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1767 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1768 if (!ddata || ddata->indestructible) {
1769 const char *error = "Invalid attempt to destroy() an indestructible object";
1770 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1771 return v8::Undefined();
1776 delay = args->Get(0)->Uint32Value();
1779 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1781 object->deleteLater();
1783 return v8::Undefined();
1786 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1788 // object, index, qmlglobal, argCount, args
1789 Q_ASSERT(args.Length() == 5);
1790 Q_ASSERT(args[0]->IsObject());
1792 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1795 return v8::Undefined();
1797 int argCount = args[3]->Int32Value();
1798 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1800 // Special hack to return info about this closure.
1801 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1802 v8::Local<v8::Array> data = v8::Array::New(2);
1803 data->Set(0, args[0]);
1804 data->Set(1, args[1]);
1808 QObject *object = resource->object;
1809 int index = args[1]->Int32Value();
1812 return v8::Undefined();
1815 // Builtin functions
1816 if (index == QOBJECT_TOSTRING_INDEX) {
1817 return ToString(resource->engine, object, argCount, arguments);
1818 } else if (index == QOBJECT_DESTROY_INDEX) {
1819 return Destroy(resource->engine, object, argCount, arguments);
1821 return v8::Undefined();
1825 QDeclarativePropertyData method;
1827 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1828 if (ddata->propertyCache) {
1829 QDeclarativePropertyData *d = ddata->propertyCache->method(index);
1831 return v8::Undefined();
1836 if (method.coreIndex == -1) {
1837 method.load(object->metaObject()->method(index));
1839 if (method.coreIndex == -1)
1840 return v8::Undefined();
1843 if (method.isV8Function()) {
1844 v8::Handle<v8::Value> rv;
1845 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1847 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1848 resource->engine->contextWrapper()->context(qmlglobal),
1850 QDeclarativeV8Function *funcptr = &func;
1852 void *args[] = { 0, &funcptr };
1853 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1855 if (rv.IsEmpty()) return v8::Undefined();
1859 CallArgs callArgs(argCount, &arguments);
1860 if (!method.isOverload()) {
1861 return CallPrecise(object, method, resource->engine, callArgs);
1863 return CallOverloaded(object, method, resource->engine, callArgs);
1867 MetaCallArgument::MetaCallArgument()
1868 : type(QVariant::Invalid)
1872 MetaCallArgument::~MetaCallArgument()
1877 void MetaCallArgument::cleanup()
1879 if (type == QMetaType::QString) {
1880 qstringPtr->~QString();
1881 } else if (type == -1 || type == QMetaType::QVariant) {
1882 qvariantPtr->~QVariant();
1883 } else if (type == qMetaTypeId<QJSValue>()) {
1884 qjsValuePtr->~QJSValue();
1885 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1886 qlistPtr->~QList<QObject *>();
1890 void *MetaCallArgument::dataPtr()
1893 return qvariantPtr->data();
1895 return (void *)&allocData;
1898 void MetaCallArgument::initAsType(int callType)
1900 if (type != 0) { cleanup(); type = 0; }
1901 if (callType == 0) return;
1903 if (callType == qMetaTypeId<QJSValue>()) {
1904 qjsValuePtr = new (&allocData) QJSValue();
1906 } else if (callType == QMetaType::Int ||
1907 callType == QMetaType::UInt ||
1908 callType == QMetaType::Bool ||
1909 callType == QMetaType::Double ||
1910 callType == QMetaType::Float) {
1912 } else if (callType == QMetaType::QObjectStar) {
1915 } else if (callType == QMetaType::QString) {
1916 qstringPtr = new (&allocData) QString();
1918 } else if (callType == QMetaType::QVariant) {
1920 qvariantPtr = new (&allocData) QVariant();
1921 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1923 qlistPtr = new (&allocData) QList<QObject *>();
1924 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1926 handlePtr = new (&allocData) QDeclarativeV8Handle;
1929 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
1933 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1935 if (type != 0) { cleanup(); type = 0; }
1937 if (callType == qMetaTypeId<QJSValue>()) {
1938 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
1939 type = qMetaTypeId<QJSValue>();
1940 } else if (callType == QMetaType::Int) {
1941 intValue = quint32(value->Int32Value());
1943 } else if (callType == QMetaType::UInt) {
1944 intValue = quint32(value->Uint32Value());
1946 } else if (callType == QMetaType::Bool) {
1947 boolValue = value->BooleanValue();
1949 } else if (callType == QMetaType::Double) {
1950 doubleValue = double(value->NumberValue());
1952 } else if (callType == QMetaType::Float) {
1953 floatValue = float(value->NumberValue());
1955 } else if (callType == QMetaType::QString) {
1956 if (value->IsNull() || value->IsUndefined())
1957 qstringPtr = new (&allocData) QString();
1959 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
1961 } else if (callType == QMetaType::QObjectStar) {
1962 qobjectPtr = engine->toQObject(value);
1964 } else if (callType == qMetaTypeId<QVariant>()) {
1965 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
1967 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1968 qlistPtr = new (&allocData) QList<QObject *>();
1969 if (value->IsArray()) {
1970 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1971 uint32_t length = array->Length();
1972 for (uint32_t ii = 0; ii < length; ++ii)
1973 qlistPtr->append(engine->toQObject(array->Get(ii)));
1975 qlistPtr->append(engine->toQObject(value));
1978 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1979 handlePtr = new (&allocData) QDeclarativeV8Handle(QDeclarativeV8Handle::fromHandle(value));
1982 qvariantPtr = new (&allocData) QVariant();
1985 QDeclarativeEnginePrivate *ep = engine->engine() ? QDeclarativeEnginePrivate::get(engine->engine()) : 0;
1986 QVariant v = engine->toVariant(value, -1);
1988 if (v.userType() == callType) {
1990 } else if (v.canConvert((QVariant::Type)callType)) {
1992 qvariantPtr->convert((QVariant::Type)callType);
1993 } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
1994 QObject *obj = ep->toQObject(v);
1997 const QMetaObject *objMo = obj->metaObject();
1998 while (objMo && objMo != mo) objMo = objMo->superClass();
1999 if (!objMo) obj = 0;
2002 *qvariantPtr = QVariant(callType, &obj);
2004 *qvariantPtr = QVariant(callType, (void *)0);
2009 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
2011 if (type == qMetaTypeId<QJSValue>()) {
2012 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2013 } else if (type == QMetaType::Int) {
2014 return v8::Integer::New(int(intValue));
2015 } else if (type == QMetaType::UInt) {
2016 return v8::Integer::NewFromUnsigned(intValue);
2017 } else if (type == QMetaType::Bool) {
2018 return v8::Boolean::New(boolValue);
2019 } else if (type == QMetaType::Double) {
2020 return v8::Number::New(doubleValue);
2021 } else if (type == QMetaType::Float) {
2022 return v8::Number::New(floatValue);
2023 } else if (type == QMetaType::QString) {
2024 return engine->toString(*qstringPtr);
2025 } else if (type == QMetaType::QObjectStar) {
2026 QObject *object = qobjectPtr;
2028 QDeclarativeData::get(object, true)->setImplicitDestructible();
2029 return engine->newQObject(object);
2030 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2031 // XXX Can this be made more by using Array as a prototype and implementing
2032 // directly against QList<QObject*>?
2033 QList<QObject *> &list = *qlistPtr;
2034 v8::Local<v8::Array> array = v8::Array::New(list.count());
2035 for (int ii = 0; ii < list.count(); ++ii)
2036 array->Set(ii, engine->newQObject(list.at(ii)));
2038 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
2039 return handlePtr->toHandle();
2040 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2041 QVariant value = *qvariantPtr;
2042 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2043 if (QObject *object = engine->toQObject(rv))
2044 QDeclarativeData::get(object, true)->setImplicitDestructible();
2047 return v8::Undefined();