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);
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.isQVariant()) {
438 ReadFunction(object, property, &v, notifier);
439 return engine->fromVariant(v);
440 } else if (QQmlValueTypeFactory::isValueType((uint)property.propType)
441 && engine->engine()) {
442 Q_ASSERT(notifier == 0);
444 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
445 QQmlValueType *valueType = ep->valueTypes[property.propType];
447 return engine->newValueType(object, property.coreIndex, valueType);
449 Q_ASSERT(notifier == 0);
451 // see if it's a sequence type
452 bool succeeded = false;
453 v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex,
459 if (property.propType == QMetaType::UnknownType) {
460 QMetaProperty p = object->metaObject()->property(property.coreIndex);
461 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
462 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
463 return v8::Undefined();
465 QVariant v(property.propType, (void *)0);
466 ReadFunction(object, property, v.data(), notifier);
467 return engine->fromVariant(v);
471 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
472 v8::Handle<v8::Value> *objectHandle,
473 const QHashedV8String &property,
474 QV8QObjectWrapper::RevisionMode revisionMode)
476 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
477 // will be a faster way of creating QObject method objects.
478 struct MethodClosure {
479 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
480 v8::Handle<v8::Value> *objectHandle,
482 v8::Handle<v8::Value> argv[] = {
483 objectHandle?*objectHandle:engine->newQObject(object),
484 v8::Integer::New(index)
486 Q_ASSERT(argv[0]->IsObject());
487 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
489 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
490 v8::Handle<v8::Value> *objectHandle,
492 v8::Handle<v8::Value> argv[] = {
493 objectHandle?*objectHandle:engine->newQObject(object),
494 v8::Integer::New(index),
495 v8::Context::GetCallingQmlGlobal()
497 Q_ASSERT(argv[0]->IsObject());
498 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
502 if (QQmlData::wasDeleted(object))
503 return v8::Handle<v8::Value>();
506 // Comparing the hash first actually makes a measurable difference here, at least on x86
507 quint32 hash = property.hash();
508 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
509 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
510 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
511 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
515 QQmlPropertyData local;
516 QQmlPropertyData *result = 0;
518 QQmlData *ddata = QQmlData::get(object, false);
519 if (ddata && ddata->propertyCache)
520 result = ddata->propertyCache->property(property);
522 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
526 return v8::Handle<v8::Value>();
528 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
529 QQmlData *ddata = QQmlData::get(object);
530 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
531 return v8::Handle<v8::Value>();
534 if (result->isFunction() && !result->isVMEProperty()) {
535 if (result->isVMEFunction()) {
536 return ((QQmlVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
537 } else if (result->isV8Function()) {
538 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
539 } else if (result->isSignalHandler()) {
540 v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
541 QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
542 handler->SetExternalResource(r);
545 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
549 QQmlEnginePrivate *ep =
550 engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
552 if (result->hasAccessors()) {
554 QQmlNotifier **nptr = 0;
556 if (ep && ep->propertyCapture && result->accessors->notifier)
559 v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
561 if (result->accessors->notifier) {
562 if (n) ep->captureProperty(n);
564 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
570 if (ep && !result->isConstant()) {
572 if (result->coreIndex == 0)
573 ep->captureProperty(QQmlData::get(object, true)->objectNameNotifier());
575 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
578 if (result->isVMEProperty()) {
579 typedef QQmlVMEMetaObject VMEMO;
580 VMEMO *vmemo = const_cast<VMEMO *>(static_cast<const VMEMO *>(object->metaObject()));
581 return vmemo->vmeProperty(result->coreIndex);
582 } else if (result->isDirect()) {
583 return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
585 return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
589 // Setter for writable properties. Shared between the interceptor and fast property accessor
590 static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
591 v8::Handle<v8::Value> value)
593 QQmlBinding *newBinding = 0;
594 if (value->IsFunction()) {
595 if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
596 if (!property->isVMEProperty()) {
597 // XXX TODO: uncomment the following lines
598 // assigning a JS function to a non-var-property is not allowed.
599 //QString error = QLatin1String("Cannot assign JavaScript function to ") +
600 // QLatin1String(QMetaType::typeName(property->propType));
601 //v8::ThrowException(v8::Exception::Error(engine->toString(error)));
603 // XXX TODO: remove the following transition behaviour
604 // Temporarily allow assignment of functions to non-var properties
605 // to mean binding assignment (as per old behaviour).
606 QQmlContextData *context = engine->callingContext();
607 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
609 v8::Local<v8::StackTrace> trace =
610 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
611 v8::StackTrace::kScriptName));
612 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
613 int lineNumber = frame->GetLineNumber();
614 int columnNumber = frame->GetColumn();
615 QString url = engine->toString(frame->GetScriptName());
617 newBinding = new QQmlBinding(&function, object, context, url, lineNumber, columnNumber);
618 newBinding->setTarget(object, *property, context);
619 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
620 QQmlBinding::RequiresThisObject);
621 qWarning("WARNING: function assignment is DEPRECATED and will be removed! Wrap RHS in Qt.binding(): %s:%d", qPrintable(engine->toString(frame->GetScriptName())), frame->GetLineNumber());
624 // binding assignment.
625 QQmlContextData *context = engine->callingContext();
626 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
628 v8::Local<v8::StackTrace> trace =
629 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
630 v8::StackTrace::kScriptName));
631 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
632 int lineNumber = frame->GetLineNumber();
633 int columnNumber = frame->GetColumn();
634 QString url = engine->toString(frame->GetScriptName());
636 newBinding = new QQmlBinding(&function, object, context, url, lineNumber, columnNumber);
637 newBinding->setTarget(object, *property, context);
638 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
639 QQmlBinding::RequiresThisObject);
643 QQmlAbstractBinding *oldBinding =
644 QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
646 oldBinding->destroy();
648 if (!newBinding && property->isVMEProperty()) {
649 // allow assignment of "special" values (null, undefined, function) to var properties
650 static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
654 #define PROPERTY_STORE(cpptype, value) \
658 void *argv[] = { &o, 0, &status, &flags }; \
659 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
662 if (value->IsNull() && property->isQObject()) {
663 PROPERTY_STORE(QObject*, 0);
664 } else if (value->IsUndefined() && property->isResettable()) {
666 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
667 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
668 PROPERTY_STORE(QVariant, QVariant());
669 } else if (value->IsUndefined() && property->propType == QMetaType::QJsonValue) {
670 PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
671 } else if (value->IsUndefined()) {
672 QString error = QLatin1String("Cannot assign [undefined] to ") +
673 QLatin1String(QMetaType::typeName(property->propType));
674 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
675 } else if (value->IsFunction()) {
676 // this is handled by the binding creation above
677 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
678 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
679 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
680 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
681 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
682 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
683 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
684 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
685 } else if (property->propType == QMetaType::QString && value->IsString()) {
686 PROPERTY_STORE(QString, engine->toString(value->ToString()));
687 } else if (property->isVMEProperty()) {
688 static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
691 if (property->isQList())
692 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
694 v = engine->toVariant(value, property->propType);
696 QQmlContextData *context = engine->callingContext();
697 if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
698 const char *valueType = 0;
699 if (v.userType() == QVariant::Invalid) valueType = "null";
700 else valueType = QMetaType::typeName(v.userType());
702 const char *targetTypeName = QMetaType::typeName(property->propType);
704 targetTypeName = "an unregistered type";
706 QString error = QLatin1String("Cannot assign ") +
707 QLatin1String(valueType) +
708 QLatin1String(" to ") +
709 QLatin1String(targetTypeName);
710 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
715 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
716 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
718 if (engine->qobjectWrapper()->m_toStringString == property ||
719 engine->qobjectWrapper()->m_destroyString == property)
722 if (QQmlData::wasDeleted(object))
725 QQmlPropertyData local;
726 QQmlPropertyData *result = 0;
727 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
732 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
733 QQmlData *ddata = QQmlData::get(object);
734 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
738 if (!result->isWritable() && !result->isQList()) {
739 QString error = QLatin1String("Cannot assign to read-only property \"") +
740 engine->toString(property.string()) + QLatin1Char('\"');
741 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
745 StoreProperty(engine, object, result, value);
750 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
751 const v8::AccessorInfo &info)
753 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
755 if (QQmlData::wasDeleted(resource->object))
756 return v8::Handle<v8::Value>();
758 QObject *object = resource->object;
760 QHashedV8String propertystring(property);
762 QV8Engine *v8engine = resource->engine;
763 v8::Handle<v8::Value> This = info.This();
764 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
765 QV8QObjectWrapper::IgnoreRevision);
766 if (!result.IsEmpty())
769 if (QV8Engine::startsWithUpper(property)) {
770 // Check for attached properties
771 QQmlContextData *context = v8engine->callingContext();
773 if (context && context->imports) {
774 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
777 if (r.scriptIndex != -1) {
778 return v8::Undefined();
780 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
781 } else if (r.importNamespace) {
782 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
783 QV8TypeWrapper::ExcludeEnums);
785 Q_ASSERT(!"Unreachable");
790 return v8::Handle<v8::Value>();
793 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
794 v8::Local<v8::Value> value,
795 const v8::AccessorInfo &info)
797 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
799 if (QQmlData::wasDeleted(resource->object))
802 QObject *object = resource->object;
804 QHashedV8String propertystring(property);
806 QV8Engine *v8engine = resource->engine;
807 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
810 QString error = QLatin1String("Cannot assign to non-existent property \"") +
811 v8engine->toString(property) + QLatin1Char('\"');
812 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
819 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
820 const v8::AccessorInfo &info)
822 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
824 if (resource->object.isNull())
825 return v8::Handle<v8::Integer>();
827 QV8Engine *engine = resource->engine;
828 QObject *object = resource->object;
830 QHashedV8String propertystring(property);
832 QQmlPropertyData local;
833 QQmlPropertyData *result = 0;
834 result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local);
837 return v8::Handle<v8::Integer>();
838 else if (!result->isWritable() && !result->isQList())
839 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
841 return v8::Integer::New(v8::DontDelete);
844 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
846 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
848 if (resource->object.isNull())
849 return v8::Array::New();
851 QObject *object = resource->object;
855 QQmlEnginePrivate *ep = resource->engine->engine()
856 ? QQmlEnginePrivate::get(resource->engine->engine())
859 QQmlPropertyCache *cache = 0;
860 QQmlData *ddata = QQmlData::get(object);
862 cache = ddata->propertyCache;
865 cache = ep ? ep->cache(object) : 0;
867 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
869 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
870 const QMetaObject *mo = object->metaObject();
871 int pc = mo->propertyCount();
872 int po = mo->propertyOffset();
873 for (int i=po; i<pc; ++i)
874 result << QString::fromUtf8(mo->property(i).name());
877 result = cache->propertyNames();
880 v8::Local<v8::Array> rv = v8::Array::New(result.count());
882 for (int ii = 0; ii < result.count(); ++ii)
883 rv->Set(ii, resource->engine->toString(result.at(ii)));
888 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
889 const v8::AccessorInfo& info)
891 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
893 if (QQmlData::wasDeleted(resource->object))
896 QObject *object = resource->object;
898 QQmlPropertyData *property =
899 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
901 int index = property->coreIndex;
903 QQmlData *ddata = QQmlData::get(object, false);
905 Q_ASSERT(ddata->propertyCache);
907 QQmlPropertyData *pdata = ddata->propertyCache->property(index);
910 Q_ASSERT(pdata->isWritable() || pdata->isQList());
912 StoreProperty(resource->engine, object, pdata, value);
915 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
916 const v8::AccessorInfo& info)
918 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
920 if (QQmlData::wasDeleted(resource->object))
923 QV8Engine *v8engine = resource->engine;
925 QString error = QLatin1String("Cannot assign to read-only property \"") +
926 v8engine->toString(property) + QLatin1Char('\"');
927 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
930 void QV8QObjectWrapper::WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *wrapper)
932 Q_ASSERT(handle->IsObject());
933 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
936 static_cast<QV8QObjectWrapper*>(wrapper)->unregisterWeakQObjectReference(resource);
937 static_cast<QV8QObjectWrapper*>(wrapper)->deleteWeakQObject(resource);
939 qPersistentDispose(handle);
942 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
944 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
945 instance->v8object.Clear();
946 qPersistentDispose(handle);
949 v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
952 Q_ASSERT(this->engine);
954 Q_ASSERT(QQmlData::get(object, false));
955 Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
958 if (constructor.IsEmpty()) {
959 v8::Local<v8::FunctionTemplate> ft;
961 QString toString = QLatin1String("toString");
962 QString destroy = QLatin1String("destroy");
964 // As we use hash linking, it is possible that iterating over the values can give duplicates.
965 // To combat this, we must unique'ify our properties.
966 StringCache uniqueHash;
967 if (stringCache.isLinked())
968 uniqueHash.reserve(stringCache.count());
970 // XXX TODO: Enables fast property accessors. These more than double the property access
971 // performance, but the cost of setting up this structure hasn't been measured so
972 // its not guarenteed that this is a win overall. We need to try and measure the cost.
973 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
974 if (stringCache.isLinked()) {
975 if (uniqueHash.contains(iter))
977 uniqueHash.insert(iter);
980 QQmlPropertyData *property = *iter;
981 if (property->notFullyResolved()) resolve(property);
983 if (property->isFunction())
986 v8::AccessorGetter fastgetter = 0;
987 v8::AccessorSetter fastsetter = FastValueSetter;
988 if (!property->isWritable())
989 fastsetter = FastValueSetterReadOnly;
991 if (property->isQObject())
992 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
993 else if (property->propType == QMetaType::Int || property->isEnum())
994 fastgetter = FAST_GETTER_FUNCTION(property, int);
995 else if (property->propType == QMetaType::Bool)
996 fastgetter = FAST_GETTER_FUNCTION(property, bool);
997 else if (property->propType == QMetaType::QString)
998 fastgetter = FAST_GETTER_FUNCTION(property, QString);
999 else if (property->propType == QMetaType::UInt)
1000 fastgetter = FAST_GETTER_FUNCTION(property, uint);
1001 else if (property->propType == QMetaType::Float)
1002 fastgetter = FAST_GETTER_FUNCTION(property, float);
1003 else if (property->propType == QMetaType::Double)
1004 fastgetter = FAST_GETTER_FUNCTION(property, double);
1007 QString name = iter.key();
1008 if (name == toString || name == destroy)
1012 ft = v8::FunctionTemplate::New();
1013 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1014 QV8QObjectWrapper::Setter,
1015 QV8QObjectWrapper::Query,
1017 QV8QObjectWrapper::Enumerator);
1018 ft->InstanceTemplate()->SetHasExternalResource(true);
1021 // We wrap the raw QQmlPropertyData pointer here. This is safe as the
1022 // pointer will remain valid at least as long as the lifetime of any QObject's of
1023 // this type and the property accessor checks if the object is 0 (deleted) before
1024 // dereferencing the pointer.
1025 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
1026 v8::External::Wrap(property));
1031 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
1033 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1034 QV8QObjectWrapper::Setter,
1035 QV8QObjectWrapper::Query,
1037 QV8QObjectWrapper::Enumerator);
1038 ft->InstanceTemplate()->SetHasExternalResource(true);
1039 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
1042 QQmlCleanup::addToEngine(this->engine);
1045 v8::Local<v8::Object> result = constructor->NewInstance();
1046 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1047 result->SetExternalResource(r);
1051 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
1053 v8::Local<v8::Object> rv;
1055 if (!ddata->propertyCache && engine->engine()) {
1056 ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
1057 if (ddata->propertyCache) ddata->propertyCache->addref();
1060 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1061 rv = ddata->propertyCache->newQObject(object, engine);
1063 // XXX NewInstance() should be optimized
1064 rv = m_constructor->NewInstance();
1065 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1066 rv->SetExternalResource(r);
1073 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1074 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
1076 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
1077 persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that
1078 "owns" this handle by setting the QQmlData::v8objectid to the id of this
1080 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create
1081 an entry in the m_taintedObject hash where we store the handle and mark the object as
1082 "tainted" in the QQmlData::hasTaintedV8Object flag.
1083 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1084 in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has
1085 released the handle.
1087 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1089 if (QQmlData::wasDeleted(object))
1092 QQmlData *ddata = QQmlData::get(object, true);
1094 return v8::Undefined();
1096 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1097 // We own the v8object
1098 return v8::Local<v8::Object>::New(ddata->v8object);
1099 } else if (ddata->v8object.IsEmpty() &&
1100 (ddata->v8objectid == m_id || // We own the QObject
1101 ddata->v8objectid == 0 || // No one owns the QObject
1102 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1104 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1105 ddata->v8object = qPersistentNew<v8::Object>(rv);
1106 ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1107 ddata->v8objectid = m_id;
1108 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1109 registerWeakQObjectReference(resource);
1113 // If this object is tainted, we have to check to see if it is in our
1114 // tainted object list
1115 TaintedHash::Iterator iter =
1116 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1117 bool found = iter != m_taintedObjects.end();
1119 // If our tainted handle doesn't exist or has been collected, and there isn't
1120 // a handle in the ddata, we can assume ownership of the ddata->v8object
1121 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1122 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1123 ddata->v8object = qPersistentNew<v8::Object>(rv);
1124 ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1125 ddata->v8objectid = m_id;
1126 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1127 registerWeakQObjectReference(resource);
1131 m_taintedObjects.erase(iter);
1135 } else if (!found) {
1136 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1137 iter = m_taintedObjects.insert(object, instance);
1138 ddata->hasTaintedV8Object = true;
1141 if ((*iter)->v8object.IsEmpty()) {
1142 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1143 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1144 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1147 return v8::Local<v8::Object>::New((*iter)->v8object);
1150 void QV8QObjectWrapper::deleteWeakQObject(QV8QObjectResource *resource)
1152 QObject *object = resource->object;
1154 QQmlData *ddata = QQmlData::get(object, false);
1156 if (ddata->inCreation) {
1157 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1161 ddata->v8object.Clear();
1162 if (!object->parent() && !ddata->indestructible) {
1163 // This object is notionally destroyed now
1164 if (ddata->ownContext && ddata->context)
1165 ddata->context->emitDestruction();
1166 ddata->isQueuedForDeletion = true;
1167 object->deleteLater();
1173 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1175 if (object->IsFunction())
1176 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1178 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1179 return qMakePair(resource->object.data(), resource->index);
1181 return qMakePair((QObject *)0, -1);
1184 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1186 v8::ScriptOrigin origin = function->GetScriptOrigin();
1187 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1189 // This is one of our special QObject method wrappers
1190 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1191 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1193 if (data->IsArray()) {
1194 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1195 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1198 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1201 return qMakePair((QObject *)0, -1);
1204 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1207 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1208 ~QV8QObjectConnectionList();
1212 : needsDestroy(false) {}
1213 Connection(const Connection &other)
1214 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1215 Connection &operator=(const Connection &other) {
1216 thisObject = other.thisObject;
1217 function = other.function;
1218 needsDestroy = other.needsDestroy;
1222 v8::Persistent<v8::Object> thisObject;
1223 v8::Persistent<v8::Function> function;
1226 qPersistentDispose(thisObject);
1227 qPersistentDispose(function);
1233 struct ConnectionList : public QList<Connection> {
1234 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1235 int connectionsInUse;
1236 bool connectionsNeedClean;
1241 typedef QHash<int, ConnectionList> SlotHash;
1246 virtual void objectDestroyed(QObject *);
1247 virtual int qt_metacall(QMetaObject::Call, int, void **);
1250 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1251 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1255 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1257 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1258 QList<Connection> &connections = *iter;
1259 for (int ii = 0; ii < connections.count(); ++ii) {
1260 qPersistentDispose(connections[ii].thisObject);
1261 qPersistentDispose(connections[ii].function);
1267 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1269 engine->qobjectWrapper()->m_connections.remove(object);
1272 needsDestroy = true;
1277 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1279 if (method == QMetaObject::InvokeMetaMethod) {
1280 SlotHash::Iterator iter = slotHash.find(index);
1281 if (iter == slotHash.end())
1283 ConnectionList &connectionList = *iter;
1284 if (connectionList.isEmpty())
1289 connectionList.connectionsInUse++;
1291 QList<Connection> connections = connectionList;
1293 QVarLengthArray<int, 9> dummy;
1294 int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1296 v8::HandleScope handle_scope;
1297 v8::Context::Scope scope(engine->context());
1299 int argCount = argsTypes?argsTypes[0]:0;
1300 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1302 for (int ii = 0; ii < argCount; ++ii) {
1303 int type = argsTypes[ii + 1];
1304 if (type == qMetaTypeId<QVariant>()) {
1305 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1307 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1311 for (int ii = 0; ii < connections.count(); ++ii) {
1312 Connection &connection = connections[ii];
1313 if (connection.needsDestroy)
1316 v8::TryCatch try_catch;
1317 if (connection.thisObject.IsEmpty()) {
1318 connection.function->Call(engine->global(), argCount, args.data());
1320 connection.function->Call(connection.thisObject, argCount, args.data());
1323 if (try_catch.HasCaught()) {
1325 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1326 v8::Local<v8::Message> message = try_catch.Message();
1327 if (!message.IsEmpty())
1328 QQmlExpressionPrivate::exceptionToError(message, error);
1329 QQmlEnginePrivate::get(engine->engine())->warning(error);
1333 connectionList.connectionsInUse--;
1334 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1335 for (QList<Connection>::Iterator iter = connectionList.begin();
1336 iter != connectionList.end(); ) {
1337 if (iter->needsDestroy) {
1339 iter = connectionList.erase(iter);
1347 if (inUse == 0 && needsDestroy)
1354 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1356 if (args.Length() == 0)
1357 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1359 QV8Engine *engine = V8ENGINE();
1361 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1362 QObject *signalObject = signalInfo.first;
1363 int signalIndex = signalInfo.second;
1365 if (signalIndex == -1)
1366 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1369 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1371 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1372 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1374 v8::Local<v8::Value> functionValue;
1375 v8::Local<v8::Value> functionThisValue;
1377 if (args.Length() == 1) {
1378 functionValue = args[0];
1380 functionThisValue = args[0];
1381 functionValue = args[1];
1384 if (!functionValue->IsFunction())
1385 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1387 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1388 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1390 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1391 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1392 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1393 if (iter == connections.end())
1394 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1396 QV8QObjectConnectionList *connectionList = *iter;
1397 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1398 if (slotIter == connectionList->slotHash.end()) {
1399 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1400 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1403 QV8QObjectConnectionList::Connection connection;
1404 if (!functionThisValue.IsEmpty())
1405 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1406 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1408 slotIter->append(connection);
1410 return v8::Undefined();
1413 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1415 if (args.Length() == 0)
1416 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1418 QV8Engine *engine = V8ENGINE();
1420 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1421 QObject *signalObject = signalInfo.first;
1422 int signalIndex = signalInfo.second;
1424 if (signalIndex == -1)
1425 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1428 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1430 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1431 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1433 v8::Local<v8::Value> functionValue;
1434 v8::Local<v8::Value> functionThisValue;
1436 if (args.Length() == 1) {
1437 functionValue = args[0];
1439 functionThisValue = args[0];
1440 functionValue = args[1];
1443 if (!functionValue->IsFunction())
1444 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1446 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1447 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1449 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1450 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1451 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1452 if (iter == connectionsList.end())
1453 return v8::Undefined(); // Nothing to disconnect from
1455 QV8QObjectConnectionList *connectionList = *iter;
1456 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1457 if (slotIter == connectionList->slotHash.end())
1458 return v8::Undefined(); // Nothing to disconnect from
1460 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1462 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1463 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1465 if (functionData.second != -1) {
1466 // This is a QObject function wrapper
1467 for (int ii = 0; ii < connections.count(); ++ii) {
1468 QV8QObjectConnectionList::Connection &connection = connections[ii];
1470 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1471 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1473 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1474 if (connectedFunctionData == functionData) {
1476 if (connections.connectionsInUse) {
1477 connection.needsDestroy = true;
1478 connections.connectionsNeedClean = true;
1480 connection.dispose();
1481 connections.removeAt(ii);
1483 return v8::Undefined();
1489 // This is a normal JS function
1490 for (int ii = 0; ii < connections.count(); ++ii) {
1491 QV8QObjectConnectionList::Connection &connection = connections[ii];
1492 if (connection.function->StrictEquals(function) &&
1493 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1494 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1496 if (connections.connectionsInUse) {
1497 connection.needsDestroy = true;
1498 connections.connectionsNeedClean = true;
1500 connection.dispose();
1501 connections.removeAt(ii);
1503 return v8::Undefined();
1508 return v8::Undefined();
1512 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1514 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1516 Only searches for real properties of \a object (including methods), not attached properties etc.
1520 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1522 Set the \a property of \a object to \a value.
1524 Returns true if the property was "set" - even if this results in an exception being thrown -
1525 and false if the object has no such property.
1527 Only searches for real properties of \a object (including methods), not attached properties etc.
1533 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1534 int Length() const { return _length; }
1535 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1539 v8::Handle<v8::Object> *_args;
1543 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1544 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1548 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1549 args[0].initAsType(returnType);
1551 for (int ii = 0; ii < argCount; ++ii)
1552 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1554 QVarLengthArray<void *, 9> argData(args.count());
1555 for (int ii = 0; ii < args.count(); ++ii)
1556 argData[ii] = args[ii].dataPtr();
1558 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1560 return args[0].toValue(engine);
1562 } else if (returnType != 0) {
1565 arg.initAsType(returnType);
1567 void *args[] = { arg.dataPtr() };
1569 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1571 return arg.toValue(engine);
1575 void *args[] = { 0 };
1576 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1577 return v8::Undefined();
1583 Returns the match score for converting \a actual to be of type \a conversionType. A
1584 zero score means "perfect match" whereas a higher score is worse.
1586 The conversion table is copied out of the QtScript callQtMethod() function.
1588 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1590 if (actual->IsNumber()) {
1591 switch (conversionType) {
1592 case QMetaType::Double:
1594 case QMetaType::Float:
1596 case QMetaType::LongLong:
1597 case QMetaType::ULongLong:
1599 case QMetaType::Long:
1600 case QMetaType::ULong:
1602 case QMetaType::Int:
1603 case QMetaType::UInt:
1605 case QMetaType::Short:
1606 case QMetaType::UShort:
1609 case QMetaType::Char:
1610 case QMetaType::UChar:
1612 case QMetaType::QJsonValue:
1617 } else if (actual->IsString()) {
1618 switch (conversionType) {
1619 case QMetaType::QString:
1621 case QMetaType::QJsonValue:
1626 } else if (actual->IsBoolean()) {
1627 switch (conversionType) {
1628 case QMetaType::Bool:
1630 case QMetaType::QJsonValue:
1635 } else if (actual->IsDate()) {
1636 switch (conversionType) {
1637 case QMetaType::QDateTime:
1639 case QMetaType::QDate:
1641 case QMetaType::QTime:
1646 } else if (actual->IsRegExp()) {
1647 switch (conversionType) {
1648 case QMetaType::QRegExp:
1653 } else if (actual->IsArray()) {
1654 switch (conversionType) {
1655 case QMetaType::QJsonArray:
1657 case QMetaType::QStringList:
1658 case QMetaType::QVariantList:
1663 } else if (actual->IsNull()) {
1664 switch (conversionType) {
1665 case QMetaType::VoidStar:
1666 case QMetaType::QObjectStar:
1667 case QMetaType::QJsonValue:
1670 const char *typeName = QMetaType::typeName(conversionType);
1671 if (typeName && typeName[strlen(typeName) - 1] == '*')
1677 } else if (actual->IsObject()) {
1678 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1680 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1681 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1682 switch (conversionType) {
1683 case QMetaType::QObjectStar:
1688 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1689 if (conversionType == qMetaTypeId<QVariant>())
1691 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1695 } else if (conversionType == QMetaType::QJsonObject) {
1706 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1712 int classInfoCount, classInfoData;
1713 int methodCount, methodData;
1716 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1720 Returns the next related method, if one, or 0.
1722 static const QQmlPropertyData * RelatedMethod(QObject *object,
1723 const QQmlPropertyData *current,
1724 QQmlPropertyData &dummy)
1726 QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1727 if (!current->isOverload())
1730 Q_ASSERT(!current->overrideIndexIsProperty);
1733 return cache->method(current->overrideIndex);
1735 const QMetaObject *mo = object->metaObject();
1736 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1738 while (methodOffset > current->overrideIndex) {
1739 mo = mo->superClass();
1740 methodOffset -= QMetaObject_methods(mo);
1743 QMetaMethod method = mo->method(current->overrideIndex);
1746 // Look for overloaded methods
1747 QByteArray methodName = method.name();
1748 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1749 if (methodName == mo->method(ii).name()) {
1750 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1751 dummy.overrideIndexIsProperty = 0;
1752 dummy.overrideIndex = ii;
1761 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1762 QV8Engine *engine, CallArgs &callArgs)
1764 if (data.hasArguments()) {
1767 QVarLengthArray<int, 9> dummy;
1768 QByteArray unknownTypeError;
1770 args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1774 QString typeName = QString::fromLatin1(unknownTypeError);
1775 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1776 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1777 return v8::Handle<v8::Value>();
1780 if (args[0] > callArgs.Length()) {
1781 QString error = QLatin1String("Insufficient arguments");
1782 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1783 return v8::Handle<v8::Value>();
1786 return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1790 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1796 Resolve the overloaded method to call. The algorithm works conceptually like this:
1797 1. Resolve the set of overloads it is *possible* to call.
1798 Impossible overloads include those that have too many parameters or have parameters
1800 2. Filter the set of overloads to only contain those with the closest number of
1802 For example, if we are called with 3 parameters and there are 2 overloads that
1803 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1804 3. Find the best remaining overload based on its match score.
1805 If two or more overloads have the same match score, call the last one. The match
1806 score is constructed by adding the matchScore() result for each of the parameters.
1808 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1809 QV8Engine *engine, CallArgs &callArgs)
1811 int argumentCount = callArgs.Length();
1813 const QQmlPropertyData *best = 0;
1814 int bestParameterScore = INT_MAX;
1815 int bestMatchScore = INT_MAX;
1817 QQmlPropertyData dummy;
1818 const QQmlPropertyData *attempt = &data;
1821 QVarLengthArray<int, 9> dummy;
1822 int methodArgumentCount = 0;
1823 int *methodArgTypes = 0;
1824 if (attempt->hasArguments()) {
1825 typedef QQmlPropertyCache PC;
1826 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1827 if (!args) // Must be an unknown argument
1830 methodArgumentCount = args[0];
1831 methodArgTypes = args + 1;
1834 if (methodArgumentCount > argumentCount)
1835 continue; // We don't have sufficient arguments to call this method
1837 int methodParameterScore = argumentCount - methodArgumentCount;
1838 if (methodParameterScore > bestParameterScore)
1839 continue; // We already have a better option
1841 int methodMatchScore = 0;
1842 for (int ii = 0; ii < methodArgumentCount; ++ii)
1843 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1845 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1847 bestParameterScore = methodParameterScore;
1848 bestMatchScore = methodMatchScore;
1851 if (bestParameterScore == 0 && bestMatchScore == 0)
1852 break; // We can't get better than that
1854 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1857 return CallPrecise(object, *best, engine, callArgs);
1859 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1860 const QQmlPropertyData *candidate = &data;
1862 error += QLatin1String("\n ") +
1863 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData());
1864 candidate = RelatedMethod(object, candidate, dummy);
1867 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1868 return v8::Handle<v8::Value>();
1872 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1876 QString objectName = object->objectName();
1878 result += QString::fromUtf8(object->metaObject()->className());
1879 result += QLatin1String("(0x");
1880 result += QString::number((quintptr)object,16);
1882 if (!objectName.isEmpty()) {
1883 result += QLatin1String(", \"");
1884 result += objectName;
1885 result += QLatin1Char('\"');
1888 result += QLatin1Char(')');
1890 result = QLatin1String("null");
1893 return engine->toString(result);
1896 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1898 QQmlData *ddata = QQmlData::get(object, false);
1899 if (!ddata || ddata->indestructible || ddata->inCreation) {
1900 const char *error = "Invalid attempt to destroy() an indestructible object";
1901 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1902 return v8::Undefined();
1907 delay = args->Get(0)->Uint32Value();
1910 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1912 object->deleteLater();
1914 return v8::Undefined();
1917 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1919 // object, index, qmlglobal, argCount, args
1920 Q_ASSERT(args.Length() == 5);
1921 Q_ASSERT(args[0]->IsObject());
1923 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1926 return v8::Undefined();
1928 int argCount = args[3]->Int32Value();
1929 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1931 // Special hack to return info about this closure.
1932 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1933 v8::Local<v8::Array> data = v8::Array::New(2);
1934 data->Set(0, args[0]);
1935 data->Set(1, args[1]);
1939 QObject *object = resource->object;
1940 int index = args[1]->Int32Value();
1943 return v8::Undefined();
1946 // Builtin functions
1947 if (index == QOBJECT_TOSTRING_INDEX) {
1948 return ToString(resource->engine, object, argCount, arguments);
1949 } else if (index == QOBJECT_DESTROY_INDEX) {
1950 return Destroy(resource->engine, object, argCount, arguments);
1952 return v8::Undefined();
1956 QQmlPropertyData method;
1958 if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
1959 if (ddata->propertyCache) {
1960 QQmlPropertyData *d = ddata->propertyCache->method(index);
1962 return v8::Undefined();
1967 if (method.coreIndex == -1) {
1968 method.load(object->metaObject()->method(index));
1970 if (method.coreIndex == -1)
1971 return v8::Undefined();
1974 if (method.isV8Function()) {
1975 v8::Handle<v8::Value> rv;
1976 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1978 QQmlV8Function func(argCount, arguments, rv, qmlglobal,
1979 resource->engine->contextWrapper()->context(qmlglobal),
1981 QQmlV8Function *funcptr = &func;
1983 void *args[] = { 0, &funcptr };
1984 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1986 if (rv.IsEmpty()) return v8::Undefined();
1990 CallArgs callArgs(argCount, &arguments);
1991 if (!method.isOverload()) {
1992 return CallPrecise(object, method, resource->engine, callArgs);
1994 return CallOverloaded(object, method, resource->engine, callArgs);
1998 CallArgument::CallArgument()
1999 : type(QVariant::Invalid)
2003 CallArgument::~CallArgument()
2008 void CallArgument::cleanup()
2010 if (type == QMetaType::QString) {
2011 qstringPtr->~QString();
2012 } else if (type == -1 || type == QMetaType::QVariant) {
2013 qvariantPtr->~QVariant();
2014 } else if (type == qMetaTypeId<QJSValue>()) {
2015 qjsValuePtr->~QJSValue();
2016 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2017 qlistPtr->~QList<QObject *>();
2018 } else if (type == QMetaType::QJsonArray) {
2019 jsonArrayPtr->~QJsonArray();
2020 } else if (type == QMetaType::QJsonObject) {
2021 jsonObjectPtr->~QJsonObject();
2022 } else if (type == QMetaType::QJsonValue) {
2023 jsonValuePtr->~QJsonValue();
2027 void *CallArgument::dataPtr()
2030 return qvariantPtr->data();
2032 return (void *)&allocData;
2035 void CallArgument::initAsType(int callType)
2037 if (type != 0) { cleanup(); type = 0; }
2038 if (callType == QMetaType::UnknownType) return;
2040 if (callType == qMetaTypeId<QJSValue>()) {
2041 qjsValuePtr = new (&allocData) QJSValue();
2043 } else if (callType == QMetaType::Int ||
2044 callType == QMetaType::UInt ||
2045 callType == QMetaType::Bool ||
2046 callType == QMetaType::Double ||
2047 callType == QMetaType::Float) {
2049 } else if (callType == QMetaType::QObjectStar) {
2052 } else if (callType == QMetaType::QString) {
2053 qstringPtr = new (&allocData) QString();
2055 } else if (callType == QMetaType::QVariant) {
2057 qvariantPtr = new (&allocData) QVariant();
2058 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2060 qlistPtr = new (&allocData) QList<QObject *>();
2061 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2063 handlePtr = new (&allocData) QQmlV8Handle;
2064 } else if (callType == QMetaType::QJsonArray) {
2066 jsonArrayPtr = new (&allocData) QJsonArray();
2067 } else if (callType == QMetaType::QJsonObject) {
2069 jsonObjectPtr = new (&allocData) QJsonObject();
2070 } else if (callType == QMetaType::QJsonValue) {
2072 jsonValuePtr = new (&allocData) QJsonValue();
2073 } else if (callType == QMetaType::Void) {
2075 qvariantPtr = new (&allocData) QVariant();
2078 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2082 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2084 if (type != 0) { cleanup(); type = 0; }
2086 if (callType == qMetaTypeId<QJSValue>()) {
2087 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2088 type = qMetaTypeId<QJSValue>();
2089 } else if (callType == QMetaType::Int) {
2090 intValue = quint32(value->Int32Value());
2092 } else if (callType == QMetaType::UInt) {
2093 intValue = quint32(value->Uint32Value());
2095 } else if (callType == QMetaType::Bool) {
2096 boolValue = value->BooleanValue();
2098 } else if (callType == QMetaType::Double) {
2099 doubleValue = double(value->NumberValue());
2101 } else if (callType == QMetaType::Float) {
2102 floatValue = float(value->NumberValue());
2104 } else if (callType == QMetaType::QString) {
2105 if (value->IsNull() || value->IsUndefined())
2106 qstringPtr = new (&allocData) QString();
2108 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2110 } else if (callType == QMetaType::QObjectStar) {
2111 qobjectPtr = engine->toQObject(value);
2113 } else if (callType == qMetaTypeId<QVariant>()) {
2114 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2116 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2117 qlistPtr = new (&allocData) QList<QObject *>();
2118 if (value->IsArray()) {
2119 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2120 uint32_t length = array->Length();
2121 for (uint32_t ii = 0; ii < length; ++ii)
2122 qlistPtr->append(engine->toQObject(array->Get(ii)));
2124 qlistPtr->append(engine->toQObject(value));
2127 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2128 handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2130 } else if (callType == QMetaType::QJsonArray) {
2131 jsonArrayPtr = new (&allocData) QJsonArray(engine->jsonArrayFromJS(value));
2133 } else if (callType == QMetaType::QJsonObject) {
2134 jsonObjectPtr = new (&allocData) QJsonObject(engine->jsonObjectFromJS(value));
2136 } else if (callType == QMetaType::QJsonValue) {
2137 jsonValuePtr = new (&allocData) QJsonValue(engine->jsonValueFromJS(value));
2139 } else if (callType == QMetaType::Void) {
2140 *qvariantPtr = QVariant();
2142 qvariantPtr = new (&allocData) QVariant();
2145 QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2146 QVariant v = engine->toVariant(value, -1);
2148 if (v.userType() == callType) {
2150 } else if (v.canConvert((QVariant::Type)callType)) {
2152 qvariantPtr->convert((QVariant::Type)callType);
2153 } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
2154 QObject *obj = ep->toQObject(v);
2157 const QMetaObject *objMo = obj->metaObject();
2158 while (objMo && objMo != mo) objMo = objMo->superClass();
2159 if (!objMo) obj = 0;
2162 *qvariantPtr = QVariant(callType, &obj);
2164 *qvariantPtr = QVariant(callType, (void *)0);
2169 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2171 if (type == qMetaTypeId<QJSValue>()) {
2172 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2173 } else if (type == QMetaType::Int) {
2174 return v8::Integer::New(int(intValue));
2175 } else if (type == QMetaType::UInt) {
2176 return v8::Integer::NewFromUnsigned(intValue);
2177 } else if (type == QMetaType::Bool) {
2178 return v8::Boolean::New(boolValue);
2179 } else if (type == QMetaType::Double) {
2180 return v8::Number::New(doubleValue);
2181 } else if (type == QMetaType::Float) {
2182 return v8::Number::New(floatValue);
2183 } else if (type == QMetaType::QString) {
2184 return engine->toString(*qstringPtr);
2185 } else if (type == QMetaType::QObjectStar) {
2186 QObject *object = qobjectPtr;
2188 QQmlData::get(object, true)->setImplicitDestructible();
2189 return engine->newQObject(object);
2190 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2191 // XXX Can this be made more by using Array as a prototype and implementing
2192 // directly against QList<QObject*>?
2193 QList<QObject *> &list = *qlistPtr;
2194 v8::Local<v8::Array> array = v8::Array::New(list.count());
2195 for (int ii = 0; ii < list.count(); ++ii)
2196 array->Set(ii, engine->newQObject(list.at(ii)));
2198 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2199 return handlePtr->toHandle();
2200 } else if (type == QMetaType::QJsonArray) {
2201 return engine->jsonArrayToJS(*jsonArrayPtr);
2202 } else if (type == QMetaType::QJsonObject) {
2203 return engine->jsonObjectToJS(*jsonObjectPtr);
2204 } else if (type == QMetaType::QJsonValue) {
2205 return engine->jsonValueToJS(*jsonValuePtr);
2206 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2207 QVariant value = *qvariantPtr;
2208 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2209 if (QObject *object = engine->toQObject(rv))
2210 QQmlData::get(object, true)->setImplicitDestructible();
2213 return v8::Undefined();