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/qjsonarray.h>
58 #include <QtCore/qjsonobject.h>
59 #include <QtCore/qjsonvalue.h>
60 #include <QtCore/qvarlengtharray.h>
61 #include <QtCore/qtimer.h>
62 #include <QtCore/qatomic.h>
64 Q_DECLARE_METATYPE(QJSValue);
65 Q_DECLARE_METATYPE(QQmlV8Handle);
69 #if defined(__GNUC__) && !defined(__INTEL_COMPILER)
70 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
71 // The code in this file does not violate strict aliasing, but GCC thinks it does
72 // so turn off the warnings for us to have a clean build
73 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
77 #define QOBJECT_TOSTRING_INDEX -2
78 #define QOBJECT_DESTROY_INDEX -3
80 // XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work
81 // correctly in a worker thread
83 class QV8QObjectInstance : public QQmlGuard<QObject>
86 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
87 : QQmlGuard<QObject>(o), wrapper(w)
93 qPersistentDispose(v8object);
96 virtual void objectDestroyed(QObject *o)
99 wrapper->m_taintedObjects.remove(o);
103 v8::Persistent<v8::Object> v8object;
104 QV8QObjectWrapper *wrapper;
107 class QV8SignalHandlerResource : public QV8ObjectResource
109 V8_RESOURCE_TYPE(SignalHandlerType)
111 QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index);
113 QQmlGuard<QObject> object;
119 template<typename A, typename B, typename C, typename D, typename E,
120 typename F, typename G, typename H>
122 template<typename Z, typename X>
124 char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
127 static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >);
130 struct CallArgument {
131 inline CallArgument();
132 inline ~CallArgument();
133 inline void *dataPtr();
135 inline void initAsType(int type);
136 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
137 inline v8::Handle<v8::Value> toValue(QV8Engine *);
140 CallArgument(const CallArgument &);
142 inline void cleanup();
151 char allocData[MaxSizeOf8<QVariant,
159 qint64 q_for_alignment;
162 // Pointers to allocData
165 QVariant *qvariantPtr;
166 QList<QObject *> *qlistPtr;
167 QJSValue *qjsValuePtr;
168 QQmlV8Handle *handlePtr;
169 QJsonArray *jsonArrayPtr;
170 QJsonObject *jsonObjectPtr;
171 QJsonValue *jsonValuePtr;
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);
217 QIntrusiveList<QV8QObjectResource, &QV8QObjectResource::weakResource>::iterator i = m_javaScriptOwnedWeakQObjects.begin();
218 for (; i != m_javaScriptOwnedWeakQObjects.end(); ++i) {
219 QV8QObjectResource *resource = *i;
221 deleteWeakQObject(resource, true);
225 struct ReadAccessor {
226 static inline void Indirect(QObject *object, const QQmlPropertyData &property,
227 void *output, QQmlNotifier **n)
232 void *args[] = { output, 0 };
233 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
236 static inline void Direct(QObject *object, const QQmlPropertyData &property,
237 void *output, QQmlNotifier **n)
242 void *args[] = { output, 0 };
243 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
246 static inline void Accessor(QObject *object, const QQmlPropertyData &property,
247 void *output, QQmlNotifier **n)
249 Q_ASSERT(property.accessors);
251 property.accessors->read(object, property.accessorData, output);
252 if (n) property.accessors->notifier(object, property.accessorData, n);
256 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, int v)
257 { return v8::Integer::New(v); }
258 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, uint v)
259 { return v8::Integer::NewFromUnsigned(v); }
260 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, bool v)
261 { return v8::Boolean::New(v); }
262 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, const QString &v)
263 { return e->toString(v); }
264 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, float v)
265 { return v8::Number::New(v); }
266 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, double v)
267 { return v8::Number::New(v); }
268 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, QObject *v)
269 { return e->newQObject(v); }
271 template<typename T, void (*ReadFunction)(QObject *, const QQmlPropertyData &,
272 void *, QQmlNotifier **)>
273 static v8::Handle<v8::Value> GenericValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info)
275 v8::Handle<v8::Object> This = info.This();
276 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This);
278 QObject *object = resource->object;
279 if (QQmlData::wasDeleted(object)) return v8::Undefined();
281 QQmlPropertyData *property =
282 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
284 QQmlEngine *engine = resource->engine->engine();
285 QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0;
289 if (ep && ep->propertyCapture) {
290 if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) {
291 QQmlNotifier *notifier = 0;
292 ReadFunction(object, *property, &value, ¬ifier);
293 if (notifier) ep->captureProperty(notifier);
294 } else if (!property->isConstant()) {
295 ep->captureProperty(object, property->coreIndex, property->notifyIndex);
296 ReadFunction(object, *property, &value, 0);
298 ReadFunction(object, *property, &value, 0);
301 ReadFunction(object, *property, &value, 0);
304 return valueToHandle(resource->engine, value);
307 #define FAST_GETTER_FUNCTION(property, cpptype) \
308 (property->hasAccessors()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Accessor>):(property->isDirect()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Direct>):((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Indirect>)))
310 static quint32 toStringHash = quint32(-1);
311 static quint32 destroyHash = quint32(-1);
313 void QV8QObjectWrapper::init(QV8Engine *engine)
317 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
318 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
319 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
321 m_toStringString = QHashedV8String(m_toStringSymbol);
322 m_destroyString = QHashedV8String(m_destroySymbol);
324 toStringHash = m_toStringString.hash();
325 destroyHash = m_destroyString.hash();
328 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
329 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
330 ft->InstanceTemplate()->SetHasExternalResource(true);
331 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
334 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
335 #define CREATE_FUNCTION_SOURCE \
336 "(function(method) { "\
337 "return (function(object, data, qmlglobal) { "\
338 "return (function() { "\
339 "return method(object, data, qmlglobal, arguments.length, arguments); "\
343 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION_SOURCE), &origin, 0,
344 v8::Handle<v8::String>(), v8::Script::NativeMode);
345 #undef CREATE_FUNCTION_SOURCE
346 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
347 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
348 v8::Handle<v8::Value> args[] = { invokeFn };
349 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
350 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
353 v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine);
354 v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine);
357 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
358 ft->InstanceTemplate()->SetHasExternalResource(true);
359 ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum);
360 ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
361 m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
365 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
366 prototype->Set(v8::String::New("connect"), connect, v8::DontEnum);
367 prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
371 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
373 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
376 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
378 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
379 return r?r->object:0;
382 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
383 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
385 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
386 return static_cast<QV8QObjectResource *>(r)->object;
389 // Load value properties
390 template<void (*ReadFunction)(QObject *, const QQmlPropertyData &,
391 void *, QQmlNotifier **)>
392 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
393 const QQmlPropertyData &property,
394 QQmlNotifier **notifier)
396 Q_ASSERT(!property.isFunction());
398 if (property.isQObject()) {
400 ReadFunction(object, property, &rv, notifier);
401 return engine->newQObject(rv);
402 } else if (property.isQList()) {
403 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
404 } else if (property.propType == QMetaType::QReal) {
406 ReadFunction(object, property, &v, notifier);
407 return valueToHandle(engine, v);
408 } else if (property.propType == QMetaType::Int || property.isEnum()) {
410 ReadFunction(object, property, &v, notifier);
411 return valueToHandle(engine, v);
412 } else if (property.propType == QMetaType::Bool) {
414 ReadFunction(object, property, &v, notifier);
415 return valueToHandle(engine, v);
416 } else if (property.propType == QMetaType::QString) {
418 ReadFunction(object, property, &v, notifier);
419 return valueToHandle(engine, v);
420 } else if (property.propType == QMetaType::UInt) {
422 ReadFunction(object, property, &v, notifier);
423 return valueToHandle(engine, v);
424 } else if (property.propType == QMetaType::Float) {
426 ReadFunction(object, property, &v, notifier);
427 return valueToHandle(engine, v);
428 } else if (property.propType == QMetaType::Double) {
430 ReadFunction(object, property, &v, notifier);
431 return valueToHandle(engine, v);
432 } else if (property.isV8Handle()) {
434 ReadFunction(object, property, &handle, notifier);
435 return handle.toHandle();
436 } else if (property.propType == qMetaTypeId<QJSValue>()) {
438 ReadFunction(object, property, &v, notifier);
439 return QJSValuePrivate::get(v)->asV8Value(engine);
440 } else if (property.isQVariant()) {
442 ReadFunction(object, property, &v, notifier);
443 return engine->fromVariant(v);
444 } else if (QQmlValueTypeFactory::isValueType((uint)property.propType)
445 && engine->engine()) {
446 Q_ASSERT(notifier == 0);
448 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
449 QQmlValueType *valueType = ep->valueTypes[property.propType];
451 return engine->newValueType(object, property.coreIndex, valueType);
453 Q_ASSERT(notifier == 0);
455 // see if it's a sequence type
456 bool succeeded = false;
457 v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex,
463 if (property.propType == QMetaType::UnknownType) {
464 QMetaProperty p = object->metaObject()->property(property.coreIndex);
465 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
466 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
467 return v8::Undefined();
469 QVariant v(property.propType, (void *)0);
470 ReadFunction(object, property, v.data(), notifier);
471 return engine->fromVariant(v);
475 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
476 v8::Handle<v8::Value> *objectHandle,
477 const QHashedV8String &property,
478 QV8QObjectWrapper::RevisionMode revisionMode)
480 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
481 // will be a faster way of creating QObject method objects.
482 struct MethodClosure {
483 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
484 v8::Handle<v8::Value> *objectHandle,
486 v8::Handle<v8::Value> argv[] = {
487 objectHandle?*objectHandle:engine->newQObject(object),
488 v8::Integer::New(index)
490 Q_ASSERT(argv[0]->IsObject());
491 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
493 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
494 v8::Handle<v8::Value> *objectHandle,
496 v8::Handle<v8::Value> argv[] = {
497 objectHandle?*objectHandle:engine->newQObject(object),
498 v8::Integer::New(index),
499 v8::Context::GetCallingQmlGlobal()
501 Q_ASSERT(argv[0]->IsObject());
502 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
506 if (QQmlData::wasDeleted(object))
507 return v8::Handle<v8::Value>();
510 // Comparing the hash first actually makes a measurable difference here, at least on x86
511 quint32 hash = property.hash();
512 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
513 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
514 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
515 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
519 QQmlPropertyData local;
520 QQmlPropertyData *result = 0;
522 QQmlData *ddata = QQmlData::get(object, false);
523 if (ddata && ddata->propertyCache)
524 result = ddata->propertyCache->property(property);
526 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
530 return v8::Handle<v8::Value>();
532 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
533 QQmlData *ddata = QQmlData::get(object);
534 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
535 return v8::Handle<v8::Value>();
538 if (result->isFunction() && !result->isVarProperty()) {
539 if (result->isVMEFunction()) {
540 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
542 return vmemo->vmeMethod(result->coreIndex);
543 } else if (result->isV8Function()) {
544 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
545 } else if (result->isSignalHandler()) {
546 v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
547 QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
548 handler->SetExternalResource(r);
551 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
555 QQmlEnginePrivate *ep =
556 engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
558 if (result->hasAccessors()) {
560 QQmlNotifier **nptr = 0;
562 if (ep && ep->propertyCapture && result->accessors->notifier)
565 v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
567 if (result->accessors->notifier) {
568 if (n) ep->captureProperty(n);
570 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
576 if (ep && !result->isConstant())
577 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
579 if (result->isVarProperty()) {
580 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
582 return vmemo->vmeProperty(result->coreIndex);
583 } else if (result->isDirect()) {
584 return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
586 return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
590 // Setter for writable properties. Shared between the interceptor and fast property accessor
591 static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
592 v8::Handle<v8::Value> value)
594 QQmlBinding *newBinding = 0;
595 if (value->IsFunction()) {
596 if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
597 if (!property->isVarProperty() && property->propType != qMetaTypeId<QJSValue>()) {
598 // assigning a JS function to a non var or QJSValue property or is not allowed.
599 QString error = QLatin1String("Cannot assign JavaScript function to ");
600 if (!QMetaType::typeName(property->propType))
601 error += QLatin1String("[unknown property type]");
603 error += QLatin1String(QMetaType::typeName(property->propType));
604 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
608 // binding assignment.
609 QQmlContextData *context = engine->callingContext();
610 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
612 v8::Local<v8::StackTrace> trace =
613 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
614 v8::StackTrace::kScriptName));
615 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
616 int lineNumber = frame->GetLineNumber();
617 int columnNumber = frame->GetColumn();
618 QString url = engine->toString(frame->GetScriptName());
620 newBinding = new QQmlBinding(&function, object, context, url, lineNumber, columnNumber);
621 newBinding->setTarget(object, *property, context);
622 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
623 QQmlBinding::RequiresThisObject);
627 QQmlAbstractBinding *oldBinding =
628 QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
630 oldBinding->destroy();
632 if (!newBinding && property->isVarProperty()) {
633 // allow assignment of "special" values (null, undefined, function) to var properties
634 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
636 vmemo->setVMEProperty(property->coreIndex, value);
640 #define PROPERTY_STORE(cpptype, value) \
644 void *argv[] = { &o, 0, &status, &flags }; \
645 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
647 if (value->IsNull() && property->isQObject()) {
648 PROPERTY_STORE(QObject*, 0);
649 } else if (value->IsUndefined() && property->isResettable()) {
651 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
652 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
653 PROPERTY_STORE(QVariant, QVariant());
654 } else if (value->IsUndefined() && property->propType == QMetaType::QJsonValue) {
655 PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
656 } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) {
657 PROPERTY_STORE(QJSValue, engine->scriptValueFromInternal(value));
658 } else if (value->IsUndefined()) {
659 QString error = QLatin1String("Cannot assign [undefined] to ");
660 if (!QMetaType::typeName(property->propType))
661 error += QLatin1String("[unknown property type]");
663 error += QLatin1String(QMetaType::typeName(property->propType));
664 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
665 } else if (value->IsFunction()) {
666 // this is handled by the binding creation above
667 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
668 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
669 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
670 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
671 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
672 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
673 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
674 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
675 } else if (property->propType == QMetaType::QString && value->IsString()) {
676 PROPERTY_STORE(QString, engine->toString(value->ToString()));
677 } else if (property->isVarProperty()) {
678 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
680 vmemo->setVMEProperty(property->coreIndex, value);
683 if (property->isQList())
684 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
686 v = engine->toVariant(value, property->propType);
688 QQmlContextData *context = engine->callingContext();
689 if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
690 const char *valueType = 0;
691 if (v.userType() == QVariant::Invalid) valueType = "null";
692 else valueType = QMetaType::typeName(v.userType());
694 const char *targetTypeName = QMetaType::typeName(property->propType);
696 targetTypeName = "an unregistered type";
698 QString error = QLatin1String("Cannot assign ") +
699 QLatin1String(valueType) +
700 QLatin1String(" to ") +
701 QLatin1String(targetTypeName);
702 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
707 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
708 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
710 if (engine->qobjectWrapper()->m_toStringString == property ||
711 engine->qobjectWrapper()->m_destroyString == property)
714 if (QQmlData::wasDeleted(object))
717 QQmlPropertyData local;
718 QQmlPropertyData *result = 0;
719 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
724 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
725 QQmlData *ddata = QQmlData::get(object);
726 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
730 if (!result->isWritable() && !result->isQList()) {
731 QString error = QLatin1String("Cannot assign to read-only property \"") +
732 engine->toString(property.string()) + QLatin1Char('\"');
733 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
737 StoreProperty(engine, object, result, value);
742 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
743 const v8::AccessorInfo &info)
745 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
747 if (QQmlData::wasDeleted(resource->object))
748 return v8::Handle<v8::Value>();
750 QObject *object = resource->object;
752 QHashedV8String propertystring(property);
754 QV8Engine *v8engine = resource->engine;
755 v8::Handle<v8::Value> This = info.This();
756 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
757 QV8QObjectWrapper::IgnoreRevision);
758 if (!result.IsEmpty())
761 if (QV8Engine::startsWithUpper(property)) {
762 // Check for attached properties
763 QQmlContextData *context = v8engine->callingContext();
765 if (context && context->imports) {
766 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
769 if (r.scriptIndex != -1) {
770 return v8::Undefined();
772 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
773 } else if (r.importNamespace) {
774 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
775 QV8TypeWrapper::ExcludeEnums);
777 Q_ASSERT(!"Unreachable");
782 return v8::Handle<v8::Value>();
785 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
786 v8::Local<v8::Value> value,
787 const v8::AccessorInfo &info)
789 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
791 if (QQmlData::wasDeleted(resource->object))
794 QObject *object = resource->object;
796 QHashedV8String propertystring(property);
798 QV8Engine *v8engine = resource->engine;
799 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
802 QString error = QLatin1String("Cannot assign to non-existent property \"") +
803 v8engine->toString(property) + QLatin1Char('\"');
804 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
811 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
812 const v8::AccessorInfo &info)
814 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
816 if (resource->object.isNull())
817 return v8::Handle<v8::Integer>();
819 QV8Engine *engine = resource->engine;
820 QObject *object = resource->object;
822 QHashedV8String propertystring(property);
824 QQmlPropertyData local;
825 QQmlPropertyData *result = 0;
826 result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local);
829 return v8::Handle<v8::Integer>();
830 else if (!result->isWritable() && !result->isQList())
831 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
833 return v8::Integer::New(v8::DontDelete);
836 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
838 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
840 if (resource->object.isNull())
841 return v8::Array::New();
843 QObject *object = resource->object;
847 QQmlEnginePrivate *ep = resource->engine->engine()
848 ? QQmlEnginePrivate::get(resource->engine->engine())
851 QQmlPropertyCache *cache = 0;
852 QQmlData *ddata = QQmlData::get(object);
854 cache = ddata->propertyCache;
857 cache = ep ? ep->cache(object) : 0;
859 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
861 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
862 const QMetaObject *mo = object->metaObject();
863 int pc = mo->propertyCount();
864 int po = mo->propertyOffset();
865 for (int i=po; i<pc; ++i)
866 result << QString::fromUtf8(mo->property(i).name());
869 result = cache->propertyNames();
872 v8::Local<v8::Array> rv = v8::Array::New(result.count());
874 for (int ii = 0; ii < result.count(); ++ii)
875 rv->Set(ii, resource->engine->toString(result.at(ii)));
880 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
881 const v8::AccessorInfo& info)
883 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
885 if (QQmlData::wasDeleted(resource->object))
888 QObject *object = resource->object;
890 QQmlPropertyData *property =
891 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
893 int index = property->coreIndex;
895 QQmlData *ddata = QQmlData::get(object, false);
897 Q_ASSERT(ddata->propertyCache);
899 QQmlPropertyData *pdata = ddata->propertyCache->property(index);
902 Q_ASSERT(pdata->isWritable() || pdata->isQList());
904 StoreProperty(resource->engine, object, pdata, value);
907 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
908 const v8::AccessorInfo& info)
910 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
912 if (QQmlData::wasDeleted(resource->object))
915 QV8Engine *v8engine = resource->engine;
917 QString error = QLatin1String("Cannot assign to read-only property \"") +
918 v8engine->toString(property) + QLatin1Char('\"');
919 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
922 void QV8QObjectWrapper::WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *wrapper)
924 Q_ASSERT(handle->IsObject());
925 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
928 static_cast<QV8QObjectWrapper*>(wrapper)->unregisterWeakQObjectReference(resource);
929 if (static_cast<QV8QObjectWrapper*>(wrapper)->deleteWeakQObject(resource, false)) {
930 qPersistentDispose(handle); // dispose.
932 handle.MakeWeak(0, WeakQObjectReferenceCallback); // revive.
936 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
938 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
939 instance->v8object.Clear();
940 qPersistentDispose(handle);
943 v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
946 Q_ASSERT(this->engine);
948 Q_ASSERT(QQmlData::get(object, false));
949 Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
952 if (constructor.IsEmpty()) {
953 v8::Local<v8::FunctionTemplate> ft;
955 QString toString = QLatin1String("toString");
956 QString destroy = QLatin1String("destroy");
958 // As we use hash linking, it is possible that iterating over the values can give duplicates.
959 // To combat this, we must unique'ify our properties.
960 StringCache uniqueHash;
961 if (stringCache.isLinked())
962 uniqueHash.reserve(stringCache.count());
964 // XXX TODO: Enables fast property accessors. These more than double the property access
965 // performance, but the cost of setting up this structure hasn't been measured so
966 // its not guarenteed that this is a win overall. We need to try and measure the cost.
967 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
968 if (stringCache.isLinked()) {
969 if (uniqueHash.contains(iter))
971 uniqueHash.insert(iter);
974 QQmlPropertyData *property = *iter;
975 if (property->notFullyResolved()) resolve(property);
977 if (property->isFunction())
980 v8::AccessorGetter fastgetter = 0;
981 v8::AccessorSetter fastsetter = FastValueSetter;
982 if (!property->isWritable())
983 fastsetter = FastValueSetterReadOnly;
985 if (property->isQObject())
986 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
987 else if (property->propType == QMetaType::Int || property->isEnum())
988 fastgetter = FAST_GETTER_FUNCTION(property, int);
989 else if (property->propType == QMetaType::Bool)
990 fastgetter = FAST_GETTER_FUNCTION(property, bool);
991 else if (property->propType == QMetaType::QString)
992 fastgetter = FAST_GETTER_FUNCTION(property, QString);
993 else if (property->propType == QMetaType::UInt)
994 fastgetter = FAST_GETTER_FUNCTION(property, uint);
995 else if (property->propType == QMetaType::Float)
996 fastgetter = FAST_GETTER_FUNCTION(property, float);
997 else if (property->propType == QMetaType::Double)
998 fastgetter = FAST_GETTER_FUNCTION(property, double);
1001 QString name = iter.key();
1002 if (name == toString || name == destroy)
1006 ft = v8::FunctionTemplate::New();
1007 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1008 QV8QObjectWrapper::Setter,
1009 QV8QObjectWrapper::Query,
1011 QV8QObjectWrapper::Enumerator);
1012 ft->InstanceTemplate()->SetHasExternalResource(true);
1015 // We wrap the raw QQmlPropertyData pointer here. This is safe as the
1016 // pointer will remain valid at least as long as the lifetime of any QObject's of
1017 // this type and the property accessor checks if the object is 0 (deleted) before
1018 // dereferencing the pointer.
1019 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
1020 v8::External::Wrap(property));
1025 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
1027 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1028 QV8QObjectWrapper::Setter,
1029 QV8QObjectWrapper::Query,
1031 QV8QObjectWrapper::Enumerator);
1032 ft->InstanceTemplate()->SetHasExternalResource(true);
1033 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
1036 QQmlCleanup::addToEngine(this->engine);
1039 v8::Local<v8::Object> result = constructor->NewInstance();
1040 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1041 result->SetExternalResource(r);
1045 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
1047 v8::Local<v8::Object> rv;
1049 if (!ddata->propertyCache && engine->engine()) {
1050 ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
1051 if (ddata->propertyCache) ddata->propertyCache->addref();
1054 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1055 rv = ddata->propertyCache->newQObject(object, engine);
1057 // XXX NewInstance() should be optimized
1058 rv = m_constructor->NewInstance();
1059 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1060 rv->SetExternalResource(r);
1067 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1068 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
1070 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
1071 persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that
1072 "owns" this handle by setting the QQmlData::v8objectid to the id of this
1074 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create
1075 an entry in the m_taintedObject hash where we store the handle and mark the object as
1076 "tainted" in the QQmlData::hasTaintedV8Object flag.
1077 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1078 in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has
1079 released the handle.
1081 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1083 if (QQmlData::wasDeleted(object))
1086 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(this, WeakQObjectReferenceCallback);
1101 ddata->v8objectid = m_id;
1102 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1103 registerWeakQObjectReference(resource);
1107 // If this object is tainted, we have to check to see if it is in our
1108 // tainted object list
1109 TaintedHash::Iterator iter =
1110 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1111 bool found = iter != m_taintedObjects.end();
1113 // If our tainted handle doesn't exist or has been collected, and there isn't
1114 // a handle in the ddata, we can assume ownership of the ddata->v8object
1115 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1116 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1117 ddata->v8object = qPersistentNew<v8::Object>(rv);
1118 ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1119 ddata->v8objectid = m_id;
1120 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1121 registerWeakQObjectReference(resource);
1125 m_taintedObjects.erase(iter);
1129 } else if (!found) {
1130 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1131 iter = m_taintedObjects.insert(object, instance);
1132 ddata->hasTaintedV8Object = true;
1135 if ((*iter)->v8object.IsEmpty()) {
1136 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1137 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1138 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1141 return v8::Local<v8::Object>::New((*iter)->v8object);
1145 // returns true if the object's qqmldata v8object handle should
1146 // be disposed by the caller, false if it should not be (due to
1147 // creation status, etc).
1148 bool QV8QObjectWrapper::deleteWeakQObject(QV8QObjectResource *resource, bool calledFromEngineDtor)
1150 QObject *object = resource->object;
1152 QQmlData *ddata = QQmlData::get(object, false);
1154 if (!calledFromEngineDtor && ddata->rootObjectInCreation) {
1155 // if weak ref callback is triggered (by gc) for a root object
1156 // prior to completion of creation, we should NOT delete it.
1160 ddata->v8object.Clear();
1161 if (!object->parent() && !ddata->indestructible) {
1162 // This object is notionally destroyed now
1163 if (ddata->ownContext && ddata->context)
1164 ddata->context->emitDestruction();
1165 ddata->isQueuedForDeletion = true;
1166 object->deleteLater();
1174 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1176 if (object->IsFunction())
1177 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1179 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1180 return qMakePair(resource->object.data(), resource->index);
1182 return qMakePair((QObject *)0, -1);
1185 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1187 v8::ScriptOrigin origin = function->GetScriptOrigin();
1188 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1190 // This is one of our special QObject method wrappers
1191 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1192 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1194 if (data->IsArray()) {
1195 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1196 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1199 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1202 return qMakePair((QObject *)0, -1);
1205 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1208 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1209 ~QV8QObjectConnectionList();
1213 : needsDestroy(false) {}
1214 Connection(const Connection &other)
1215 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1216 Connection &operator=(const Connection &other) {
1217 thisObject = other.thisObject;
1218 function = other.function;
1219 needsDestroy = other.needsDestroy;
1223 v8::Persistent<v8::Object> thisObject;
1224 v8::Persistent<v8::Function> function;
1227 qPersistentDispose(thisObject);
1228 qPersistentDispose(function);
1234 struct ConnectionList : public QList<Connection> {
1235 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1236 int connectionsInUse;
1237 bool connectionsNeedClean;
1242 typedef QHash<int, ConnectionList> SlotHash;
1247 virtual void objectDestroyed(QObject *);
1248 virtual int qt_metacall(QMetaObject::Call, int, void **);
1251 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1252 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1256 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1258 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1259 QList<Connection> &connections = *iter;
1260 for (int ii = 0; ii < connections.count(); ++ii) {
1261 qPersistentDispose(connections[ii].thisObject);
1262 qPersistentDispose(connections[ii].function);
1268 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1270 engine->qobjectWrapper()->m_connections.remove(object);
1273 needsDestroy = true;
1278 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1280 if (method == QMetaObject::InvokeMetaMethod) {
1281 SlotHash::Iterator iter = slotHash.find(index);
1282 if (iter == slotHash.end())
1284 ConnectionList &connectionList = *iter;
1285 if (connectionList.isEmpty())
1290 connectionList.connectionsInUse++;
1292 QList<Connection> connections = connectionList;
1294 QVarLengthArray<int, 9> dummy;
1295 int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1297 v8::HandleScope handle_scope;
1298 v8::Context::Scope scope(engine->context());
1300 int argCount = argsTypes?argsTypes[0]:0;
1301 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1303 for (int ii = 0; ii < argCount; ++ii) {
1304 int type = argsTypes[ii + 1];
1305 if (type == qMetaTypeId<QVariant>()) {
1306 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1308 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1312 for (int ii = 0; ii < connections.count(); ++ii) {
1313 Connection &connection = connections[ii];
1314 if (connection.needsDestroy)
1317 v8::TryCatch try_catch;
1318 if (connection.thisObject.IsEmpty()) {
1319 connection.function->Call(engine->global(), argCount, args.data());
1321 connection.function->Call(connection.thisObject, argCount, args.data());
1324 if (try_catch.HasCaught()) {
1326 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1327 v8::Local<v8::Message> message = try_catch.Message();
1328 if (!message.IsEmpty())
1329 QQmlExpressionPrivate::exceptionToError(message, error);
1330 QQmlEnginePrivate::get(engine->engine())->warning(error);
1334 connectionList.connectionsInUse--;
1335 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1336 for (QList<Connection>::Iterator iter = connectionList.begin();
1337 iter != connectionList.end(); ) {
1338 if (iter->needsDestroy) {
1340 iter = connectionList.erase(iter);
1348 if (inUse == 0 && needsDestroy)
1355 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1357 if (args.Length() == 0)
1358 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1360 QV8Engine *engine = V8ENGINE();
1362 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1363 QObject *signalObject = signalInfo.first;
1364 int signalIndex = signalInfo.second;
1366 if (signalIndex == -1)
1367 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1370 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1372 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1373 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1375 v8::Local<v8::Value> functionValue;
1376 v8::Local<v8::Value> functionThisValue;
1378 if (args.Length() == 1) {
1379 functionValue = args[0];
1381 functionThisValue = args[0];
1382 functionValue = args[1];
1385 if (!functionValue->IsFunction())
1386 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1388 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1389 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1391 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1392 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1393 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1394 if (iter == connections.end())
1395 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1397 QV8QObjectConnectionList *connectionList = *iter;
1398 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1399 if (slotIter == connectionList->slotHash.end()) {
1400 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1401 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1404 QV8QObjectConnectionList::Connection connection;
1405 if (!functionThisValue.IsEmpty())
1406 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1407 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1409 slotIter->append(connection);
1411 return v8::Undefined();
1414 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1416 if (args.Length() == 0)
1417 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1419 QV8Engine *engine = V8ENGINE();
1421 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1422 QObject *signalObject = signalInfo.first;
1423 int signalIndex = signalInfo.second;
1425 if (signalIndex == -1)
1426 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1429 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1431 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1432 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1434 v8::Local<v8::Value> functionValue;
1435 v8::Local<v8::Value> functionThisValue;
1437 if (args.Length() == 1) {
1438 functionValue = args[0];
1440 functionThisValue = args[0];
1441 functionValue = args[1];
1444 if (!functionValue->IsFunction())
1445 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1447 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1448 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1450 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1451 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1452 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1453 if (iter == connectionsList.end())
1454 return v8::Undefined(); // Nothing to disconnect from
1456 QV8QObjectConnectionList *connectionList = *iter;
1457 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1458 if (slotIter == connectionList->slotHash.end())
1459 return v8::Undefined(); // Nothing to disconnect from
1461 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1463 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1464 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1466 if (functionData.second != -1) {
1467 // This is a QObject function wrapper
1468 for (int ii = 0; ii < connections.count(); ++ii) {
1469 QV8QObjectConnectionList::Connection &connection = connections[ii];
1471 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1472 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1474 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1475 if (connectedFunctionData == functionData) {
1477 if (connections.connectionsInUse) {
1478 connection.needsDestroy = true;
1479 connections.connectionsNeedClean = true;
1481 connection.dispose();
1482 connections.removeAt(ii);
1484 return v8::Undefined();
1490 // This is a normal JS function
1491 for (int ii = 0; ii < connections.count(); ++ii) {
1492 QV8QObjectConnectionList::Connection &connection = connections[ii];
1493 if (connection.function->StrictEquals(function) &&
1494 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1495 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1497 if (connections.connectionsInUse) {
1498 connection.needsDestroy = true;
1499 connections.connectionsNeedClean = true;
1501 connection.dispose();
1502 connections.removeAt(ii);
1504 return v8::Undefined();
1509 return v8::Undefined();
1513 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1515 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1517 Only searches for real properties of \a object (including methods), not attached properties etc.
1521 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1523 Set the \a property of \a object to \a value.
1525 Returns true if the property was "set" - even if this results in an exception being thrown -
1526 and false if the object has no such property.
1528 Only searches for real properties of \a object (including methods), not attached properties etc.
1534 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1535 int Length() const { return _length; }
1536 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1540 v8::Handle<v8::Object> *_args;
1544 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1545 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1549 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1550 args[0].initAsType(returnType);
1552 for (int ii = 0; ii < argCount; ++ii)
1553 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1555 QVarLengthArray<void *, 9> argData(args.count());
1556 for (int ii = 0; ii < args.count(); ++ii)
1557 argData[ii] = args[ii].dataPtr();
1559 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1561 return args[0].toValue(engine);
1563 } else if (returnType != QMetaType::Void) {
1566 arg.initAsType(returnType);
1568 void *args[] = { arg.dataPtr() };
1570 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1572 return arg.toValue(engine);
1576 void *args[] = { 0 };
1577 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1578 return v8::Undefined();
1584 Returns the match score for converting \a actual to be of type \a conversionType. A
1585 zero score means "perfect match" whereas a higher score is worse.
1587 The conversion table is copied out of the QtScript callQtMethod() function.
1589 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1591 if (actual->IsNumber()) {
1592 switch (conversionType) {
1593 case QMetaType::Double:
1595 case QMetaType::Float:
1597 case QMetaType::LongLong:
1598 case QMetaType::ULongLong:
1600 case QMetaType::Long:
1601 case QMetaType::ULong:
1603 case QMetaType::Int:
1604 case QMetaType::UInt:
1606 case QMetaType::Short:
1607 case QMetaType::UShort:
1610 case QMetaType::Char:
1611 case QMetaType::UChar:
1613 case QMetaType::QJsonValue:
1618 } else if (actual->IsString()) {
1619 switch (conversionType) {
1620 case QMetaType::QString:
1622 case QMetaType::QJsonValue:
1627 } else if (actual->IsBoolean()) {
1628 switch (conversionType) {
1629 case QMetaType::Bool:
1631 case QMetaType::QJsonValue:
1636 } else if (actual->IsDate()) {
1637 switch (conversionType) {
1638 case QMetaType::QDateTime:
1640 case QMetaType::QDate:
1642 case QMetaType::QTime:
1647 } else if (actual->IsRegExp()) {
1648 switch (conversionType) {
1649 case QMetaType::QRegExp:
1654 } else if (actual->IsArray()) {
1655 switch (conversionType) {
1656 case QMetaType::QJsonArray:
1658 case QMetaType::QStringList:
1659 case QMetaType::QVariantList:
1664 } else if (actual->IsNull()) {
1665 switch (conversionType) {
1666 case QMetaType::VoidStar:
1667 case QMetaType::QObjectStar:
1668 case QMetaType::QJsonValue:
1671 const char *typeName = QMetaType::typeName(conversionType);
1672 if (typeName && typeName[strlen(typeName) - 1] == '*')
1678 } else if (actual->IsObject()) {
1679 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1681 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1682 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1683 switch (conversionType) {
1684 case QMetaType::QObjectStar:
1689 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1690 if (conversionType == qMetaTypeId<QVariant>())
1692 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1696 } else if (conversionType == QMetaType::QJsonObject) {
1707 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1713 int classInfoCount, classInfoData;
1714 int methodCount, methodData;
1717 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1721 Returns the next related method, if one, or 0.
1723 static const QQmlPropertyData * RelatedMethod(QObject *object,
1724 const QQmlPropertyData *current,
1725 QQmlPropertyData &dummy)
1727 QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1728 if (!current->isOverload())
1731 Q_ASSERT(!current->overrideIndexIsProperty);
1734 return cache->method(current->overrideIndex);
1736 const QMetaObject *mo = object->metaObject();
1737 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1739 while (methodOffset > current->overrideIndex) {
1740 mo = mo->superClass();
1741 methodOffset -= QMetaObject_methods(mo);
1744 QMetaMethod method = mo->method(current->overrideIndex);
1747 // Look for overloaded methods
1748 QByteArray methodName = method.name();
1749 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1750 if (methodName == mo->method(ii).name()) {
1751 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1752 dummy.overrideIndexIsProperty = 0;
1753 dummy.overrideIndex = ii;
1762 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1763 QV8Engine *engine, CallArgs &callArgs)
1765 if (data.hasArguments()) {
1768 QVarLengthArray<int, 9> dummy;
1769 QByteArray unknownTypeError;
1771 args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1775 QString typeName = QString::fromLatin1(unknownTypeError);
1776 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1777 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1778 return v8::Handle<v8::Value>();
1781 if (args[0] > callArgs.Length()) {
1782 QString error = QLatin1String("Insufficient arguments");
1783 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1784 return v8::Handle<v8::Value>();
1787 return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1791 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1797 Resolve the overloaded method to call. The algorithm works conceptually like this:
1798 1. Resolve the set of overloads it is *possible* to call.
1799 Impossible overloads include those that have too many parameters or have parameters
1801 2. Filter the set of overloads to only contain those with the closest number of
1803 For example, if we are called with 3 parameters and there are 2 overloads that
1804 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1805 3. Find the best remaining overload based on its match score.
1806 If two or more overloads have the same match score, call the last one. The match
1807 score is constructed by adding the matchScore() result for each of the parameters.
1809 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1810 QV8Engine *engine, CallArgs &callArgs)
1812 int argumentCount = callArgs.Length();
1814 const QQmlPropertyData *best = 0;
1815 int bestParameterScore = INT_MAX;
1816 int bestMatchScore = INT_MAX;
1818 QQmlPropertyData dummy;
1819 const QQmlPropertyData *attempt = &data;
1822 QVarLengthArray<int, 9> dummy;
1823 int methodArgumentCount = 0;
1824 int *methodArgTypes = 0;
1825 if (attempt->hasArguments()) {
1826 typedef QQmlPropertyCache PC;
1827 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1828 if (!args) // Must be an unknown argument
1831 methodArgumentCount = args[0];
1832 methodArgTypes = args + 1;
1835 if (methodArgumentCount > argumentCount)
1836 continue; // We don't have sufficient arguments to call this method
1838 int methodParameterScore = argumentCount - methodArgumentCount;
1839 if (methodParameterScore > bestParameterScore)
1840 continue; // We already have a better option
1842 int methodMatchScore = 0;
1843 for (int ii = 0; ii < methodArgumentCount; ++ii)
1844 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1846 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1848 bestParameterScore = methodParameterScore;
1849 bestMatchScore = methodMatchScore;
1852 if (bestParameterScore == 0 && bestMatchScore == 0)
1853 break; // We can't get better than that
1855 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1858 return CallPrecise(object, *best, engine, callArgs);
1860 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1861 const QQmlPropertyData *candidate = &data;
1863 error += QLatin1String("\n ") +
1864 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData());
1865 candidate = RelatedMethod(object, candidate, dummy);
1868 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1869 return v8::Handle<v8::Value>();
1873 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1877 QString objectName = object->objectName();
1879 result += QString::fromUtf8(object->metaObject()->className());
1880 result += QLatin1String("(0x");
1881 result += QString::number((quintptr)object,16);
1883 if (!objectName.isEmpty()) {
1884 result += QLatin1String(", \"");
1885 result += objectName;
1886 result += QLatin1Char('\"');
1889 result += QLatin1Char(')');
1891 result = QLatin1String("null");
1894 return engine->toString(result);
1897 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1899 QQmlData *ddata = QQmlData::get(object, false);
1900 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation) {
1901 const char *error = "Invalid attempt to destroy() an indestructible object";
1902 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1903 return v8::Undefined();
1908 delay = args->Get(0)->Uint32Value();
1911 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1913 object->deleteLater();
1915 return v8::Undefined();
1918 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1920 // object, index, qmlglobal, argCount, args
1921 Q_ASSERT(args.Length() == 5);
1922 Q_ASSERT(args[0]->IsObject());
1924 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1927 return v8::Undefined();
1929 int argCount = args[3]->Int32Value();
1930 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1932 // Special hack to return info about this closure.
1933 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1934 v8::Local<v8::Array> data = v8::Array::New(2);
1935 data->Set(0, args[0]);
1936 data->Set(1, args[1]);
1940 QObject *object = resource->object;
1941 int index = args[1]->Int32Value();
1944 return v8::Undefined();
1947 // Builtin functions
1948 if (index == QOBJECT_TOSTRING_INDEX) {
1949 return ToString(resource->engine, object, argCount, arguments);
1950 } else if (index == QOBJECT_DESTROY_INDEX) {
1951 return Destroy(resource->engine, object, argCount, arguments);
1953 return v8::Undefined();
1957 QQmlPropertyData method;
1959 if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
1960 if (ddata->propertyCache) {
1961 QQmlPropertyData *d = ddata->propertyCache->method(index);
1963 return v8::Undefined();
1968 if (method.coreIndex == -1) {
1969 method.load(object->metaObject()->method(index));
1971 if (method.coreIndex == -1)
1972 return v8::Undefined();
1975 if (method.isV8Function()) {
1976 v8::Handle<v8::Value> rv;
1977 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1979 QQmlV8Function func(argCount, arguments, rv, qmlglobal,
1980 resource->engine->contextWrapper()->context(qmlglobal),
1982 QQmlV8Function *funcptr = &func;
1984 void *args[] = { 0, &funcptr };
1985 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1987 if (rv.IsEmpty()) return v8::Undefined();
1991 CallArgs callArgs(argCount, &arguments);
1992 if (!method.isOverload()) {
1993 return CallPrecise(object, method, resource->engine, callArgs);
1995 return CallOverloaded(object, method, resource->engine, callArgs);
1999 CallArgument::CallArgument()
2000 : type(QVariant::Invalid)
2004 CallArgument::~CallArgument()
2009 void CallArgument::cleanup()
2011 if (type == QMetaType::QString) {
2012 qstringPtr->~QString();
2013 } else if (type == -1 || type == QMetaType::QVariant) {
2014 qvariantPtr->~QVariant();
2015 } else if (type == qMetaTypeId<QJSValue>()) {
2016 qjsValuePtr->~QJSValue();
2017 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2018 qlistPtr->~QList<QObject *>();
2019 } else if (type == QMetaType::QJsonArray) {
2020 jsonArrayPtr->~QJsonArray();
2021 } else if (type == QMetaType::QJsonObject) {
2022 jsonObjectPtr->~QJsonObject();
2023 } else if (type == QMetaType::QJsonValue) {
2024 jsonValuePtr->~QJsonValue();
2028 void *CallArgument::dataPtr()
2031 return qvariantPtr->data();
2033 return (void *)&allocData;
2036 void CallArgument::initAsType(int callType)
2038 if (type != 0) { cleanup(); type = 0; }
2039 if (callType == QMetaType::UnknownType) return;
2041 if (callType == qMetaTypeId<QJSValue>()) {
2042 qjsValuePtr = new (&allocData) QJSValue();
2044 } else if (callType == QMetaType::Int ||
2045 callType == QMetaType::UInt ||
2046 callType == QMetaType::Bool ||
2047 callType == QMetaType::Double ||
2048 callType == QMetaType::Float) {
2050 } else if (callType == QMetaType::QObjectStar) {
2053 } else if (callType == QMetaType::QString) {
2054 qstringPtr = new (&allocData) QString();
2056 } else if (callType == QMetaType::QVariant) {
2058 qvariantPtr = new (&allocData) QVariant();
2059 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2061 qlistPtr = new (&allocData) QList<QObject *>();
2062 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2064 handlePtr = new (&allocData) QQmlV8Handle;
2065 } else if (callType == QMetaType::QJsonArray) {
2067 jsonArrayPtr = new (&allocData) QJsonArray();
2068 } else if (callType == QMetaType::QJsonObject) {
2070 jsonObjectPtr = new (&allocData) QJsonObject();
2071 } else if (callType == QMetaType::QJsonValue) {
2073 jsonValuePtr = new (&allocData) QJsonValue();
2074 } else if (callType == QMetaType::Void) {
2076 qvariantPtr = new (&allocData) QVariant();
2079 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2083 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2085 if (type != 0) { cleanup(); type = 0; }
2087 if (callType == qMetaTypeId<QJSValue>()) {
2088 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2089 type = qMetaTypeId<QJSValue>();
2090 } else if (callType == QMetaType::Int) {
2091 intValue = quint32(value->Int32Value());
2093 } else if (callType == QMetaType::UInt) {
2094 intValue = quint32(value->Uint32Value());
2096 } else if (callType == QMetaType::Bool) {
2097 boolValue = value->BooleanValue();
2099 } else if (callType == QMetaType::Double) {
2100 doubleValue = double(value->NumberValue());
2102 } else if (callType == QMetaType::Float) {
2103 floatValue = float(value->NumberValue());
2105 } else if (callType == QMetaType::QString) {
2106 if (value->IsNull() || value->IsUndefined())
2107 qstringPtr = new (&allocData) QString();
2109 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2111 } else if (callType == QMetaType::QObjectStar) {
2112 qobjectPtr = engine->toQObject(value);
2114 } else if (callType == qMetaTypeId<QVariant>()) {
2115 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2117 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2118 qlistPtr = new (&allocData) QList<QObject *>();
2119 if (value->IsArray()) {
2120 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2121 uint32_t length = array->Length();
2122 for (uint32_t ii = 0; ii < length; ++ii)
2123 qlistPtr->append(engine->toQObject(array->Get(ii)));
2125 qlistPtr->append(engine->toQObject(value));
2128 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2129 handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2131 } else if (callType == QMetaType::QJsonArray) {
2132 jsonArrayPtr = new (&allocData) QJsonArray(engine->jsonArrayFromJS(value));
2134 } else if (callType == QMetaType::QJsonObject) {
2135 jsonObjectPtr = new (&allocData) QJsonObject(engine->jsonObjectFromJS(value));
2137 } else if (callType == QMetaType::QJsonValue) {
2138 jsonValuePtr = new (&allocData) QJsonValue(engine->jsonValueFromJS(value));
2140 } else if (callType == QMetaType::Void) {
2141 *qvariantPtr = QVariant();
2143 qvariantPtr = new (&allocData) QVariant();
2146 QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2147 QVariant v = engine->toVariant(value, -1);
2149 if (v.userType() == callType) {
2151 } else if (v.canConvert(callType)) {
2153 qvariantPtr->convert(callType);
2155 QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
2157 QObject *obj = ep->toQObject(v);
2159 if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo))
2162 *qvariantPtr = QVariant(callType, &obj);
2164 *qvariantPtr = QVariant(callType, (void *)0);
2170 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2172 if (type == qMetaTypeId<QJSValue>()) {
2173 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2174 } else if (type == QMetaType::Int) {
2175 return v8::Integer::New(int(intValue));
2176 } else if (type == QMetaType::UInt) {
2177 return v8::Integer::NewFromUnsigned(intValue);
2178 } else if (type == QMetaType::Bool) {
2179 return v8::Boolean::New(boolValue);
2180 } else if (type == QMetaType::Double) {
2181 return v8::Number::New(doubleValue);
2182 } else if (type == QMetaType::Float) {
2183 return v8::Number::New(floatValue);
2184 } else if (type == QMetaType::QString) {
2185 return engine->toString(*qstringPtr);
2186 } else if (type == QMetaType::QObjectStar) {
2187 QObject *object = qobjectPtr;
2189 QQmlData::get(object, true)->setImplicitDestructible();
2190 return engine->newQObject(object);
2191 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2192 // XXX Can this be made more by using Array as a prototype and implementing
2193 // directly against QList<QObject*>?
2194 QList<QObject *> &list = *qlistPtr;
2195 v8::Local<v8::Array> array = v8::Array::New(list.count());
2196 for (int ii = 0; ii < list.count(); ++ii)
2197 array->Set(ii, engine->newQObject(list.at(ii)));
2199 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2200 return handlePtr->toHandle();
2201 } else if (type == QMetaType::QJsonArray) {
2202 return engine->jsonArrayToJS(*jsonArrayPtr);
2203 } else if (type == QMetaType::QJsonObject) {
2204 return engine->jsonObjectToJS(*jsonObjectPtr);
2205 } else if (type == QMetaType::QJsonValue) {
2206 return engine->jsonValueToJS(*jsonValuePtr);
2207 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2208 QVariant value = *qvariantPtr;
2209 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2210 if (QObject *object = engine->toQObject(rv))
2211 QQmlData::get(object, true)->setImplicitDestructible();
2214 return v8::Undefined();