1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** 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/qqmlguard_p.h>
47 #include <private/qqmlpropertycache_p.h>
48 #include <private/qqmlengine_p.h>
49 #include <private/qqmlvmemetaobject_p.h>
50 #include <private/qqmlbinding_p.h>
51 #include <private/qjsvalue_p.h>
52 #include <private/qscript_impl_p.h>
53 #include <private/qqmlaccessors_p.h>
54 #include <private/qqmlexpression_p.h>
56 #include <QtQml/qjsvalue.h>
57 #include <QtCore/qvarlengtharray.h>
58 #include <QtCore/qtimer.h>
59 #include <QtCore/qatomic.h>
61 Q_DECLARE_METATYPE(QJSValue);
62 Q_DECLARE_METATYPE(QQmlV8Handle);
67 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
68 // The code in this file does not violate strict aliasing, but GCC thinks it does
69 // so turn off the warnings for us to have a clean build
70 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
74 #define QOBJECT_TOSTRING_INDEX -2
75 #define QOBJECT_DESTROY_INDEX -3
77 // XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work
78 // correctly in a worker thread
80 class QV8QObjectResource : public QV8ObjectResource
82 V8_RESOURCE_TYPE(QObjectType);
85 QV8QObjectResource(QV8Engine *engine, QObject *object);
87 QQmlGuard<QObject> object;
90 class QV8QObjectInstance : public QQmlGuard<QObject>
93 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
94 : QQmlGuard<QObject>(o), wrapper(w)
100 qPersistentDispose(v8object);
103 virtual void objectDestroyed(QObject *o)
106 wrapper->m_taintedObjects.remove(o);
110 v8::Persistent<v8::Object> v8object;
111 QV8QObjectWrapper *wrapper;
114 class QV8SignalHandlerResource : public QV8ObjectResource
116 V8_RESOURCE_TYPE(SignalHandlerType)
118 QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index);
120 QQmlGuard<QObject> object;
126 template<typename A, typename B, typename C, typename D, typename E>
128 template<typename Z, typename X>
130 static const size_t Size = sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X);
133 static const size_t Size = SMax<A, SMax<B, SMax<C, SMax<D, E> > > >::Size;
136 struct CallArgument {
137 inline CallArgument();
138 inline ~CallArgument();
139 inline void *dataPtr();
141 inline void initAsType(int type);
142 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
143 inline v8::Handle<v8::Value> toValue(QV8Engine *);
146 CallArgument(const CallArgument &);
148 inline void cleanup();
157 char allocData[MaxSizeOf5<QVariant,
161 QQmlV8Handle>::Size];
162 qint64 q_for_alignment;
165 // Pointers to allocData
168 QVariant *qvariantPtr;
169 QList<QObject *> *qlistPtr;
170 QJSValue *qjsValuePtr;
171 QQmlV8Handle *handlePtr;
178 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
179 : QV8ObjectResource(engine), object(object)
183 QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index)
184 : QV8ObjectResource(engine), object(object), index(index)
188 static QAtomicInt objectIdCounter(1);
190 QV8QObjectWrapper::QV8QObjectWrapper()
191 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
195 QV8QObjectWrapper::~QV8QObjectWrapper()
197 for (TaintedHash::Iterator iter = m_taintedObjects.begin();
198 iter != m_taintedObjects.end();
200 (*iter)->wrapper = 0;
202 m_taintedObjects.clear();
205 void QV8QObjectWrapper::destroy()
207 qDeleteAll(m_connections);
208 m_connections.clear();
210 qPersistentDispose(m_hiddenObject);
211 qPersistentDispose(m_destroySymbol);
212 qPersistentDispose(m_toStringSymbol);
213 qPersistentDispose(m_signalHandlerConstructor);
214 qPersistentDispose(m_methodConstructor);
215 qPersistentDispose(m_constructor);
218 struct ReadAccessor {
219 static inline void Indirect(QObject *object, const QQmlPropertyData &property,
220 void *output, QQmlNotifier **n)
225 void *args[] = { output, 0 };
226 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
229 static inline void Direct(QObject *object, const QQmlPropertyData &property,
230 void *output, QQmlNotifier **n)
235 void *args[] = { output, 0 };
236 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
239 static inline void Accessor(QObject *object, const QQmlPropertyData &property,
240 void *output, QQmlNotifier **n)
242 Q_ASSERT(property.accessors);
244 property.accessors->read(object, property.accessorData, output);
245 if (n) property.accessors->notifier(object, property.accessorData, n);
249 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, int v)
250 { return v8::Integer::New(v); }
251 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, uint v)
252 { return v8::Integer::NewFromUnsigned(v); }
253 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, bool v)
254 { return v8::Boolean::New(v); }
255 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, const QString &v)
256 { return e->toString(v); }
257 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, float v)
258 { return v8::Number::New(v); }
259 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, double v)
260 { return v8::Number::New(v); }
261 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, QObject *v)
262 { return e->newQObject(v); }
264 template<typename T, void (*ReadFunction)(QObject *, const QQmlPropertyData &,
265 void *, QQmlNotifier **)>
266 static v8::Handle<v8::Value> GenericValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info)
268 v8::Handle<v8::Object> This = info.This();
269 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This);
271 QObject *object = resource->object;
272 if (!object) return v8::Undefined();
274 QQmlPropertyData *property =
275 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
277 QQmlEngine *engine = resource->engine->engine();
278 QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0;
282 if (ep && ep->propertyCapture) {
283 if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) {
284 QQmlNotifier *notifier = 0;
285 ReadFunction(object, *property, &value, ¬ifier);
286 if (notifier) ep->captureProperty(notifier);
287 } else if (!property->isConstant()) {
288 ep->captureProperty(object, property->coreIndex, property->notifyIndex);
289 ReadFunction(object, *property, &value, 0);
291 ReadFunction(object, *property, &value, 0);
294 ReadFunction(object, *property, &value, 0);
297 return valueToHandle(resource->engine, value);
300 #define FAST_GETTER_FUNCTION(property, cpptype) \
301 (property->hasAccessors()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Accessor>):(property->isDirect()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Direct>):((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Indirect>)))
303 static quint32 toStringHash = -1;
304 static quint32 destroyHash = -1;
306 void QV8QObjectWrapper::init(QV8Engine *engine)
310 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
311 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
312 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
314 m_toStringString = QHashedV8String(m_toStringSymbol);
315 m_destroyString = QHashedV8String(m_destroySymbol);
317 toStringHash = m_toStringString.hash();
318 destroyHash = m_destroyString.hash();
321 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
322 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
323 ft->InstanceTemplate()->SetHasExternalResource(true);
324 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
327 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
328 #define CREATE_FUNCTION_SOURCE \
329 "(function(method) { "\
330 "return (function(object, data, qmlglobal) { "\
331 "return (function() { "\
332 "return method(object, data, qmlglobal, arguments.length, arguments); "\
336 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION_SOURCE), &origin, 0,
337 v8::Handle<v8::String>(), v8::Script::NativeMode);
338 #undef CREATE_FUNCTION_SOURCE
339 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
340 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
341 v8::Handle<v8::Value> args[] = { invokeFn };
342 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
343 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
346 v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine);
347 v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine);
350 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
351 ft->InstanceTemplate()->SetHasExternalResource(true);
352 ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum);
353 ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
354 m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
358 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
359 prototype->Set(v8::String::New("connect"), connect, v8::DontEnum);
360 prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
364 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
366 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
369 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
371 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
372 return r?r->object:0;
375 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
376 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
378 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
379 return static_cast<QV8QObjectResource *>(r)->object;
382 // Load value properties
383 template<void (*ReadFunction)(QObject *, const QQmlPropertyData &,
384 void *, QQmlNotifier **)>
385 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
386 const QQmlPropertyData &property,
387 QQmlNotifier **notifier)
389 Q_ASSERT(!property.isFunction());
391 if (property.isQObject()) {
393 ReadFunction(object, property, &rv, notifier);
394 return engine->newQObject(rv);
395 } else if (property.isQList()) {
396 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
397 } else if (property.propType == QMetaType::QReal) {
399 ReadFunction(object, property, &v, notifier);
400 return valueToHandle(engine, v);
401 } else if (property.propType == QMetaType::Int || property.isEnum()) {
403 ReadFunction(object, property, &v, notifier);
404 return valueToHandle(engine, v);
405 } else if (property.propType == QMetaType::Bool) {
407 ReadFunction(object, property, &v, notifier);
408 return valueToHandle(engine, v);
409 } else if (property.propType == QMetaType::QString) {
411 ReadFunction(object, property, &v, notifier);
412 return valueToHandle(engine, v);
413 } else if (property.propType == QMetaType::UInt) {
415 ReadFunction(object, property, &v, notifier);
416 return valueToHandle(engine, v);
417 } else if (property.propType == QMetaType::Float) {
419 ReadFunction(object, property, &v, notifier);
420 return valueToHandle(engine, v);
421 } else if (property.propType == QMetaType::Double) {
423 ReadFunction(object, property, &v, notifier);
424 return valueToHandle(engine, v);
425 } else if (property.isV8Handle()) {
427 ReadFunction(object, property, &handle, notifier);
428 return handle.toHandle();
429 } else if (property.isQVariant()) {
431 ReadFunction(object, property, &v, notifier);
432 return engine->fromVariant(v);
433 } else if (QQmlValueTypeFactory::isValueType((uint)property.propType)
434 && engine->engine()) {
435 Q_ASSERT(notifier == 0);
437 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
438 QQmlValueType *valueType = ep->valueTypes[property.propType];
440 return engine->newValueType(object, property.coreIndex, valueType);
442 Q_ASSERT(notifier == 0);
444 // see if it's a sequence type
445 bool succeeded = false;
446 v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex,
452 if (property.propType == QVariant::Invalid) {
453 QMetaProperty p = object->metaObject()->property(property.coreIndex);
454 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
455 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
456 return v8::Undefined();
458 QVariant v(property.propType, (void *)0);
459 ReadFunction(object, property, v.data(), notifier);
460 return engine->fromVariant(v);
464 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
465 v8::Handle<v8::Value> *objectHandle,
466 const QHashedV8String &property,
467 QV8QObjectWrapper::RevisionMode revisionMode)
469 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
470 // will be a faster way of creating QObject method objects.
471 struct MethodClosure {
472 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
473 v8::Handle<v8::Value> *objectHandle,
475 v8::Handle<v8::Value> argv[] = {
476 objectHandle?*objectHandle:engine->newQObject(object),
477 v8::Integer::New(index)
479 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
481 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
482 v8::Handle<v8::Value> *objectHandle,
484 v8::Handle<v8::Value> argv[] = {
485 objectHandle?*objectHandle:engine->newQObject(object),
486 v8::Integer::New(index),
487 v8::Context::GetCallingQmlGlobal()
489 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
494 // Comparing the hash first actually makes a measurable difference here, at least on x86
495 quint32 hash = property.hash();
496 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
497 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
498 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
499 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
503 QQmlPropertyData local;
504 QQmlPropertyData *result = 0;
506 QQmlData *ddata = QQmlData::get(object, false);
507 if (ddata && ddata->propertyCache)
508 result = ddata->propertyCache->property(property);
510 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
514 return v8::Handle<v8::Value>();
516 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
517 QQmlData *ddata = QQmlData::get(object);
518 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
519 return v8::Handle<v8::Value>();
522 if (result->isFunction() && !result->isVMEProperty()) {
523 if (result->isVMEFunction()) {
524 return ((QQmlVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
525 } else if (result->isV8Function()) {
526 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
527 } else if (result->isSignalHandler()) {
528 v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
529 QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
530 handler->SetExternalResource(r);
533 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
537 QQmlEnginePrivate *ep =
538 engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
540 if (result->hasAccessors()) {
542 QQmlNotifier **nptr = 0;
544 if (ep && ep->propertyCapture && result->accessors->notifier)
547 v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
549 if (result->accessors->notifier) {
550 if (n) ep->captureProperty(n);
552 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
558 if (ep && !result->isConstant()) {
560 if (result->coreIndex == 0)
561 ep->captureProperty(QQmlData::get(object, true)->objectNameNotifier());
563 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
566 if (result->isVMEProperty()) {
567 typedef QQmlVMEMetaObject VMEMO;
568 VMEMO *vmemo = const_cast<VMEMO *>(static_cast<const VMEMO *>(object->metaObject()));
569 return vmemo->vmeProperty(result->coreIndex);
570 } else if (result->isDirect()) {
571 return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
573 return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
577 // Setter for writable properties. Shared between the interceptor and fast property accessor
578 static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
579 v8::Handle<v8::Value> value)
581 QQmlBinding *newBinding = 0;
582 if (value->IsFunction()) {
583 if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
584 if (!property->isVMEProperty()) {
585 // XXX TODO: uncomment the following lines
586 // assigning a JS function to a non-var-property is not allowed.
587 //QString error = QLatin1String("Cannot assign JavaScript function to ") +
588 // QLatin1String(QMetaType::typeName(property->propType));
589 //v8::ThrowException(v8::Exception::Error(engine->toString(error)));
591 // XXX TODO: remove the following transition behaviour
592 // Temporarily allow assignment of functions to non-var properties
593 // to mean binding assignment (as per old behaviour).
594 QQmlContextData *context = engine->callingContext();
595 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
597 v8::Local<v8::StackTrace> trace =
598 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
599 v8::StackTrace::kScriptName));
600 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
601 int lineNumber = frame->GetLineNumber();
602 int columNumber = frame->GetColumn();
603 QString url = engine->toString(frame->GetScriptName());
605 newBinding = new QQmlBinding(&function, object, context);
606 newBinding->setSourceLocation(url, lineNumber, columNumber);
607 newBinding->setTarget(object, *property, context);
608 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
609 QQmlBinding::RequiresThisObject);
610 qWarning("WARNING: function assignment is DEPRECATED and will be removed! Wrap RHS in Qt.binding(): %s:%d", qPrintable(engine->toString(frame->GetScriptName())), frame->GetLineNumber());
613 // binding assignment.
614 QQmlContextData *context = engine->callingContext();
615 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
617 v8::Local<v8::StackTrace> trace =
618 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
619 v8::StackTrace::kScriptName));
620 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
621 int lineNumber = frame->GetLineNumber();
622 int columNumber = frame->GetColumn();
623 QString url = engine->toString(frame->GetScriptName());
625 newBinding = new QQmlBinding(&function, object, context);
626 newBinding->setSourceLocation(url, lineNumber, columNumber);
627 newBinding->setTarget(object, *property, context);
628 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
629 QQmlBinding::RequiresThisObject);
633 QQmlAbstractBinding *oldBinding =
634 QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
636 oldBinding->destroy();
638 if (!newBinding && property->isVMEProperty()) {
639 // allow assignment of "special" values (null, undefined, function) to var properties
640 static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
644 #define PROPERTY_STORE(cpptype, value) \
648 void *argv[] = { &o, 0, &status, &flags }; \
649 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
652 if (value->IsNull() && property->isQObject()) {
653 PROPERTY_STORE(QObject*, 0);
654 } else if (value->IsUndefined() && property->isResettable()) {
656 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
657 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
658 PROPERTY_STORE(QVariant, QVariant());
659 } else if (value->IsUndefined()) {
660 QString error = QLatin1String("Cannot assign [undefined] to ") +
661 QLatin1String(QMetaType::typeName(property->propType));
662 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
663 } else if (value->IsFunction()) {
664 // this is handled by the binding creation above
665 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
666 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
667 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
668 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
669 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
670 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
671 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
672 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
673 } else if (property->propType == QMetaType::QString && value->IsString()) {
674 PROPERTY_STORE(QString, engine->toString(value->ToString()));
675 } else if (property->isVMEProperty()) {
676 static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
679 if (property->isQList())
680 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
682 v = engine->toVariant(value, property->propType);
684 QQmlContextData *context = engine->callingContext();
685 if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
686 const char *valueType = 0;
687 if (v.userType() == QVariant::Invalid) valueType = "null";
688 else valueType = QMetaType::typeName(v.userType());
690 QString error = QLatin1String("Cannot assign ") +
691 QLatin1String(valueType) +
692 QLatin1String(" to ") +
693 QLatin1String(QMetaType::typeName(property->propType));
694 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
699 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
700 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
702 if (engine->qobjectWrapper()->m_toStringString == property ||
703 engine->qobjectWrapper()->m_destroyString == property)
706 QQmlPropertyData local;
707 QQmlPropertyData *result = 0;
708 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
713 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
714 QQmlData *ddata = QQmlData::get(object);
715 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
719 if (!result->isWritable() && !result->isQList()) {
720 QString error = QLatin1String("Cannot assign to read-only property \"") +
721 engine->toString(property.string()) + QLatin1Char('\"');
722 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
726 StoreProperty(engine, object, result, value);
731 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
732 const v8::AccessorInfo &info)
734 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
736 if (resource->object.isNull())
737 return v8::Handle<v8::Value>();
739 QObject *object = resource->object;
741 QHashedV8String propertystring(property);
743 QV8Engine *v8engine = resource->engine;
744 v8::Handle<v8::Value> This = info.This();
745 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
746 QV8QObjectWrapper::IgnoreRevision);
747 if (!result.IsEmpty())
750 if (QV8Engine::startsWithUpper(property)) {
751 // Check for attached properties
752 QQmlContextData *context = v8engine->callingContext();
754 if (context && context->imports) {
755 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
758 if (r.scriptIndex != -1) {
759 return v8::Undefined();
761 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
762 } else if (r.importNamespace) {
763 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
764 QV8TypeWrapper::ExcludeEnums);
766 Q_ASSERT(!"Unreachable");
771 return v8::Handle<v8::Value>();
774 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
775 v8::Local<v8::Value> value,
776 const v8::AccessorInfo &info)
778 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
780 if (resource->object.isNull())
783 QObject *object = resource->object;
785 QHashedV8String propertystring(property);
787 QV8Engine *v8engine = resource->engine;
788 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
791 QString error = QLatin1String("Cannot assign to non-existent property \"") +
792 v8engine->toString(property) + QLatin1Char('\"');
793 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
800 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
801 const v8::AccessorInfo &info)
803 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
805 if (resource->object.isNull())
806 return v8::Handle<v8::Integer>();
808 QV8Engine *engine = resource->engine;
809 QObject *object = resource->object;
811 QHashedV8String propertystring(property);
813 QQmlPropertyData local;
814 QQmlPropertyData *result = 0;
815 result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local);
818 return v8::Handle<v8::Integer>();
819 else if (!result->isWritable() && !result->isQList())
820 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
822 return v8::Integer::New(v8::DontDelete);
825 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
827 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
829 if (resource->object.isNull())
830 return v8::Array::New();
832 QObject *object = resource->object;
836 QQmlEnginePrivate *ep = resource->engine->engine()
837 ? QQmlEnginePrivate::get(resource->engine->engine())
840 QQmlPropertyCache *cache = 0;
841 QQmlData *ddata = QQmlData::get(object);
843 cache = ddata->propertyCache;
846 cache = ep ? ep->cache(object) : 0;
848 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
850 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
851 const QMetaObject *mo = object->metaObject();
852 int pc = mo->propertyCount();
853 int po = mo->propertyOffset();
854 for (int i=po; i<pc; ++i)
855 result << QString::fromUtf8(mo->property(i).name());
858 result = cache->propertyNames();
861 v8::Local<v8::Array> rv = v8::Array::New(result.count());
863 for (int ii = 0; ii < result.count(); ++ii)
864 rv->Set(ii, resource->engine->toString(result.at(ii)));
869 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
870 const v8::AccessorInfo& info)
872 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
874 if (resource->object.isNull())
877 QObject *object = resource->object;
879 QQmlPropertyData *property =
880 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
882 int index = property->coreIndex;
884 QQmlData *ddata = QQmlData::get(object, false);
886 Q_ASSERT(ddata->propertyCache);
888 QQmlPropertyData *pdata = ddata->propertyCache->property(index);
891 Q_ASSERT(pdata->isWritable() || pdata->isQList());
893 StoreProperty(resource->engine, object, pdata, value);
896 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
897 const v8::AccessorInfo& info)
899 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
901 if (resource->object.isNull())
904 QV8Engine *v8engine = resource->engine;
906 QString error = QLatin1String("Cannot assign to read-only property \"") +
907 v8engine->toString(property) + QLatin1Char('\"');
908 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
911 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
913 Q_ASSERT(handle->IsObject());
915 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
919 QObject *object = resource->object;
921 QQmlData *ddata = QQmlData::get(object, false);
923 ddata->v8object.Clear();
924 if (!object->parent() && !ddata->indestructible)
925 object->deleteLater();
929 qPersistentDispose(handle);
932 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
934 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
935 instance->v8object.Clear();
936 qPersistentDispose(handle);
939 v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
942 Q_ASSERT(this->engine);
944 Q_ASSERT(QQmlData::get(object, false));
945 Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
948 if (constructor.IsEmpty()) {
949 v8::Local<v8::FunctionTemplate> ft;
951 QString toString = QLatin1String("toString");
952 QString destroy = QLatin1String("destroy");
954 // As we use hash linking, it is possible that iterating over the values can give duplicates.
955 // To combat this, we must unique'ify our properties.
956 StringCache uniqueHash;
957 if (stringCache.isLinked())
958 uniqueHash.reserve(stringCache.count());
960 // XXX TODO: Enables fast property accessors. These more than double the property access
961 // performance, but the cost of setting up this structure hasn't been measured so
962 // its not guarenteed that this is a win overall. We need to try and measure the cost.
963 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
964 if (stringCache.isLinked()) {
965 if (uniqueHash.contains(iter))
967 uniqueHash.insert(iter);
970 QQmlPropertyData *property = *iter;
971 if (property->notFullyResolved()) resolve(property);
973 if (property->isFunction())
976 v8::AccessorGetter fastgetter = 0;
977 v8::AccessorSetter fastsetter = FastValueSetter;
978 if (!property->isWritable())
979 fastsetter = FastValueSetterReadOnly;
981 if (property->isQObject())
982 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
983 else if (property->propType == QMetaType::Int || property->isEnum())
984 fastgetter = FAST_GETTER_FUNCTION(property, int);
985 else if (property->propType == QMetaType::Bool)
986 fastgetter = FAST_GETTER_FUNCTION(property, bool);
987 else if (property->propType == QMetaType::QString)
988 fastgetter = FAST_GETTER_FUNCTION(property, QString);
989 else if (property->propType == QMetaType::UInt)
990 fastgetter = FAST_GETTER_FUNCTION(property, uint);
991 else if (property->propType == QMetaType::Float)
992 fastgetter = FAST_GETTER_FUNCTION(property, float);
993 else if (property->propType == QMetaType::Double)
994 fastgetter = FAST_GETTER_FUNCTION(property, double);
997 QString name = iter.key();
998 if (name == toString || name == destroy)
1002 ft = v8::FunctionTemplate::New();
1003 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1004 QV8QObjectWrapper::Setter,
1005 QV8QObjectWrapper::Query,
1007 QV8QObjectWrapper::Enumerator);
1008 ft->InstanceTemplate()->SetHasExternalResource(true);
1011 // We wrap the raw QQmlPropertyData pointer here. This is safe as the
1012 // pointer will remain valid at least as long as the lifetime of any QObject's of
1013 // this type and the property accessor checks if the object is 0 (deleted) before
1014 // dereferencing the pointer.
1015 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
1016 v8::External::Wrap(property));
1021 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
1023 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1024 QV8QObjectWrapper::Setter,
1025 QV8QObjectWrapper::Query,
1027 QV8QObjectWrapper::Enumerator);
1028 ft->InstanceTemplate()->SetHasExternalResource(true);
1029 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
1032 QQmlCleanup::addToEngine(this->engine);
1035 v8::Local<v8::Object> result = constructor->NewInstance();
1036 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1037 result->SetExternalResource(r);
1041 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
1043 v8::Local<v8::Object> rv;
1045 if (!ddata->propertyCache && engine->engine()) {
1046 ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
1047 if (ddata->propertyCache) ddata->propertyCache->addref();
1050 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1051 rv = ddata->propertyCache->newQObject(object, engine);
1053 // XXX NewInstance() should be optimized
1054 rv = m_constructor->NewInstance();
1055 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1056 rv->SetExternalResource(r);
1063 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1064 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
1066 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
1067 persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that
1068 "owns" this handle by setting the QQmlData::v8objectid to the id of this
1070 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create
1071 an entry in the m_taintedObject hash where we store the handle and mark the object as
1072 "tainted" in the QQmlData::hasTaintedV8Object flag.
1073 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1074 in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has
1075 released the handle.
1077 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1082 if (QObjectPrivate::get(object)->wasDeleted)
1083 return v8::Undefined();
1085 QQmlData *ddata = QQmlData::get(object, true);
1088 return v8::Undefined();
1090 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1091 // We own the v8object
1092 return v8::Local<v8::Object>::New(ddata->v8object);
1093 } else if (ddata->v8object.IsEmpty() &&
1094 (ddata->v8objectid == m_id || // We own the QObject
1095 ddata->v8objectid == 0 || // No one owns the QObject
1096 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1098 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1099 ddata->v8object = qPersistentNew<v8::Object>(rv);
1100 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1101 ddata->v8objectid = m_id;
1105 // If this object is tainted, we have to check to see if it is in our
1106 // tainted object list
1107 TaintedHash::Iterator iter =
1108 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1109 bool found = iter != m_taintedObjects.end();
1111 // If our tainted handle doesn't exist or has been collected, and there isn't
1112 // a handle in the ddata, we can assume ownership of the ddata->v8object
1113 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1114 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1115 ddata->v8object = qPersistentNew<v8::Object>(rv);
1116 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1117 ddata->v8objectid = m_id;
1121 m_taintedObjects.erase(iter);
1125 } else if (!found) {
1126 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1127 iter = m_taintedObjects.insert(object, instance);
1128 ddata->hasTaintedV8Object = true;
1131 if ((*iter)->v8object.IsEmpty()) {
1132 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1133 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1134 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1137 return v8::Local<v8::Object>::New((*iter)->v8object);
1141 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1143 if (object->IsFunction())
1144 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1146 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1147 return qMakePair(resource->object.data(), resource->index);
1149 return qMakePair((QObject *)0, -1);
1152 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1154 v8::ScriptOrigin origin = function->GetScriptOrigin();
1155 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1157 // This is one of our special QObject method wrappers
1158 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1159 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1161 if (data->IsArray()) {
1162 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1163 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1166 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1169 return qMakePair((QObject *)0, -1);
1172 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1175 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1176 ~QV8QObjectConnectionList();
1180 : needsDestroy(false) {}
1181 Connection(const Connection &other)
1182 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1183 Connection &operator=(const Connection &other) {
1184 thisObject = other.thisObject;
1185 function = other.function;
1186 needsDestroy = other.needsDestroy;
1190 v8::Persistent<v8::Object> thisObject;
1191 v8::Persistent<v8::Function> function;
1194 qPersistentDispose(thisObject);
1195 qPersistentDispose(function);
1201 struct ConnectionList : public QList<Connection> {
1202 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1203 int connectionsInUse;
1204 bool connectionsNeedClean;
1209 typedef QHash<int, ConnectionList> SlotHash;
1214 virtual void objectDestroyed(QObject *);
1215 virtual int qt_metacall(QMetaObject::Call, int, void **);
1218 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1219 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1223 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1225 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1226 QList<Connection> &connections = *iter;
1227 for (int ii = 0; ii < connections.count(); ++ii) {
1228 qPersistentDispose(connections[ii].thisObject);
1229 qPersistentDispose(connections[ii].function);
1235 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1237 engine->qobjectWrapper()->m_connections.remove(object);
1240 needsDestroy = true;
1245 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1247 if (method == QMetaObject::InvokeMetaMethod) {
1248 SlotHash::Iterator iter = slotHash.find(index);
1249 if (iter == slotHash.end())
1251 ConnectionList &connectionList = *iter;
1252 if (connectionList.isEmpty())
1257 connectionList.connectionsInUse++;
1259 QList<Connection> connections = connectionList;
1261 QVarLengthArray<int, 9> dummy;
1262 int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1264 v8::HandleScope handle_scope;
1265 v8::Context::Scope scope(engine->context());
1267 int argCount = argsTypes?argsTypes[0]:0;
1268 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1270 for (int ii = 0; ii < argCount; ++ii) {
1271 int type = argsTypes[ii + 1];
1272 if (type == qMetaTypeId<QVariant>()) {
1273 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1275 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1279 for (int ii = 0; ii < connections.count(); ++ii) {
1280 Connection &connection = connections[ii];
1281 if (connection.needsDestroy)
1284 v8::TryCatch try_catch;
1285 if (connection.thisObject.IsEmpty()) {
1286 connection.function->Call(engine->global(), argCount, args.data());
1288 connection.function->Call(connection.thisObject, argCount, args.data());
1291 if (try_catch.HasCaught()) {
1293 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1294 v8::Local<v8::Message> message = try_catch.Message();
1295 if (!message.IsEmpty())
1296 QQmlExpressionPrivate::exceptionToError(message, error);
1297 QQmlEnginePrivate::get(engine->engine())->warning(error);
1301 connectionList.connectionsInUse--;
1302 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1303 for (QList<Connection>::Iterator iter = connectionList.begin();
1304 iter != connectionList.end(); ) {
1305 if (iter->needsDestroy) {
1307 iter = connectionList.erase(iter);
1315 if (inUse == 0 && needsDestroy)
1322 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1324 if (args.Length() == 0)
1325 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1327 QV8Engine *engine = V8ENGINE();
1329 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1330 QObject *signalObject = signalInfo.first;
1331 int signalIndex = signalInfo.second;
1333 if (signalIndex == -1)
1334 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1337 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1339 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1340 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1342 v8::Local<v8::Value> functionValue;
1343 v8::Local<v8::Value> functionThisValue;
1345 if (args.Length() == 1) {
1346 functionValue = args[0];
1348 functionThisValue = args[0];
1349 functionValue = args[1];
1352 if (!functionValue->IsFunction())
1353 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1355 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1356 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1358 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1359 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1360 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1361 if (iter == connections.end())
1362 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1364 QV8QObjectConnectionList *connectionList = *iter;
1365 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1366 if (slotIter == connectionList->slotHash.end()) {
1367 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1368 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1371 QV8QObjectConnectionList::Connection connection;
1372 if (!functionThisValue.IsEmpty())
1373 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1374 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1376 slotIter->append(connection);
1378 return v8::Undefined();
1381 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1383 if (args.Length() == 0)
1384 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1386 QV8Engine *engine = V8ENGINE();
1388 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1389 QObject *signalObject = signalInfo.first;
1390 int signalIndex = signalInfo.second;
1392 if (signalIndex == -1)
1393 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1396 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1398 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1399 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1401 v8::Local<v8::Value> functionValue;
1402 v8::Local<v8::Value> functionThisValue;
1404 if (args.Length() == 1) {
1405 functionValue = args[0];
1407 functionThisValue = args[0];
1408 functionValue = args[1];
1411 if (!functionValue->IsFunction())
1412 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1414 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1415 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1417 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1418 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1419 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1420 if (iter == connectionsList.end())
1421 return v8::Undefined(); // Nothing to disconnect from
1423 QV8QObjectConnectionList *connectionList = *iter;
1424 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1425 if (slotIter == connectionList->slotHash.end())
1426 return v8::Undefined(); // Nothing to disconnect from
1428 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1430 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1431 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1433 if (functionData.second != -1) {
1434 // This is a QObject function wrapper
1435 for (int ii = 0; ii < connections.count(); ++ii) {
1436 QV8QObjectConnectionList::Connection &connection = connections[ii];
1438 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1439 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1441 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1442 if (connectedFunctionData == functionData) {
1444 if (connections.connectionsInUse) {
1445 connection.needsDestroy = true;
1446 connections.connectionsNeedClean = true;
1448 connection.dispose();
1449 connections.removeAt(ii);
1451 return v8::Undefined();
1457 // This is a normal JS function
1458 for (int ii = 0; ii < connections.count(); ++ii) {
1459 QV8QObjectConnectionList::Connection &connection = connections[ii];
1460 if (connection.function->StrictEquals(function) &&
1461 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1462 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1464 if (connections.connectionsInUse) {
1465 connection.needsDestroy = true;
1466 connections.connectionsNeedClean = true;
1468 connection.dispose();
1469 connections.removeAt(ii);
1471 return v8::Undefined();
1476 return v8::Undefined();
1480 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1482 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1484 Only searches for real properties of \a object (including methods), not attached properties etc.
1488 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1490 Set the \a property of \a object to \a value.
1492 Returns true if the property was "set" - even if this results in an exception being thrown -
1493 and false if the object has no such property.
1495 Only searches for real properties of \a object (including methods), not attached properties etc.
1501 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1502 int Length() const { return _length; }
1503 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1507 v8::Handle<v8::Object> *_args;
1511 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1512 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1516 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1517 args[0].initAsType(returnType);
1519 for (int ii = 0; ii < argCount; ++ii)
1520 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1522 QVarLengthArray<void *, 9> argData(args.count());
1523 for (int ii = 0; ii < args.count(); ++ii)
1524 argData[ii] = args[ii].dataPtr();
1526 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1528 return args[0].toValue(engine);
1530 } else if (returnType != 0) {
1533 arg.initAsType(returnType);
1535 void *args[] = { arg.dataPtr() };
1537 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1539 return arg.toValue(engine);
1543 void *args[] = { 0 };
1544 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1545 return v8::Undefined();
1551 Returns the match score for converting \a actual to be of type \a conversionType. A
1552 zero score means "perfect match" whereas a higher score is worse.
1554 The conversion table is copied out of the QtScript callQtMethod() function.
1556 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1558 if (actual->IsNumber()) {
1559 switch (conversionType) {
1560 case QMetaType::Double:
1562 case QMetaType::Float:
1564 case QMetaType::LongLong:
1565 case QMetaType::ULongLong:
1567 case QMetaType::Long:
1568 case QMetaType::ULong:
1570 case QMetaType::Int:
1571 case QMetaType::UInt:
1573 case QMetaType::Short:
1574 case QMetaType::UShort:
1577 case QMetaType::Char:
1578 case QMetaType::UChar:
1583 } else if (actual->IsString()) {
1584 switch (conversionType) {
1585 case QMetaType::QString:
1590 } else if (actual->IsBoolean()) {
1591 switch (conversionType) {
1592 case QMetaType::Bool:
1597 } else if (actual->IsDate()) {
1598 switch (conversionType) {
1599 case QMetaType::QDateTime:
1601 case QMetaType::QDate:
1603 case QMetaType::QTime:
1608 } else if (actual->IsRegExp()) {
1609 switch (conversionType) {
1610 case QMetaType::QRegExp:
1615 } else if (actual->IsArray()) {
1616 switch (conversionType) {
1617 case QMetaType::QStringList:
1618 case QMetaType::QVariantList:
1623 } else if (actual->IsNull()) {
1624 switch (conversionType) {
1625 case QMetaType::VoidStar:
1626 case QMetaType::QObjectStar:
1629 const char *typeName = QMetaType::typeName(conversionType);
1630 if (typeName && typeName[strlen(typeName) - 1] == '*')
1636 } else if (actual->IsObject()) {
1637 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1639 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1640 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1641 switch (conversionType) {
1642 case QMetaType::QObjectStar:
1647 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1648 if (conversionType == qMetaTypeId<QVariant>())
1650 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1663 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1669 int classInfoCount, classInfoData;
1670 int methodCount, methodData;
1673 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1676 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1678 QByteArray sig = m.signature();
1679 int paren = sig.indexOf('(');
1683 return sig.left(paren);
1687 Returns the next related method, if one, or 0.
1689 static const QQmlPropertyData * RelatedMethod(QObject *object,
1690 const QQmlPropertyData *current,
1691 QQmlPropertyData &dummy)
1693 QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1694 if (!current->isOverload())
1697 Q_ASSERT(!current->overrideIndexIsProperty);
1700 return cache->method(current->overrideIndex);
1702 const QMetaObject *mo = object->metaObject();
1703 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1705 while (methodOffset > current->overrideIndex) {
1706 mo = mo->superClass();
1707 methodOffset -= QMetaObject_methods(mo);
1710 QMetaMethod method = mo->method(current->overrideIndex);
1713 // Look for overloaded methods
1714 QByteArray methodName = QMetaMethod_name(method);
1715 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1716 if (methodName == QMetaMethod_name(mo->method(ii))) {
1717 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1718 dummy.overrideIndexIsProperty = 0;
1719 dummy.overrideIndex = ii;
1728 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1729 QV8Engine *engine, CallArgs &callArgs)
1731 if (data.hasArguments()) {
1734 QVarLengthArray<int, 9> dummy;
1735 QByteArray unknownTypeError;
1737 args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1741 QString typeName = QString::fromLatin1(unknownTypeError);
1742 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1743 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1744 return v8::Handle<v8::Value>();
1747 if (args[0] > callArgs.Length()) {
1748 QString error = QLatin1String("Insufficient arguments");
1749 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1750 return v8::Handle<v8::Value>();
1753 return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1757 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1763 Resolve the overloaded method to call. The algorithm works conceptually like this:
1764 1. Resolve the set of overloads it is *possible* to call.
1765 Impossible overloads include those that have too many parameters or have parameters
1767 2. Filter the set of overloads to only contain those with the closest number of
1769 For example, if we are called with 3 parameters and there are 2 overloads that
1770 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1771 3. Find the best remaining overload based on its match score.
1772 If two or more overloads have the same match score, call the last one. The match
1773 score is constructed by adding the matchScore() result for each of the parameters.
1775 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1776 QV8Engine *engine, CallArgs &callArgs)
1778 int argumentCount = callArgs.Length();
1780 const QQmlPropertyData *best = 0;
1781 int bestParameterScore = INT_MAX;
1782 int bestMatchScore = INT_MAX;
1784 QQmlPropertyData dummy;
1785 const QQmlPropertyData *attempt = &data;
1788 QVarLengthArray<int, 9> dummy;
1789 int methodArgumentCount = 0;
1790 int *methodArgTypes = 0;
1791 if (attempt->hasArguments()) {
1792 typedef QQmlPropertyCache PC;
1793 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1794 if (!args) // Must be an unknown argument
1797 methodArgumentCount = args[0];
1798 methodArgTypes = args + 1;
1801 if (methodArgumentCount > argumentCount)
1802 continue; // We don't have sufficient arguments to call this method
1804 int methodParameterScore = argumentCount - methodArgumentCount;
1805 if (methodParameterScore > bestParameterScore)
1806 continue; // We already have a better option
1808 int methodMatchScore = 0;
1809 for (int ii = 0; ii < methodArgumentCount; ++ii)
1810 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1812 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1814 bestParameterScore = methodParameterScore;
1815 bestMatchScore = methodMatchScore;
1818 if (bestParameterScore == 0 && bestMatchScore == 0)
1819 break; // We can't get better than that
1821 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1824 return CallPrecise(object, *best, engine, callArgs);
1826 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1827 const QQmlPropertyData *candidate = &data;
1829 error += QLatin1String("\n ") +
1830 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1831 candidate = RelatedMethod(object, candidate, dummy);
1834 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1835 return v8::Handle<v8::Value>();
1839 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1843 QString objectName = object->objectName();
1845 result += QString::fromUtf8(object->metaObject()->className());
1846 result += QLatin1String("(0x");
1847 result += QString::number((quintptr)object,16);
1849 if (!objectName.isEmpty()) {
1850 result += QLatin1String(", \"");
1851 result += objectName;
1852 result += QLatin1Char('\"');
1855 result += QLatin1Char(')');
1857 result = QLatin1String("null");
1860 return engine->toString(result);
1863 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1865 QQmlData *ddata = QQmlData::get(object, false);
1866 if (!ddata || ddata->indestructible) {
1867 const char *error = "Invalid attempt to destroy() an indestructible object";
1868 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1869 return v8::Undefined();
1874 delay = args->Get(0)->Uint32Value();
1877 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1879 object->deleteLater();
1881 return v8::Undefined();
1884 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1886 // object, index, qmlglobal, argCount, args
1887 Q_ASSERT(args.Length() == 5);
1888 Q_ASSERT(args[0]->IsObject());
1890 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1893 return v8::Undefined();
1895 int argCount = args[3]->Int32Value();
1896 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1898 // Special hack to return info about this closure.
1899 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1900 v8::Local<v8::Array> data = v8::Array::New(2);
1901 data->Set(0, args[0]);
1902 data->Set(1, args[1]);
1906 QObject *object = resource->object;
1907 int index = args[1]->Int32Value();
1910 return v8::Undefined();
1913 // Builtin functions
1914 if (index == QOBJECT_TOSTRING_INDEX) {
1915 return ToString(resource->engine, object, argCount, arguments);
1916 } else if (index == QOBJECT_DESTROY_INDEX) {
1917 return Destroy(resource->engine, object, argCount, arguments);
1919 return v8::Undefined();
1923 QQmlPropertyData method;
1925 if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
1926 if (ddata->propertyCache) {
1927 QQmlPropertyData *d = ddata->propertyCache->method(index);
1929 return v8::Undefined();
1934 if (method.coreIndex == -1) {
1935 method.load(object->metaObject()->method(index));
1937 if (method.coreIndex == -1)
1938 return v8::Undefined();
1941 if (method.isV8Function()) {
1942 v8::Handle<v8::Value> rv;
1943 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1945 QQmlV8Function func(argCount, arguments, rv, qmlglobal,
1946 resource->engine->contextWrapper()->context(qmlglobal),
1948 QQmlV8Function *funcptr = &func;
1950 void *args[] = { 0, &funcptr };
1951 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1953 if (rv.IsEmpty()) return v8::Undefined();
1957 CallArgs callArgs(argCount, &arguments);
1958 if (!method.isOverload()) {
1959 return CallPrecise(object, method, resource->engine, callArgs);
1961 return CallOverloaded(object, method, resource->engine, callArgs);
1965 CallArgument::CallArgument()
1966 : type(QVariant::Invalid)
1970 CallArgument::~CallArgument()
1975 void CallArgument::cleanup()
1977 if (type == QMetaType::QString) {
1978 qstringPtr->~QString();
1979 } else if (type == -1 || type == QMetaType::QVariant) {
1980 qvariantPtr->~QVariant();
1981 } else if (type == qMetaTypeId<QJSValue>()) {
1982 qjsValuePtr->~QJSValue();
1983 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1984 qlistPtr->~QList<QObject *>();
1988 void *CallArgument::dataPtr()
1991 return qvariantPtr->data();
1993 return (void *)&allocData;
1996 void CallArgument::initAsType(int callType)
1998 if (type != 0) { cleanup(); type = 0; }
1999 if (callType == 0) return;
2001 if (callType == qMetaTypeId<QJSValue>()) {
2002 qjsValuePtr = new (&allocData) QJSValue();
2004 } else if (callType == QMetaType::Int ||
2005 callType == QMetaType::UInt ||
2006 callType == QMetaType::Bool ||
2007 callType == QMetaType::Double ||
2008 callType == QMetaType::Float) {
2010 } else if (callType == QMetaType::QObjectStar) {
2013 } else if (callType == QMetaType::QString) {
2014 qstringPtr = new (&allocData) QString();
2016 } else if (callType == QMetaType::QVariant) {
2018 qvariantPtr = new (&allocData) QVariant();
2019 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2021 qlistPtr = new (&allocData) QList<QObject *>();
2022 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2024 handlePtr = new (&allocData) QQmlV8Handle;
2027 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2031 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2033 if (type != 0) { cleanup(); type = 0; }
2035 if (callType == qMetaTypeId<QJSValue>()) {
2036 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2037 type = qMetaTypeId<QJSValue>();
2038 } else if (callType == QMetaType::Int) {
2039 intValue = quint32(value->Int32Value());
2041 } else if (callType == QMetaType::UInt) {
2042 intValue = quint32(value->Uint32Value());
2044 } else if (callType == QMetaType::Bool) {
2045 boolValue = value->BooleanValue();
2047 } else if (callType == QMetaType::Double) {
2048 doubleValue = double(value->NumberValue());
2050 } else if (callType == QMetaType::Float) {
2051 floatValue = float(value->NumberValue());
2053 } else if (callType == QMetaType::QString) {
2054 if (value->IsNull() || value->IsUndefined())
2055 qstringPtr = new (&allocData) QString();
2057 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2059 } else if (callType == QMetaType::QObjectStar) {
2060 qobjectPtr = engine->toQObject(value);
2062 } else if (callType == qMetaTypeId<QVariant>()) {
2063 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2065 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2066 qlistPtr = new (&allocData) QList<QObject *>();
2067 if (value->IsArray()) {
2068 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2069 uint32_t length = array->Length();
2070 for (uint32_t ii = 0; ii < length; ++ii)
2071 qlistPtr->append(engine->toQObject(array->Get(ii)));
2073 qlistPtr->append(engine->toQObject(value));
2076 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2077 handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2080 qvariantPtr = new (&allocData) QVariant();
2083 QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2084 QVariant v = engine->toVariant(value, -1);
2086 if (v.userType() == callType) {
2088 } else if (v.canConvert((QVariant::Type)callType)) {
2090 qvariantPtr->convert((QVariant::Type)callType);
2091 } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
2092 QObject *obj = ep->toQObject(v);
2095 const QMetaObject *objMo = obj->metaObject();
2096 while (objMo && objMo != mo) objMo = objMo->superClass();
2097 if (!objMo) obj = 0;
2100 *qvariantPtr = QVariant(callType, &obj);
2102 *qvariantPtr = QVariant(callType, (void *)0);
2107 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2109 if (type == qMetaTypeId<QJSValue>()) {
2110 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2111 } else if (type == QMetaType::Int) {
2112 return v8::Integer::New(int(intValue));
2113 } else if (type == QMetaType::UInt) {
2114 return v8::Integer::NewFromUnsigned(intValue);
2115 } else if (type == QMetaType::Bool) {
2116 return v8::Boolean::New(boolValue);
2117 } else if (type == QMetaType::Double) {
2118 return v8::Number::New(doubleValue);
2119 } else if (type == QMetaType::Float) {
2120 return v8::Number::New(floatValue);
2121 } else if (type == QMetaType::QString) {
2122 return engine->toString(*qstringPtr);
2123 } else if (type == QMetaType::QObjectStar) {
2124 QObject *object = qobjectPtr;
2126 QQmlData::get(object, true)->setImplicitDestructible();
2127 return engine->newQObject(object);
2128 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2129 // XXX Can this be made more by using Array as a prototype and implementing
2130 // directly against QList<QObject*>?
2131 QList<QObject *> &list = *qlistPtr;
2132 v8::Local<v8::Array> array = v8::Array::New(list.count());
2133 for (int ii = 0; ii < list.count(); ++ii)
2134 array->Set(ii, engine->newQObject(list.at(ii)));
2136 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2137 return handlePtr->toHandle();
2138 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2139 QVariant value = *qvariantPtr;
2140 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2141 if (QObject *object = engine->toQObject(rv))
2142 QQmlData::get(object, true)->setImplicitDestructible();
2145 return v8::Undefined();