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>
55 #include <private/qqmlglobal_p.h>
57 #include <QtQml/qjsvalue.h>
58 #include <QtCore/qjsonarray.h>
59 #include <QtCore/qjsonobject.h>
60 #include <QtCore/qjsonvalue.h>
61 #include <QtCore/qvarlengtharray.h>
62 #include <QtCore/qtimer.h>
63 #include <QtCore/qatomic.h>
65 Q_DECLARE_METATYPE(QJSValue);
66 Q_DECLARE_METATYPE(QQmlV8Handle);
70 #if defined(__GNUC__) && !defined(__INTEL_COMPILER)
71 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
72 // The code in this file does not violate strict aliasing, but GCC thinks it does
73 // so turn off the warnings for us to have a clean build
74 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
78 #define QOBJECT_TOSTRING_INDEX -2
79 #define QOBJECT_DESTROY_INDEX -3
81 // XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work
82 // correctly in a worker thread
84 class QV8QObjectInstance : public QQmlGuard<QObject>
87 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
88 : QQmlGuard<QObject>(o), wrapper(w)
94 qPersistentDispose(v8object);
97 virtual void objectDestroyed(QObject *o)
100 wrapper->m_taintedObjects.remove(o);
104 v8::Persistent<v8::Object> v8object;
105 QV8QObjectWrapper *wrapper;
108 class QV8SignalHandlerResource : public QV8ObjectResource
110 V8_RESOURCE_TYPE(SignalHandlerType)
112 QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index);
114 QQmlGuard<QObject> object;
120 template<typename A, typename B, typename C, typename D, typename E,
121 typename F, typename G, typename H>
123 template<typename Z, typename X>
125 char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
128 static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >);
131 struct CallArgument {
132 inline CallArgument();
133 inline ~CallArgument();
134 inline void *dataPtr();
136 inline void initAsType(int type);
137 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
138 inline v8::Handle<v8::Value> toValue(QV8Engine *);
141 CallArgument(const CallArgument &);
143 inline void cleanup();
152 char allocData[MaxSizeOf8<QVariant,
160 qint64 q_for_alignment;
163 // Pointers to allocData
166 QVariant *qvariantPtr;
167 QList<QObject *> *qlistPtr;
168 QJSValue *qjsValuePtr;
169 QQmlV8Handle *handlePtr;
170 QJsonArray *jsonArrayPtr;
171 QJsonObject *jsonObjectPtr;
172 QJsonValue *jsonValuePtr;
179 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
180 : QV8ObjectResource(engine), object(object)
184 QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index)
185 : QV8ObjectResource(engine), object(object), index(index)
189 static QAtomicInt objectIdCounter(1);
191 QV8QObjectWrapper::QV8QObjectWrapper()
192 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
196 QV8QObjectWrapper::~QV8QObjectWrapper()
198 for (TaintedHash::Iterator iter = m_taintedObjects.begin();
199 iter != m_taintedObjects.end();
201 (*iter)->wrapper = 0;
203 m_taintedObjects.clear();
206 void QV8QObjectWrapper::destroy()
208 qDeleteAll(m_connections);
209 m_connections.clear();
211 qPersistentDispose(m_hiddenObject);
212 qPersistentDispose(m_destroySymbol);
213 qPersistentDispose(m_toStringSymbol);
214 qPersistentDispose(m_signalHandlerConstructor);
215 qPersistentDispose(m_methodConstructor);
216 qPersistentDispose(m_constructor);
218 QIntrusiveList<QV8QObjectResource, &QV8QObjectResource::weakResource>::iterator i = m_javaScriptOwnedWeakQObjects.begin();
219 for (; i != m_javaScriptOwnedWeakQObjects.end(); ++i) {
220 QV8QObjectResource *resource = *i;
222 deleteWeakQObject(resource, true);
226 struct ReadAccessor {
227 static inline void Indirect(QObject *object, const QQmlPropertyData &property,
228 void *output, QQmlNotifier **n)
233 void *args[] = { output, 0 };
234 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
237 static inline void Direct(QObject *object, const QQmlPropertyData &property,
238 void *output, QQmlNotifier **n)
243 void *args[] = { output, 0 };
244 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
247 static inline void Accessor(QObject *object, const QQmlPropertyData &property,
248 void *output, QQmlNotifier **n)
250 Q_ASSERT(property.accessors);
252 property.accessors->read(object, property.accessorData, output);
253 if (n) property.accessors->notifier(object, property.accessorData, n);
257 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, int v)
258 { return v8::Integer::New(v); }
259 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, uint v)
260 { return v8::Integer::NewFromUnsigned(v); }
261 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, bool v)
262 { return v8::Boolean::New(v); }
263 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, const QString &v)
264 { return e->toString(v); }
265 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, float v)
266 { return v8::Number::New(v); }
267 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, double v)
268 { return v8::Number::New(v); }
269 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, QObject *v)
270 { return e->newQObject(v); }
272 template<typename T, void (*ReadFunction)(QObject *, const QQmlPropertyData &,
273 void *, QQmlNotifier **)>
274 static v8::Handle<v8::Value> GenericValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info)
276 v8::Handle<v8::Object> This = info.This();
277 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This);
279 QObject *object = resource->object;
280 if (QQmlData::wasDeleted(object)) return v8::Undefined();
282 QQmlPropertyData *property =
283 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
285 QQmlEngine *engine = resource->engine->engine();
286 QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0;
290 if (ep && ep->propertyCapture) {
291 if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) {
292 QQmlNotifier *notifier = 0;
293 ReadFunction(object, *property, &value, ¬ifier);
294 if (notifier) ep->captureProperty(notifier);
295 } else if (!property->isConstant()) {
296 ep->captureProperty(object, property->coreIndex, property->notifyIndex);
297 ReadFunction(object, *property, &value, 0);
299 ReadFunction(object, *property, &value, 0);
302 ReadFunction(object, *property, &value, 0);
305 return valueToHandle(resource->engine, value);
308 #define FAST_GETTER_FUNCTION(property, cpptype) \
309 (property->hasAccessors()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Accessor>):(property->isDirect()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Direct>):((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Indirect>)))
311 static quint32 toStringHash = quint32(-1);
312 static quint32 destroyHash = quint32(-1);
314 void QV8QObjectWrapper::init(QV8Engine *engine)
318 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
319 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
320 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
322 m_toStringString = QHashedV8String(m_toStringSymbol);
323 m_destroyString = QHashedV8String(m_destroySymbol);
325 toStringHash = m_toStringString.hash();
326 destroyHash = m_destroyString.hash();
329 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
330 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
331 ft->InstanceTemplate()->SetHasExternalResource(true);
332 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
335 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
336 #define CREATE_FUNCTION_SOURCE \
337 "(function(method) { "\
338 "return (function(object, data, qmlglobal) { "\
339 "return (function() { "\
340 "return method(object, data, qmlglobal, arguments.length, arguments); "\
344 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION_SOURCE), &origin, 0,
345 v8::Handle<v8::String>(), v8::Script::NativeMode);
346 #undef CREATE_FUNCTION_SOURCE
347 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
348 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
349 v8::Handle<v8::Value> args[] = { invokeFn };
350 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
351 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
354 v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine);
355 v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine);
358 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
359 ft->InstanceTemplate()->SetHasExternalResource(true);
360 ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum);
361 ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
362 m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
366 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
367 prototype->Set(v8::String::New("connect"), connect, v8::DontEnum);
368 prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
372 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
374 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
377 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
379 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
380 return r?r->object:0;
383 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
384 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
386 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
387 return static_cast<QV8QObjectResource *>(r)->object;
390 // Load value properties
391 template<void (*ReadFunction)(QObject *, const QQmlPropertyData &,
392 void *, QQmlNotifier **)>
393 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
394 const QQmlPropertyData &property,
395 QQmlNotifier **notifier)
397 Q_ASSERT(!property.isFunction());
399 if (property.isQObject()) {
401 ReadFunction(object, property, &rv, notifier);
402 return engine->newQObject(rv);
403 } else if (property.isQList()) {
404 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
405 } else if (property.propType == QMetaType::QReal) {
407 ReadFunction(object, property, &v, notifier);
408 return valueToHandle(engine, v);
409 } else if (property.propType == QMetaType::Int || property.isEnum()) {
411 ReadFunction(object, property, &v, notifier);
412 return valueToHandle(engine, v);
413 } else if (property.propType == QMetaType::Bool) {
415 ReadFunction(object, property, &v, notifier);
416 return valueToHandle(engine, v);
417 } else if (property.propType == QMetaType::QString) {
419 ReadFunction(object, property, &v, notifier);
420 return valueToHandle(engine, v);
421 } else if (property.propType == QMetaType::UInt) {
423 ReadFunction(object, property, &v, notifier);
424 return valueToHandle(engine, v);
425 } else if (property.propType == QMetaType::Float) {
427 ReadFunction(object, property, &v, notifier);
428 return valueToHandle(engine, v);
429 } else if (property.propType == QMetaType::Double) {
431 ReadFunction(object, property, &v, notifier);
432 return valueToHandle(engine, v);
433 } else if (property.isV8Handle()) {
435 ReadFunction(object, property, &handle, notifier);
436 return handle.toHandle();
437 } else if (property.propType == qMetaTypeId<QJSValue>()) {
439 ReadFunction(object, property, &v, notifier);
440 return QJSValuePrivate::get(v)->asV8Value(engine);
441 } else if (property.isQVariant()) {
443 ReadFunction(object, property, &v, notifier);
445 if (QQmlValueTypeFactory::isValueType(v.userType())) {
446 if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(v.userType()))
447 return engine->newValueType(object, property.coreIndex, valueType); // VariantReference value-type.
450 return engine->fromVariant(v);
451 } else if (QQmlValueTypeFactory::isValueType(property.propType)) {
452 Q_ASSERT(notifier == 0);
454 if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property.propType))
455 return engine->newValueType(object, property.coreIndex, valueType);
457 Q_ASSERT(notifier == 0);
459 // see if it's a sequence type
460 bool succeeded = false;
461 v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex,
467 if (property.propType == QMetaType::UnknownType) {
468 QMetaProperty p = object->metaObject()->property(property.coreIndex);
469 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
470 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
471 return v8::Undefined();
473 QVariant v(property.propType, (void *)0);
474 ReadFunction(object, property, v.data(), notifier);
475 return engine->fromVariant(v);
479 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
480 v8::Handle<v8::Value> *objectHandle,
481 const QHashedV8String &property,
482 QV8QObjectWrapper::RevisionMode revisionMode)
484 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
485 // will be a faster way of creating QObject method objects.
486 struct MethodClosure {
487 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
488 v8::Handle<v8::Value> *objectHandle,
490 v8::Handle<v8::Value> argv[] = {
491 objectHandle?*objectHandle:engine->newQObject(object),
492 v8::Integer::New(index)
494 Q_ASSERT(argv[0]->IsObject());
495 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
497 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
498 v8::Handle<v8::Value> *objectHandle,
500 v8::Handle<v8::Value> argv[] = {
501 objectHandle?*objectHandle:engine->newQObject(object),
502 v8::Integer::New(index),
503 v8::Context::GetCallingQmlGlobal()
505 Q_ASSERT(argv[0]->IsObject());
506 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
510 if (QQmlData::wasDeleted(object))
511 return v8::Handle<v8::Value>();
514 // Comparing the hash first actually makes a measurable difference here, at least on x86
515 quint32 hash = property.hash();
516 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
517 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
518 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
519 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
523 QQmlPropertyData local;
524 QQmlPropertyData *result = 0;
526 QQmlData *ddata = QQmlData::get(object, false);
527 if (ddata && ddata->propertyCache)
528 result = ddata->propertyCache->property(property);
530 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
534 return v8::Handle<v8::Value>();
536 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
537 QQmlData *ddata = QQmlData::get(object);
538 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
539 return v8::Handle<v8::Value>();
542 if (result->isFunction() && !result->isVarProperty()) {
543 if (result->isVMEFunction()) {
544 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
546 return vmemo->vmeMethod(result->coreIndex);
547 } else if (result->isV8Function()) {
548 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
549 } else if (result->isSignalHandler()) {
550 v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
551 QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
552 handler->SetExternalResource(r);
555 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
559 QQmlEnginePrivate *ep =
560 engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
562 if (result->hasAccessors()) {
564 QQmlNotifier **nptr = 0;
566 if (ep && ep->propertyCapture && result->accessors->notifier)
569 v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
571 if (result->accessors->notifier) {
572 if (n) ep->captureProperty(n);
574 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
580 if (ep && !result->isConstant())
581 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
583 if (result->isVarProperty()) {
584 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
586 return vmemo->vmeProperty(result->coreIndex);
587 } else if (result->isDirect()) {
588 return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
590 return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
594 // Setter for writable properties. Shared between the interceptor and fast property accessor
595 static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
596 v8::Handle<v8::Value> value)
598 QQmlBinding *newBinding = 0;
599 if (value->IsFunction()) {
600 if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
601 if (!property->isVarProperty() && property->propType != qMetaTypeId<QJSValue>()) {
602 // assigning a JS function to a non var or QJSValue property or is not allowed.
603 QString error = QLatin1String("Cannot assign JavaScript function to ");
604 if (!QMetaType::typeName(property->propType))
605 error += QLatin1String("[unknown property type]");
607 error += QLatin1String(QMetaType::typeName(property->propType));
608 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
612 // binding assignment.
613 QQmlContextData *context = engine->callingContext();
614 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
616 v8::Local<v8::StackTrace> trace =
617 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
618 v8::StackTrace::kScriptName));
619 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
620 int lineNumber = frame->GetLineNumber();
621 int columnNumber = frame->GetColumn();
622 QString url = engine->toString(frame->GetScriptName());
624 newBinding = new QQmlBinding(&function, object, context, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber));
625 newBinding->setTarget(object, *property, context);
626 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
627 QQmlBinding::RequiresThisObject);
631 QQmlAbstractBinding *oldBinding =
632 QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
634 oldBinding->destroy();
636 if (!newBinding && property->isVarProperty()) {
637 // allow assignment of "special" values (null, undefined, function) to var properties
638 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
640 vmemo->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);
651 if (value->IsNull() && property->isQObject()) {
652 PROPERTY_STORE(QObject*, 0);
653 } else if (value->IsUndefined() && property->isResettable()) {
655 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
656 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
657 PROPERTY_STORE(QVariant, QVariant());
658 } else if (value->IsUndefined() && property->propType == QMetaType::QJsonValue) {
659 PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
660 } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) {
661 PROPERTY_STORE(QJSValue, engine->scriptValueFromInternal(value));
662 } else if (value->IsUndefined()) {
663 QString error = QLatin1String("Cannot assign [undefined] to ");
664 if (!QMetaType::typeName(property->propType))
665 error += QLatin1String("[unknown property type]");
667 error += QLatin1String(QMetaType::typeName(property->propType));
668 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
669 } else if (value->IsFunction()) {
670 // this is handled by the binding creation above
671 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
672 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
673 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
674 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
675 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
676 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
677 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
678 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
679 } else if (property->propType == QMetaType::QString && value->IsString()) {
680 PROPERTY_STORE(QString, engine->toString(value->ToString()));
681 } else if (property->isVarProperty()) {
682 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
684 vmemo->setVMEProperty(property->coreIndex, value);
687 if (property->isQList())
688 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
690 v = engine->toVariant(value, property->propType);
692 QQmlContextData *context = engine->callingContext();
693 if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
694 const char *valueType = 0;
695 if (v.userType() == QVariant::Invalid) valueType = "null";
696 else valueType = QMetaType::typeName(v.userType());
698 const char *targetTypeName = QMetaType::typeName(property->propType);
700 targetTypeName = "an unregistered type";
702 QString error = QLatin1String("Cannot assign ") +
703 QLatin1String(valueType) +
704 QLatin1String(" to ") +
705 QLatin1String(targetTypeName);
706 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
711 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
712 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
714 if (engine->qobjectWrapper()->m_toStringString == property ||
715 engine->qobjectWrapper()->m_destroyString == property)
718 if (QQmlData::wasDeleted(object))
721 QQmlPropertyData local;
722 QQmlPropertyData *result = 0;
723 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
728 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
729 QQmlData *ddata = QQmlData::get(object);
730 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
734 if (!result->isWritable() && !result->isQList()) {
735 QString error = QLatin1String("Cannot assign to read-only property \"") +
736 engine->toString(property.string()) + QLatin1Char('\"');
737 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
741 StoreProperty(engine, object, result, value);
746 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
747 const v8::AccessorInfo &info)
749 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
751 if (QQmlData::wasDeleted(resource->object))
752 return v8::Handle<v8::Value>();
754 QObject *object = resource->object;
756 QHashedV8String propertystring(property);
758 QV8Engine *v8engine = resource->engine;
759 v8::Handle<v8::Value> This = info.This();
760 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
761 QV8QObjectWrapper::IgnoreRevision);
762 if (!result.IsEmpty())
765 if (QV8Engine::startsWithUpper(property)) {
766 // Check for attached properties
767 QQmlContextData *context = v8engine->callingContext();
769 if (context && context->imports) {
770 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
773 if (r.scriptIndex != -1) {
774 return v8::Undefined();
776 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
777 } else if (r.importNamespace) {
778 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
779 QV8TypeWrapper::ExcludeEnums);
781 Q_ASSERT(!"Unreachable");
786 return v8::Handle<v8::Value>();
789 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
790 v8::Local<v8::Value> value,
791 const v8::AccessorInfo &info)
793 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
795 if (QQmlData::wasDeleted(resource->object))
798 QObject *object = resource->object;
800 QHashedV8String propertystring(property);
802 QV8Engine *v8engine = resource->engine;
803 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
806 QString error = QLatin1String("Cannot assign to non-existent property \"") +
807 v8engine->toString(property) + QLatin1Char('\"');
808 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
815 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
816 const v8::AccessorInfo &info)
818 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
820 if (resource->object.isNull())
821 return v8::Handle<v8::Integer>();
823 QV8Engine *engine = resource->engine;
824 QObject *object = resource->object;
826 QHashedV8String propertystring(property);
828 QQmlPropertyData local;
829 QQmlPropertyData *result = 0;
830 result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local);
833 return v8::Handle<v8::Integer>();
834 else if (!result->isWritable() && !result->isQList())
835 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
837 return v8::Integer::New(v8::DontDelete);
840 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
842 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
844 if (resource->object.isNull())
845 return v8::Array::New();
847 QObject *object = resource->object;
851 QQmlEnginePrivate *ep = resource->engine->engine()
852 ? QQmlEnginePrivate::get(resource->engine->engine())
855 QQmlPropertyCache *cache = 0;
856 QQmlData *ddata = QQmlData::get(object);
858 cache = ddata->propertyCache;
861 cache = ep ? ep->cache(object) : 0;
863 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
865 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
866 const QMetaObject *mo = object->metaObject();
867 int pc = mo->propertyCount();
868 int po = mo->propertyOffset();
869 for (int i=po; i<pc; ++i)
870 result << QString::fromUtf8(mo->property(i).name());
873 result = cache->propertyNames();
876 v8::Local<v8::Array> rv = v8::Array::New(result.count());
878 for (int ii = 0; ii < result.count(); ++ii)
879 rv->Set(ii, resource->engine->toString(result.at(ii)));
884 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
885 const v8::AccessorInfo& info)
887 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
889 if (QQmlData::wasDeleted(resource->object))
892 QObject *object = resource->object;
894 QQmlPropertyData *property =
895 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
897 int index = property->coreIndex;
899 QQmlData *ddata = QQmlData::get(object, false);
901 Q_ASSERT(ddata->propertyCache);
903 QQmlPropertyData *pdata = ddata->propertyCache->property(index);
906 Q_ASSERT(pdata->isWritable() || pdata->isQList());
908 StoreProperty(resource->engine, object, pdata, value);
911 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
912 const v8::AccessorInfo& info)
914 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
916 if (QQmlData::wasDeleted(resource->object))
919 QV8Engine *v8engine = resource->engine;
921 QString error = QLatin1String("Cannot assign to read-only property \"") +
922 v8engine->toString(property) + QLatin1Char('\"');
923 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
926 void QV8QObjectWrapper::WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *wrapper)
928 Q_ASSERT(handle->IsObject());
929 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
932 static_cast<QV8QObjectWrapper*>(wrapper)->unregisterWeakQObjectReference(resource);
933 if (static_cast<QV8QObjectWrapper*>(wrapper)->deleteWeakQObject(resource, false)) {
934 qPersistentDispose(handle); // dispose.
936 handle.MakeWeak(0, WeakQObjectReferenceCallback); // revive.
940 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
942 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
943 instance->v8object.Clear();
944 qPersistentDispose(handle);
947 v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
950 Q_ASSERT(this->engine);
952 Q_ASSERT(QQmlData::get(object, false));
953 Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
956 if (constructor.IsEmpty()) {
957 v8::Local<v8::FunctionTemplate> ft;
959 QString toString = QLatin1String("toString");
960 QString destroy = QLatin1String("destroy");
962 // As we use hash linking, it is possible that iterating over the values can give duplicates.
963 // To combat this, we must unique'ify our properties.
964 StringCache uniqueHash;
965 if (stringCache.isLinked())
966 uniqueHash.reserve(stringCache.count());
968 // XXX TODO: Enables fast property accessors. These more than double the property access
969 // performance, but the cost of setting up this structure hasn't been measured so
970 // its not guarenteed that this is a win overall. We need to try and measure the cost.
971 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
972 if (stringCache.isLinked()) {
973 if (uniqueHash.contains(iter))
975 uniqueHash.insert(iter);
978 QQmlPropertyData *property = *iter;
979 if (property->notFullyResolved()) resolve(property);
981 if (property->isFunction())
984 v8::AccessorGetter fastgetter = 0;
985 v8::AccessorSetter fastsetter = FastValueSetter;
986 if (!property->isWritable())
987 fastsetter = FastValueSetterReadOnly;
989 if (property->isQObject())
990 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
991 else if (property->propType == QMetaType::Int || property->isEnum())
992 fastgetter = FAST_GETTER_FUNCTION(property, int);
993 else if (property->propType == QMetaType::Bool)
994 fastgetter = FAST_GETTER_FUNCTION(property, bool);
995 else if (property->propType == QMetaType::QString)
996 fastgetter = FAST_GETTER_FUNCTION(property, QString);
997 else if (property->propType == QMetaType::UInt)
998 fastgetter = FAST_GETTER_FUNCTION(property, uint);
999 else if (property->propType == QMetaType::Float)
1000 fastgetter = FAST_GETTER_FUNCTION(property, float);
1001 else if (property->propType == QMetaType::Double)
1002 fastgetter = FAST_GETTER_FUNCTION(property, double);
1005 QString name = iter.key();
1006 if (name == toString || name == destroy)
1010 ft = v8::FunctionTemplate::New();
1011 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1012 QV8QObjectWrapper::Setter,
1013 QV8QObjectWrapper::Query,
1015 QV8QObjectWrapper::Enumerator);
1016 ft->InstanceTemplate()->SetHasExternalResource(true);
1019 // We wrap the raw QQmlPropertyData pointer here. This is safe as the
1020 // pointer will remain valid at least as long as the lifetime of any QObject's of
1021 // this type and the property accessor checks if the object is 0 (deleted) before
1022 // dereferencing the pointer.
1023 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
1024 v8::External::Wrap(property));
1029 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
1031 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1032 QV8QObjectWrapper::Setter,
1033 QV8QObjectWrapper::Query,
1035 QV8QObjectWrapper::Enumerator);
1036 ft->InstanceTemplate()->SetHasExternalResource(true);
1037 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
1040 QQmlCleanup::addToEngine(this->engine);
1043 v8::Local<v8::Object> result = constructor->NewInstance();
1044 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1045 result->SetExternalResource(r);
1049 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
1051 v8::Local<v8::Object> rv;
1053 if (!ddata->propertyCache && engine->engine()) {
1054 ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
1055 if (ddata->propertyCache) ddata->propertyCache->addref();
1058 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1059 rv = ddata->propertyCache->newQObject(object, engine);
1061 // XXX NewInstance() should be optimized
1062 rv = m_constructor->NewInstance();
1063 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1064 rv->SetExternalResource(r);
1071 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1072 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
1074 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
1075 persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that
1076 "owns" this handle by setting the QQmlData::v8objectid to the id of this
1078 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create
1079 an entry in the m_taintedObject hash where we store the handle and mark the object as
1080 "tainted" in the QQmlData::hasTaintedV8Object flag.
1081 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1082 in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has
1083 released the handle.
1085 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1087 if (QQmlData::wasDeleted(object))
1090 QQmlData *ddata = QQmlData::get(object, true);
1092 return v8::Undefined();
1094 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1095 // We own the v8object
1096 return v8::Local<v8::Object>::New(ddata->v8object);
1097 } else if (ddata->v8object.IsEmpty() &&
1098 (ddata->v8objectid == m_id || // We own the QObject
1099 ddata->v8objectid == 0 || // No one owns the QObject
1100 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1102 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1103 ddata->v8object = qPersistentNew<v8::Object>(rv);
1104 ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1105 ddata->v8objectid = m_id;
1106 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1107 registerWeakQObjectReference(resource);
1111 // If this object is tainted, we have to check to see if it is in our
1112 // tainted object list
1113 TaintedHash::Iterator iter =
1114 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1115 bool found = iter != m_taintedObjects.end();
1117 // If our tainted handle doesn't exist or has been collected, and there isn't
1118 // a handle in the ddata, we can assume ownership of the ddata->v8object
1119 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1120 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1121 ddata->v8object = qPersistentNew<v8::Object>(rv);
1122 ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1123 ddata->v8objectid = m_id;
1124 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1125 registerWeakQObjectReference(resource);
1129 m_taintedObjects.erase(iter);
1133 } else if (!found) {
1134 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1135 iter = m_taintedObjects.insert(object, instance);
1136 ddata->hasTaintedV8Object = true;
1139 if ((*iter)->v8object.IsEmpty()) {
1140 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1141 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1142 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1145 return v8::Local<v8::Object>::New((*iter)->v8object);
1149 // returns true if the object's qqmldata v8object handle should
1150 // be disposed by the caller, false if it should not be (due to
1151 // creation status, etc).
1152 bool QV8QObjectWrapper::deleteWeakQObject(QV8QObjectResource *resource, bool calledFromEngineDtor)
1154 QObject *object = resource->object;
1156 QQmlData *ddata = QQmlData::get(object, false);
1158 if (!calledFromEngineDtor && ddata->rootObjectInCreation) {
1159 // if weak ref callback is triggered (by gc) for a root object
1160 // prior to completion of creation, we should NOT delete it.
1164 ddata->v8object.Clear();
1165 if (!object->parent() && !ddata->indestructible) {
1166 // This object is notionally destroyed now
1167 if (ddata->ownContext && ddata->context)
1168 ddata->context->emitDestruction();
1169 ddata->isQueuedForDeletion = true;
1170 object->deleteLater();
1178 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1180 if (object->IsFunction())
1181 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1183 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1184 return qMakePair(resource->object.data(), resource->index);
1186 return qMakePair((QObject *)0, -1);
1189 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1191 v8::ScriptOrigin origin = function->GetScriptOrigin();
1192 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1194 // This is one of our special QObject method wrappers
1195 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1196 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1198 if (data->IsArray()) {
1199 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1200 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1203 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1206 return qMakePair((QObject *)0, -1);
1209 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1212 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1213 ~QV8QObjectConnectionList();
1217 : needsDestroy(false) {}
1218 Connection(const Connection &other)
1219 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1220 Connection &operator=(const Connection &other) {
1221 thisObject = other.thisObject;
1222 function = other.function;
1223 needsDestroy = other.needsDestroy;
1227 v8::Persistent<v8::Object> thisObject;
1228 v8::Persistent<v8::Function> function;
1231 qPersistentDispose(thisObject);
1232 qPersistentDispose(function);
1238 struct ConnectionList : public QList<Connection> {
1239 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1240 int connectionsInUse;
1241 bool connectionsNeedClean;
1246 typedef QHash<int, ConnectionList> SlotHash;
1251 virtual void objectDestroyed(QObject *);
1252 virtual int qt_metacall(QMetaObject::Call, int, void **);
1255 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1256 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1260 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1262 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1263 QList<Connection> &connections = *iter;
1264 for (int ii = 0; ii < connections.count(); ++ii) {
1265 qPersistentDispose(connections[ii].thisObject);
1266 qPersistentDispose(connections[ii].function);
1272 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1274 engine->qobjectWrapper()->m_connections.remove(object);
1277 needsDestroy = true;
1282 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1284 if (method == QMetaObject::InvokeMetaMethod) {
1285 SlotHash::Iterator iter = slotHash.find(index);
1286 if (iter == slotHash.end())
1288 ConnectionList &connectionList = *iter;
1289 if (connectionList.isEmpty())
1294 connectionList.connectionsInUse++;
1296 QList<Connection> connections = connectionList;
1298 QVarLengthArray<int, 9> dummy;
1299 int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1301 v8::HandleScope handle_scope;
1302 v8::Context::Scope scope(engine->context());
1304 int argCount = argsTypes?argsTypes[0]:0;
1305 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1307 for (int ii = 0; ii < argCount; ++ii) {
1308 int type = argsTypes[ii + 1];
1309 if (type == qMetaTypeId<QVariant>()) {
1310 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1312 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1316 for (int ii = 0; ii < connections.count(); ++ii) {
1317 Connection &connection = connections[ii];
1318 if (connection.needsDestroy)
1321 v8::TryCatch try_catch;
1322 if (connection.thisObject.IsEmpty()) {
1323 connection.function->Call(engine->global(), argCount, args.data());
1325 connection.function->Call(connection.thisObject, argCount, args.data());
1328 if (try_catch.HasCaught()) {
1330 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1331 v8::Local<v8::Message> message = try_catch.Message();
1332 if (!message.IsEmpty())
1333 QQmlExpressionPrivate::exceptionToError(message, error);
1334 QQmlEnginePrivate::get(engine->engine())->warning(error);
1338 connectionList.connectionsInUse--;
1339 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1340 for (QList<Connection>::Iterator iter = connectionList.begin();
1341 iter != connectionList.end(); ) {
1342 if (iter->needsDestroy) {
1344 iter = connectionList.erase(iter);
1352 if (inUse == 0 && needsDestroy)
1359 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1361 if (args.Length() == 0)
1362 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1364 QV8Engine *engine = V8ENGINE();
1366 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1367 QObject *signalObject = signalInfo.first;
1368 int signalIndex = signalInfo.second;
1370 if (signalIndex == -1)
1371 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1374 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1376 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1377 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1379 v8::Local<v8::Value> functionValue;
1380 v8::Local<v8::Value> functionThisValue;
1382 if (args.Length() == 1) {
1383 functionValue = args[0];
1385 functionThisValue = args[0];
1386 functionValue = args[1];
1389 if (!functionValue->IsFunction())
1390 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1392 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1393 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1395 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1396 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1397 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1398 if (iter == connections.end())
1399 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1401 QV8QObjectConnectionList *connectionList = *iter;
1402 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1403 if (slotIter == connectionList->slotHash.end()) {
1404 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1405 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1408 QV8QObjectConnectionList::Connection connection;
1409 if (!functionThisValue.IsEmpty())
1410 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1411 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1413 slotIter->append(connection);
1415 return v8::Undefined();
1418 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1420 if (args.Length() == 0)
1421 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1423 QV8Engine *engine = V8ENGINE();
1425 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1426 QObject *signalObject = signalInfo.first;
1427 int signalIndex = signalInfo.second;
1429 if (signalIndex == -1)
1430 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1433 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1435 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1436 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1438 v8::Local<v8::Value> functionValue;
1439 v8::Local<v8::Value> functionThisValue;
1441 if (args.Length() == 1) {
1442 functionValue = args[0];
1444 functionThisValue = args[0];
1445 functionValue = args[1];
1448 if (!functionValue->IsFunction())
1449 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1451 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1452 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1454 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1455 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1456 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1457 if (iter == connectionsList.end())
1458 return v8::Undefined(); // Nothing to disconnect from
1460 QV8QObjectConnectionList *connectionList = *iter;
1461 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1462 if (slotIter == connectionList->slotHash.end())
1463 return v8::Undefined(); // Nothing to disconnect from
1465 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1467 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1468 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1470 if (functionData.second != -1) {
1471 // This is a QObject function wrapper
1472 for (int ii = 0; ii < connections.count(); ++ii) {
1473 QV8QObjectConnectionList::Connection &connection = connections[ii];
1475 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1476 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1478 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1479 if (connectedFunctionData == functionData) {
1481 if (connections.connectionsInUse) {
1482 connection.needsDestroy = true;
1483 connections.connectionsNeedClean = true;
1485 connection.dispose();
1486 connections.removeAt(ii);
1488 return v8::Undefined();
1494 // This is a normal JS function
1495 for (int ii = 0; ii < connections.count(); ++ii) {
1496 QV8QObjectConnectionList::Connection &connection = connections[ii];
1497 if (connection.function->StrictEquals(function) &&
1498 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1499 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1501 if (connections.connectionsInUse) {
1502 connection.needsDestroy = true;
1503 connections.connectionsNeedClean = true;
1505 connection.dispose();
1506 connections.removeAt(ii);
1508 return v8::Undefined();
1513 return v8::Undefined();
1517 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1519 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1521 Only searches for real properties of \a object (including methods), not attached properties etc.
1525 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1527 Set the \a property of \a object to \a value.
1529 Returns true if the property was "set" - even if this results in an exception being thrown -
1530 and false if the object has no such property.
1532 Only searches for real properties of \a object (including methods), not attached properties etc.
1538 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1539 int Length() const { return _length; }
1540 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1544 v8::Handle<v8::Object> *_args;
1548 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1549 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1553 // Special handling is required for value types.
1554 // We need to save the current value in a temporary,
1555 // and reapply it after converting all arguments.
1556 // This avoids the "overwriting copy-value-type-value"
1557 // problem during Q_INVOKABLE function invocation.
1558 QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1559 QVariant valueTypeValue;
1560 if (valueTypeObject)
1561 valueTypeValue = valueTypeObject->value();
1563 // Convert all arguments.
1564 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1565 args[0].initAsType(returnType);
1566 for (int ii = 0; ii < argCount; ++ii)
1567 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1568 QVarLengthArray<void *, 9> argData(args.count());
1569 for (int ii = 0; ii < args.count(); ++ii)
1570 argData[ii] = args[ii].dataPtr();
1572 // Reinstate saved value type object value if required.
1573 if (valueTypeObject)
1574 valueTypeObject->setValue(valueTypeValue);
1576 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1578 return args[0].toValue(engine);
1580 } else if (returnType != QMetaType::Void) {
1583 arg.initAsType(returnType);
1585 void *args[] = { arg.dataPtr() };
1587 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1589 return arg.toValue(engine);
1593 void *args[] = { 0 };
1594 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1595 return v8::Undefined();
1601 Returns the match score for converting \a actual to be of type \a conversionType. A
1602 zero score means "perfect match" whereas a higher score is worse.
1604 The conversion table is copied out of the QtScript callQtMethod() function.
1606 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1608 if (actual->IsNumber()) {
1609 switch (conversionType) {
1610 case QMetaType::Double:
1612 case QMetaType::Float:
1614 case QMetaType::LongLong:
1615 case QMetaType::ULongLong:
1617 case QMetaType::Long:
1618 case QMetaType::ULong:
1620 case QMetaType::Int:
1621 case QMetaType::UInt:
1623 case QMetaType::Short:
1624 case QMetaType::UShort:
1627 case QMetaType::Char:
1628 case QMetaType::UChar:
1630 case QMetaType::QJsonValue:
1635 } else if (actual->IsString()) {
1636 switch (conversionType) {
1637 case QMetaType::QString:
1639 case QMetaType::QJsonValue:
1644 } else if (actual->IsBoolean()) {
1645 switch (conversionType) {
1646 case QMetaType::Bool:
1648 case QMetaType::QJsonValue:
1653 } else if (actual->IsDate()) {
1654 switch (conversionType) {
1655 case QMetaType::QDateTime:
1657 case QMetaType::QDate:
1659 case QMetaType::QTime:
1664 } else if (actual->IsRegExp()) {
1665 switch (conversionType) {
1666 case QMetaType::QRegExp:
1671 } else if (actual->IsArray()) {
1672 switch (conversionType) {
1673 case QMetaType::QJsonArray:
1675 case QMetaType::QStringList:
1676 case QMetaType::QVariantList:
1678 case QMetaType::QVector4D:
1679 case QMetaType::QMatrix4x4:
1681 case QMetaType::QVector3D:
1686 } else if (actual->IsNull()) {
1687 switch (conversionType) {
1688 case QMetaType::VoidStar:
1689 case QMetaType::QObjectStar:
1690 case QMetaType::QJsonValue:
1693 const char *typeName = QMetaType::typeName(conversionType);
1694 if (typeName && typeName[strlen(typeName) - 1] == '*')
1700 } else if (actual->IsObject()) {
1701 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1703 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1704 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1705 switch (conversionType) {
1706 case QMetaType::QObjectStar:
1711 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1712 if (conversionType == qMetaTypeId<QVariant>())
1714 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1718 } else if (r && r->resourceType() == QV8ObjectResource::ValueTypeType) {
1719 if (r->engine->toVariant(actual, -1).userType() == conversionType)
1722 } else if (conversionType == QMetaType::QJsonObject) {
1733 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1739 int classInfoCount, classInfoData;
1740 int methodCount, methodData;
1743 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1747 Returns the next related method, if one, or 0.
1749 static const QQmlPropertyData * RelatedMethod(QObject *object,
1750 const QQmlPropertyData *current,
1751 QQmlPropertyData &dummy)
1753 QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1754 if (!current->isOverload())
1757 Q_ASSERT(!current->overrideIndexIsProperty);
1760 return cache->method(current->overrideIndex);
1762 const QMetaObject *mo = object->metaObject();
1763 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1765 while (methodOffset > current->overrideIndex) {
1766 mo = mo->superClass();
1767 methodOffset -= QMetaObject_methods(mo);
1770 QMetaMethod method = mo->method(current->overrideIndex);
1773 // Look for overloaded methods
1774 QByteArray methodName = method.name();
1775 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1776 if (methodName == mo->method(ii).name()) {
1777 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1778 dummy.overrideIndexIsProperty = 0;
1779 dummy.overrideIndex = ii;
1788 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1789 QV8Engine *engine, CallArgs &callArgs)
1791 QByteArray unknownTypeError;
1793 int returnType = QQmlPropertyCache::methodReturnType(object, data, &unknownTypeError);
1795 if (returnType == QMetaType::UnknownType) {
1796 QString typeName = QString::fromLatin1(unknownTypeError);
1797 QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName);
1798 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1799 return v8::Handle<v8::Value>();
1802 if (data.hasArguments()) {
1805 QVarLengthArray<int, 9> dummy;
1807 args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1811 QString typeName = QString::fromLatin1(unknownTypeError);
1812 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1813 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1814 return v8::Handle<v8::Value>();
1817 if (args[0] > callArgs.Length()) {
1818 QString error = QLatin1String("Insufficient arguments");
1819 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1820 return v8::Handle<v8::Value>();
1823 return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs);
1827 return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs);
1833 Resolve the overloaded method to call. The algorithm works conceptually like this:
1834 1. Resolve the set of overloads it is *possible* to call.
1835 Impossible overloads include those that have too many parameters or have parameters
1837 2. Filter the set of overloads to only contain those with the closest number of
1839 For example, if we are called with 3 parameters and there are 2 overloads that
1840 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1841 3. Find the best remaining overload based on its match score.
1842 If two or more overloads have the same match score, call the last one. The match
1843 score is constructed by adding the matchScore() result for each of the parameters.
1845 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1846 QV8Engine *engine, CallArgs &callArgs)
1848 int argumentCount = callArgs.Length();
1850 const QQmlPropertyData *best = 0;
1851 int bestParameterScore = INT_MAX;
1852 int bestMatchScore = INT_MAX;
1854 // Special handling is required for value types.
1855 // We need to save the current value in a temporary,
1856 // and reapply it after converting all arguments.
1857 // This avoids the "overwriting copy-value-type-value"
1858 // problem during Q_INVOKABLE function invocation.
1859 QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1860 QVariant valueTypeValue;
1861 if (valueTypeObject)
1862 valueTypeValue = valueTypeObject->value();
1864 QQmlPropertyData dummy;
1865 const QQmlPropertyData *attempt = &data;
1868 QVarLengthArray<int, 9> dummy;
1869 int methodArgumentCount = 0;
1870 int *methodArgTypes = 0;
1871 if (attempt->hasArguments()) {
1872 typedef QQmlPropertyCache PC;
1873 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1874 if (!args) // Must be an unknown argument
1877 methodArgumentCount = args[0];
1878 methodArgTypes = args + 1;
1881 if (methodArgumentCount > argumentCount)
1882 continue; // We don't have sufficient arguments to call this method
1884 int methodParameterScore = argumentCount - methodArgumentCount;
1885 if (methodParameterScore > bestParameterScore)
1886 continue; // We already have a better option
1888 int methodMatchScore = 0;
1889 for (int ii = 0; ii < methodArgumentCount; ++ii)
1890 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1892 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1894 bestParameterScore = methodParameterScore;
1895 bestMatchScore = methodMatchScore;
1898 if (bestParameterScore == 0 && bestMatchScore == 0)
1899 break; // We can't get better than that
1901 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1904 if (valueTypeObject)
1905 valueTypeObject->setValue(valueTypeValue);
1906 return CallPrecise(object, *best, engine, callArgs);
1908 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1909 const QQmlPropertyData *candidate = &data;
1911 error += QLatin1String("\n ") +
1912 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData());
1913 candidate = RelatedMethod(object, candidate, dummy);
1916 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1917 return v8::Handle<v8::Value>();
1921 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1925 QString objectName = object->objectName();
1927 result += QString::fromUtf8(object->metaObject()->className());
1928 result += QLatin1String("(0x");
1929 result += QString::number((quintptr)object,16);
1931 if (!objectName.isEmpty()) {
1932 result += QLatin1String(", \"");
1933 result += objectName;
1934 result += QLatin1Char('\"');
1937 result += QLatin1Char(')');
1939 result = QLatin1String("null");
1942 return engine->toString(result);
1945 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1947 QQmlData *ddata = QQmlData::get(object, false);
1948 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation) {
1949 const char *error = "Invalid attempt to destroy() an indestructible object";
1950 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1951 return v8::Undefined();
1956 delay = args->Get(0)->Uint32Value();
1959 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1961 object->deleteLater();
1963 return v8::Undefined();
1966 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1968 // object, index, qmlglobal, argCount, args
1969 Q_ASSERT(args.Length() == 5);
1970 Q_ASSERT(args[0]->IsObject());
1972 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1975 return v8::Undefined();
1977 int argCount = args[3]->Int32Value();
1978 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1980 // Special hack to return info about this closure.
1981 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1982 v8::Local<v8::Array> data = v8::Array::New(2);
1983 data->Set(0, args[0]);
1984 data->Set(1, args[1]);
1988 QObject *object = resource->object;
1989 int index = args[1]->Int32Value();
1992 return v8::Undefined();
1995 // Builtin functions
1996 if (index == QOBJECT_TOSTRING_INDEX) {
1997 return ToString(resource->engine, object, argCount, arguments);
1998 } else if (index == QOBJECT_DESTROY_INDEX) {
1999 return Destroy(resource->engine, object, argCount, arguments);
2001 return v8::Undefined();
2005 QQmlPropertyData method;
2007 if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
2008 if (ddata->propertyCache) {
2009 QQmlPropertyData *d = ddata->propertyCache->method(index);
2011 return v8::Undefined();
2016 if (method.coreIndex == -1) {
2017 method.load(object->metaObject()->method(index));
2019 if (method.coreIndex == -1)
2020 return v8::Undefined();
2023 if (method.isV8Function()) {
2024 v8::Handle<v8::Value> rv;
2025 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
2027 QQmlV8Function func(argCount, arguments, rv, qmlglobal,
2028 resource->engine->contextWrapper()->context(qmlglobal),
2030 QQmlV8Function *funcptr = &func;
2032 void *args[] = { 0, &funcptr };
2033 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
2035 if (rv.IsEmpty()) return v8::Undefined();
2039 CallArgs callArgs(argCount, &arguments);
2040 if (!method.isOverload()) {
2041 return CallPrecise(object, method, resource->engine, callArgs);
2043 return CallOverloaded(object, method, resource->engine, callArgs);
2047 CallArgument::CallArgument()
2048 : type(QVariant::Invalid)
2052 CallArgument::~CallArgument()
2057 void CallArgument::cleanup()
2059 if (type == QMetaType::QString) {
2060 qstringPtr->~QString();
2061 } else if (type == -1 || type == QMetaType::QVariant) {
2062 qvariantPtr->~QVariant();
2063 } else if (type == qMetaTypeId<QJSValue>()) {
2064 qjsValuePtr->~QJSValue();
2065 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2066 qlistPtr->~QList<QObject *>();
2067 } else if (type == QMetaType::QJsonArray) {
2068 jsonArrayPtr->~QJsonArray();
2069 } else if (type == QMetaType::QJsonObject) {
2070 jsonObjectPtr->~QJsonObject();
2071 } else if (type == QMetaType::QJsonValue) {
2072 jsonValuePtr->~QJsonValue();
2076 void *CallArgument::dataPtr()
2079 return qvariantPtr->data();
2081 return (void *)&allocData;
2084 void CallArgument::initAsType(int callType)
2086 if (type != 0) { cleanup(); type = 0; }
2087 if (callType == QMetaType::UnknownType) return;
2089 if (callType == qMetaTypeId<QJSValue>()) {
2090 qjsValuePtr = new (&allocData) QJSValue();
2092 } else if (callType == QMetaType::Int ||
2093 callType == QMetaType::UInt ||
2094 callType == QMetaType::Bool ||
2095 callType == QMetaType::Double ||
2096 callType == QMetaType::Float) {
2098 } else if (callType == QMetaType::QObjectStar) {
2101 } else if (callType == QMetaType::QString) {
2102 qstringPtr = new (&allocData) QString();
2104 } else if (callType == QMetaType::QVariant) {
2106 qvariantPtr = new (&allocData) QVariant();
2107 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2109 qlistPtr = new (&allocData) QList<QObject *>();
2110 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2112 handlePtr = new (&allocData) QQmlV8Handle;
2113 } else if (callType == QMetaType::QJsonArray) {
2115 jsonArrayPtr = new (&allocData) QJsonArray();
2116 } else if (callType == QMetaType::QJsonObject) {
2118 jsonObjectPtr = new (&allocData) QJsonObject();
2119 } else if (callType == QMetaType::QJsonValue) {
2121 jsonValuePtr = new (&allocData) QJsonValue();
2122 } else if (callType == QMetaType::Void) {
2124 qvariantPtr = new (&allocData) QVariant();
2127 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2131 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2133 if (type != 0) { cleanup(); type = 0; }
2135 if (callType == qMetaTypeId<QJSValue>()) {
2136 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2137 type = qMetaTypeId<QJSValue>();
2138 } else if (callType == QMetaType::Int) {
2139 intValue = quint32(value->Int32Value());
2141 } else if (callType == QMetaType::UInt) {
2142 intValue = quint32(value->Uint32Value());
2144 } else if (callType == QMetaType::Bool) {
2145 boolValue = value->BooleanValue();
2147 } else if (callType == QMetaType::Double) {
2148 doubleValue = double(value->NumberValue());
2150 } else if (callType == QMetaType::Float) {
2151 floatValue = float(value->NumberValue());
2153 } else if (callType == QMetaType::QString) {
2154 if (value->IsNull() || value->IsUndefined())
2155 qstringPtr = new (&allocData) QString();
2157 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2159 } else if (callType == QMetaType::QObjectStar) {
2160 qobjectPtr = engine->toQObject(value);
2162 } else if (callType == qMetaTypeId<QVariant>()) {
2163 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2165 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2166 qlistPtr = new (&allocData) QList<QObject *>();
2167 if (value->IsArray()) {
2168 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2169 uint32_t length = array->Length();
2170 for (uint32_t ii = 0; ii < length; ++ii)
2171 qlistPtr->append(engine->toQObject(array->Get(ii)));
2173 qlistPtr->append(engine->toQObject(value));
2176 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2177 handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2179 } else if (callType == QMetaType::QJsonArray) {
2180 jsonArrayPtr = new (&allocData) QJsonArray(engine->jsonArrayFromJS(value));
2182 } else if (callType == QMetaType::QJsonObject) {
2183 jsonObjectPtr = new (&allocData) QJsonObject(engine->jsonObjectFromJS(value));
2185 } else if (callType == QMetaType::QJsonValue) {
2186 jsonValuePtr = new (&allocData) QJsonValue(engine->jsonValueFromJS(value));
2188 } else if (callType == QMetaType::Void) {
2189 *qvariantPtr = QVariant();
2191 qvariantPtr = new (&allocData) QVariant();
2194 QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2195 QVariant v = engine->toVariant(value, -1); // why -1 instead of callType?
2197 if (v.userType() == callType) {
2199 } else if (v.canConvert(callType)) {
2201 qvariantPtr->convert(callType);
2202 } else if (engine->sequenceWrapper()->isSequenceType(callType) && v.userType() == qMetaTypeId<QVariantList>()) {
2203 // convert the JS array to a sequence of the correct type.
2204 QVariant seqV = engine->toVariant(value, callType);
2205 *qvariantPtr = seqV;
2207 QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
2209 QObject *obj = ep->toQObject(v);
2211 if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo))
2214 *qvariantPtr = QVariant(callType, &obj);
2216 *qvariantPtr = QVariant(callType, (void *)0);
2222 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2224 if (type == qMetaTypeId<QJSValue>()) {
2225 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2226 } else if (type == QMetaType::Int) {
2227 return v8::Integer::New(int(intValue));
2228 } else if (type == QMetaType::UInt) {
2229 return v8::Integer::NewFromUnsigned(intValue);
2230 } else if (type == QMetaType::Bool) {
2231 return v8::Boolean::New(boolValue);
2232 } else if (type == QMetaType::Double) {
2233 return v8::Number::New(doubleValue);
2234 } else if (type == QMetaType::Float) {
2235 return v8::Number::New(floatValue);
2236 } else if (type == QMetaType::QString) {
2237 return engine->toString(*qstringPtr);
2238 } else if (type == QMetaType::QObjectStar) {
2239 QObject *object = qobjectPtr;
2241 QQmlData::get(object, true)->setImplicitDestructible();
2242 return engine->newQObject(object);
2243 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2244 // XXX Can this be made more by using Array as a prototype and implementing
2245 // directly against QList<QObject*>?
2246 QList<QObject *> &list = *qlistPtr;
2247 v8::Local<v8::Array> array = v8::Array::New(list.count());
2248 for (int ii = 0; ii < list.count(); ++ii)
2249 array->Set(ii, engine->newQObject(list.at(ii)));
2251 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2252 return handlePtr->toHandle();
2253 } else if (type == QMetaType::QJsonArray) {
2254 return engine->jsonArrayToJS(*jsonArrayPtr);
2255 } else if (type == QMetaType::QJsonObject) {
2256 return engine->jsonObjectToJS(*jsonObjectPtr);
2257 } else if (type == QMetaType::QJsonValue) {
2258 return engine->jsonValueToJS(*jsonValuePtr);
2259 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2260 QVariant value = *qvariantPtr;
2261 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2262 if (QObject *object = engine->toQObject(rv))
2263 QQmlData::get(object, true)->setImplicitDestructible();
2266 return v8::Undefined();