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 if (QQmlValueTypeFactory::isValueType(v.userType()) && engine->engine()) {
444 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
445 QQmlValueType *valueType = ep->valueTypes[v.userType()];
447 return engine->newValueType(object, property.coreIndex, valueType); // VariantReference value-type.
449 return engine->fromVariant(v);
450 } else if (QQmlValueTypeFactory::isValueType((uint)property.propType)
451 && engine->engine()) {
452 Q_ASSERT(notifier == 0);
454 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
455 QQmlValueType *valueType = ep->valueTypes[property.propType];
457 return engine->newValueType(object, property.coreIndex, valueType);
459 Q_ASSERT(notifier == 0);
461 // see if it's a sequence type
462 bool succeeded = false;
463 v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex,
469 if (property.propType == QMetaType::UnknownType) {
470 QMetaProperty p = object->metaObject()->property(property.coreIndex);
471 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
472 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
473 return v8::Undefined();
475 QVariant v(property.propType, (void *)0);
476 ReadFunction(object, property, v.data(), notifier);
477 return engine->fromVariant(v);
481 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
482 v8::Handle<v8::Value> *objectHandle,
483 const QHashedV8String &property,
484 QV8QObjectWrapper::RevisionMode revisionMode)
486 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
487 // will be a faster way of creating QObject method objects.
488 struct MethodClosure {
489 static v8::Handle<v8::Value> create(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)
496 Q_ASSERT(argv[0]->IsObject());
497 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
499 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
500 v8::Handle<v8::Value> *objectHandle,
502 v8::Handle<v8::Value> argv[] = {
503 objectHandle?*objectHandle:engine->newQObject(object),
504 v8::Integer::New(index),
505 v8::Context::GetCallingQmlGlobal()
507 Q_ASSERT(argv[0]->IsObject());
508 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
512 if (QQmlData::wasDeleted(object))
513 return v8::Handle<v8::Value>();
516 // Comparing the hash first actually makes a measurable difference here, at least on x86
517 quint32 hash = property.hash();
518 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
519 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
520 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
521 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
525 QQmlPropertyData local;
526 QQmlPropertyData *result = 0;
528 QQmlData *ddata = QQmlData::get(object, false);
529 if (ddata && ddata->propertyCache)
530 result = ddata->propertyCache->property(property);
532 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
536 return v8::Handle<v8::Value>();
538 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
539 QQmlData *ddata = QQmlData::get(object);
540 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
541 return v8::Handle<v8::Value>();
544 if (result->isFunction() && !result->isVarProperty()) {
545 if (result->isVMEFunction()) {
546 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
548 return vmemo->vmeMethod(result->coreIndex);
549 } else if (result->isV8Function()) {
550 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
551 } else if (result->isSignalHandler()) {
552 v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
553 QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
554 handler->SetExternalResource(r);
557 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
561 QQmlEnginePrivate *ep =
562 engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
564 if (result->hasAccessors()) {
566 QQmlNotifier **nptr = 0;
568 if (ep && ep->propertyCapture && result->accessors->notifier)
571 v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
573 if (result->accessors->notifier) {
574 if (n) ep->captureProperty(n);
576 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
582 if (ep && !result->isConstant())
583 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
585 if (result->isVarProperty()) {
586 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
588 return vmemo->vmeProperty(result->coreIndex);
589 } else if (result->isDirect()) {
590 return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
592 return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
596 // Setter for writable properties. Shared between the interceptor and fast property accessor
597 static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
598 v8::Handle<v8::Value> value)
600 QQmlBinding *newBinding = 0;
601 if (value->IsFunction()) {
602 if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
603 if (!property->isVarProperty() && property->propType != qMetaTypeId<QJSValue>()) {
604 // assigning a JS function to a non var or QJSValue property or is not allowed.
605 QString error = QLatin1String("Cannot assign JavaScript function to ");
606 if (!QMetaType::typeName(property->propType))
607 error += QLatin1String("[unknown property type]");
609 error += QLatin1String(QMetaType::typeName(property->propType));
610 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
614 // binding assignment.
615 QQmlContextData *context = engine->callingContext();
616 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
618 v8::Local<v8::StackTrace> trace =
619 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
620 v8::StackTrace::kScriptName));
621 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
622 int lineNumber = frame->GetLineNumber();
623 int columnNumber = frame->GetColumn();
624 QString url = engine->toString(frame->GetScriptName());
626 newBinding = new QQmlBinding(&function, object, context, url, lineNumber, columnNumber);
627 newBinding->setTarget(object, *property, context);
628 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
629 QQmlBinding::RequiresThisObject);
633 QQmlAbstractBinding *oldBinding =
634 QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
636 oldBinding->destroy();
638 if (!newBinding && property->isVarProperty()) {
639 // allow assignment of "special" values (null, undefined, function) to var properties
640 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
642 vmemo->setVMEProperty(property->coreIndex, value);
646 #define PROPERTY_STORE(cpptype, value) \
650 void *argv[] = { &o, 0, &status, &flags }; \
651 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
653 if (value->IsNull() && property->isQObject()) {
654 PROPERTY_STORE(QObject*, 0);
655 } else if (value->IsUndefined() && property->isResettable()) {
657 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
658 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
659 PROPERTY_STORE(QVariant, QVariant());
660 } else if (value->IsUndefined() && property->propType == QMetaType::QJsonValue) {
661 PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
662 } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) {
663 PROPERTY_STORE(QJSValue, engine->scriptValueFromInternal(value));
664 } else if (value->IsUndefined()) {
665 QString error = QLatin1String("Cannot assign [undefined] to ");
666 if (!QMetaType::typeName(property->propType))
667 error += QLatin1String("[unknown property type]");
669 error += QLatin1String(QMetaType::typeName(property->propType));
670 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
671 } else if (value->IsFunction()) {
672 // this is handled by the binding creation above
673 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
674 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
675 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
676 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
677 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
678 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
679 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
680 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
681 } else if (property->propType == QMetaType::QString && value->IsString()) {
682 PROPERTY_STORE(QString, engine->toString(value->ToString()));
683 } else if (property->isVarProperty()) {
684 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
686 vmemo->setVMEProperty(property->coreIndex, value);
689 if (property->isQList())
690 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
692 v = engine->toVariant(value, property->propType);
694 QQmlContextData *context = engine->callingContext();
695 if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
696 const char *valueType = 0;
697 if (v.userType() == QVariant::Invalid) valueType = "null";
698 else valueType = QMetaType::typeName(v.userType());
700 const char *targetTypeName = QMetaType::typeName(property->propType);
702 targetTypeName = "an unregistered type";
704 QString error = QLatin1String("Cannot assign ") +
705 QLatin1String(valueType) +
706 QLatin1String(" to ") +
707 QLatin1String(targetTypeName);
708 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
713 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
714 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
716 if (engine->qobjectWrapper()->m_toStringString == property ||
717 engine->qobjectWrapper()->m_destroyString == property)
720 if (QQmlData::wasDeleted(object))
723 QQmlPropertyData local;
724 QQmlPropertyData *result = 0;
725 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
730 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
731 QQmlData *ddata = QQmlData::get(object);
732 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
736 if (!result->isWritable() && !result->isQList()) {
737 QString error = QLatin1String("Cannot assign to read-only property \"") +
738 engine->toString(property.string()) + QLatin1Char('\"');
739 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
743 StoreProperty(engine, object, result, value);
748 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
749 const v8::AccessorInfo &info)
751 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
753 if (QQmlData::wasDeleted(resource->object))
754 return v8::Handle<v8::Value>();
756 QObject *object = resource->object;
758 QHashedV8String propertystring(property);
760 QV8Engine *v8engine = resource->engine;
761 v8::Handle<v8::Value> This = info.This();
762 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
763 QV8QObjectWrapper::IgnoreRevision);
764 if (!result.IsEmpty())
767 if (QV8Engine::startsWithUpper(property)) {
768 // Check for attached properties
769 QQmlContextData *context = v8engine->callingContext();
771 if (context && context->imports) {
772 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
775 if (r.scriptIndex != -1) {
776 return v8::Undefined();
778 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
779 } else if (r.importNamespace) {
780 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
781 QV8TypeWrapper::ExcludeEnums);
783 Q_ASSERT(!"Unreachable");
788 return v8::Handle<v8::Value>();
791 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
792 v8::Local<v8::Value> value,
793 const v8::AccessorInfo &info)
795 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
797 if (QQmlData::wasDeleted(resource->object))
800 QObject *object = resource->object;
802 QHashedV8String propertystring(property);
804 QV8Engine *v8engine = resource->engine;
805 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
808 QString error = QLatin1String("Cannot assign to non-existent property \"") +
809 v8engine->toString(property) + QLatin1Char('\"');
810 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
817 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
818 const v8::AccessorInfo &info)
820 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
822 if (resource->object.isNull())
823 return v8::Handle<v8::Integer>();
825 QV8Engine *engine = resource->engine;
826 QObject *object = resource->object;
828 QHashedV8String propertystring(property);
830 QQmlPropertyData local;
831 QQmlPropertyData *result = 0;
832 result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local);
835 return v8::Handle<v8::Integer>();
836 else if (!result->isWritable() && !result->isQList())
837 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
839 return v8::Integer::New(v8::DontDelete);
842 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
844 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
846 if (resource->object.isNull())
847 return v8::Array::New();
849 QObject *object = resource->object;
853 QQmlEnginePrivate *ep = resource->engine->engine()
854 ? QQmlEnginePrivate::get(resource->engine->engine())
857 QQmlPropertyCache *cache = 0;
858 QQmlData *ddata = QQmlData::get(object);
860 cache = ddata->propertyCache;
863 cache = ep ? ep->cache(object) : 0;
865 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
867 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
868 const QMetaObject *mo = object->metaObject();
869 int pc = mo->propertyCount();
870 int po = mo->propertyOffset();
871 for (int i=po; i<pc; ++i)
872 result << QString::fromUtf8(mo->property(i).name());
875 result = cache->propertyNames();
878 v8::Local<v8::Array> rv = v8::Array::New(result.count());
880 for (int ii = 0; ii < result.count(); ++ii)
881 rv->Set(ii, resource->engine->toString(result.at(ii)));
886 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
887 const v8::AccessorInfo& info)
889 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
891 if (QQmlData::wasDeleted(resource->object))
894 QObject *object = resource->object;
896 QQmlPropertyData *property =
897 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
899 int index = property->coreIndex;
901 QQmlData *ddata = QQmlData::get(object, false);
903 Q_ASSERT(ddata->propertyCache);
905 QQmlPropertyData *pdata = ddata->propertyCache->property(index);
908 Q_ASSERT(pdata->isWritable() || pdata->isQList());
910 StoreProperty(resource->engine, object, pdata, value);
913 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
914 const v8::AccessorInfo& info)
916 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
918 if (QQmlData::wasDeleted(resource->object))
921 QV8Engine *v8engine = resource->engine;
923 QString error = QLatin1String("Cannot assign to read-only property \"") +
924 v8engine->toString(property) + QLatin1Char('\"');
925 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
928 void QV8QObjectWrapper::WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *wrapper)
930 Q_ASSERT(handle->IsObject());
931 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
934 static_cast<QV8QObjectWrapper*>(wrapper)->unregisterWeakQObjectReference(resource);
935 if (static_cast<QV8QObjectWrapper*>(wrapper)->deleteWeakQObject(resource, false)) {
936 qPersistentDispose(handle); // dispose.
938 handle.MakeWeak(0, WeakQObjectReferenceCallback); // revive.
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);
1151 // returns true if the object's qqmldata v8object handle should
1152 // be disposed by the caller, false if it should not be (due to
1153 // creation status, etc).
1154 bool QV8QObjectWrapper::deleteWeakQObject(QV8QObjectResource *resource, bool calledFromEngineDtor)
1156 QObject *object = resource->object;
1158 QQmlData *ddata = QQmlData::get(object, false);
1160 if (!calledFromEngineDtor && ddata->rootObjectInCreation) {
1161 // if weak ref callback is triggered (by gc) for a root object
1162 // prior to completion of creation, we should NOT delete it.
1166 ddata->v8object.Clear();
1167 if (!object->parent() && !ddata->indestructible) {
1168 // This object is notionally destroyed now
1169 if (ddata->ownContext && ddata->context)
1170 ddata->context->emitDestruction();
1171 ddata->isQueuedForDeletion = true;
1172 object->deleteLater();
1180 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1182 if (object->IsFunction())
1183 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1185 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1186 return qMakePair(resource->object.data(), resource->index);
1188 return qMakePair((QObject *)0, -1);
1191 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1193 v8::ScriptOrigin origin = function->GetScriptOrigin();
1194 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1196 // This is one of our special QObject method wrappers
1197 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1198 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1200 if (data->IsArray()) {
1201 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1202 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1205 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1208 return qMakePair((QObject *)0, -1);
1211 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1214 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1215 ~QV8QObjectConnectionList();
1219 : needsDestroy(false) {}
1220 Connection(const Connection &other)
1221 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1222 Connection &operator=(const Connection &other) {
1223 thisObject = other.thisObject;
1224 function = other.function;
1225 needsDestroy = other.needsDestroy;
1229 v8::Persistent<v8::Object> thisObject;
1230 v8::Persistent<v8::Function> function;
1233 qPersistentDispose(thisObject);
1234 qPersistentDispose(function);
1240 struct ConnectionList : public QList<Connection> {
1241 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1242 int connectionsInUse;
1243 bool connectionsNeedClean;
1248 typedef QHash<int, ConnectionList> SlotHash;
1253 virtual void objectDestroyed(QObject *);
1254 virtual int qt_metacall(QMetaObject::Call, int, void **);
1257 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1258 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1262 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1264 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1265 QList<Connection> &connections = *iter;
1266 for (int ii = 0; ii < connections.count(); ++ii) {
1267 qPersistentDispose(connections[ii].thisObject);
1268 qPersistentDispose(connections[ii].function);
1274 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1276 engine->qobjectWrapper()->m_connections.remove(object);
1279 needsDestroy = true;
1284 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1286 if (method == QMetaObject::InvokeMetaMethod) {
1287 SlotHash::Iterator iter = slotHash.find(index);
1288 if (iter == slotHash.end())
1290 ConnectionList &connectionList = *iter;
1291 if (connectionList.isEmpty())
1296 connectionList.connectionsInUse++;
1298 QList<Connection> connections = connectionList;
1300 QVarLengthArray<int, 9> dummy;
1301 int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1303 v8::HandleScope handle_scope;
1304 v8::Context::Scope scope(engine->context());
1306 int argCount = argsTypes?argsTypes[0]:0;
1307 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1309 for (int ii = 0; ii < argCount; ++ii) {
1310 int type = argsTypes[ii + 1];
1311 if (type == qMetaTypeId<QVariant>()) {
1312 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1314 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1318 for (int ii = 0; ii < connections.count(); ++ii) {
1319 Connection &connection = connections[ii];
1320 if (connection.needsDestroy)
1323 v8::TryCatch try_catch;
1324 if (connection.thisObject.IsEmpty()) {
1325 connection.function->Call(engine->global(), argCount, args.data());
1327 connection.function->Call(connection.thisObject, argCount, args.data());
1330 if (try_catch.HasCaught()) {
1332 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1333 v8::Local<v8::Message> message = try_catch.Message();
1334 if (!message.IsEmpty())
1335 QQmlExpressionPrivate::exceptionToError(message, error);
1336 QQmlEnginePrivate::get(engine->engine())->warning(error);
1340 connectionList.connectionsInUse--;
1341 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1342 for (QList<Connection>::Iterator iter = connectionList.begin();
1343 iter != connectionList.end(); ) {
1344 if (iter->needsDestroy) {
1346 iter = connectionList.erase(iter);
1354 if (inUse == 0 && needsDestroy)
1361 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1363 if (args.Length() == 0)
1364 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1366 QV8Engine *engine = V8ENGINE();
1368 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1369 QObject *signalObject = signalInfo.first;
1370 int signalIndex = signalInfo.second;
1372 if (signalIndex == -1)
1373 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1376 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1378 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1379 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1381 v8::Local<v8::Value> functionValue;
1382 v8::Local<v8::Value> functionThisValue;
1384 if (args.Length() == 1) {
1385 functionValue = args[0];
1387 functionThisValue = args[0];
1388 functionValue = args[1];
1391 if (!functionValue->IsFunction())
1392 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1394 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1395 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1397 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1398 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1399 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1400 if (iter == connections.end())
1401 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1403 QV8QObjectConnectionList *connectionList = *iter;
1404 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1405 if (slotIter == connectionList->slotHash.end()) {
1406 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1407 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1410 QV8QObjectConnectionList::Connection connection;
1411 if (!functionThisValue.IsEmpty())
1412 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1413 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1415 slotIter->append(connection);
1417 return v8::Undefined();
1420 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1422 if (args.Length() == 0)
1423 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1425 QV8Engine *engine = V8ENGINE();
1427 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1428 QObject *signalObject = signalInfo.first;
1429 int signalIndex = signalInfo.second;
1431 if (signalIndex == -1)
1432 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1435 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1437 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1438 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1440 v8::Local<v8::Value> functionValue;
1441 v8::Local<v8::Value> functionThisValue;
1443 if (args.Length() == 1) {
1444 functionValue = args[0];
1446 functionThisValue = args[0];
1447 functionValue = args[1];
1450 if (!functionValue->IsFunction())
1451 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1453 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1454 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1456 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1457 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1458 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1459 if (iter == connectionsList.end())
1460 return v8::Undefined(); // Nothing to disconnect from
1462 QV8QObjectConnectionList *connectionList = *iter;
1463 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1464 if (slotIter == connectionList->slotHash.end())
1465 return v8::Undefined(); // Nothing to disconnect from
1467 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1469 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1470 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1472 if (functionData.second != -1) {
1473 // This is a QObject function wrapper
1474 for (int ii = 0; ii < connections.count(); ++ii) {
1475 QV8QObjectConnectionList::Connection &connection = connections[ii];
1477 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1478 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1480 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1481 if (connectedFunctionData == functionData) {
1483 if (connections.connectionsInUse) {
1484 connection.needsDestroy = true;
1485 connections.connectionsNeedClean = true;
1487 connection.dispose();
1488 connections.removeAt(ii);
1490 return v8::Undefined();
1496 // This is a normal JS function
1497 for (int ii = 0; ii < connections.count(); ++ii) {
1498 QV8QObjectConnectionList::Connection &connection = connections[ii];
1499 if (connection.function->StrictEquals(function) &&
1500 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1501 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1503 if (connections.connectionsInUse) {
1504 connection.needsDestroy = true;
1505 connections.connectionsNeedClean = true;
1507 connection.dispose();
1508 connections.removeAt(ii);
1510 return v8::Undefined();
1515 return v8::Undefined();
1519 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1521 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1523 Only searches for real properties of \a object (including methods), not attached properties etc.
1527 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1529 Set the \a property of \a object to \a value.
1531 Returns true if the property was "set" - even if this results in an exception being thrown -
1532 and false if the object has no such property.
1534 Only searches for real properties of \a object (including methods), not attached properties etc.
1540 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1541 int Length() const { return _length; }
1542 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1546 v8::Handle<v8::Object> *_args;
1550 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1551 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1555 // Special handling is required for value types.
1556 // We need to save the current value in a temporary,
1557 // and reapply it after converting all arguments.
1558 // This avoids the "overwriting copy-value-type-value"
1559 // problem during Q_INVOKABLE function invocation.
1560 QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1561 QVariant valueTypeValue;
1562 if (valueTypeObject)
1563 valueTypeValue = valueTypeObject->value();
1565 // Convert all arguments.
1566 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1567 args[0].initAsType(returnType);
1568 for (int ii = 0; ii < argCount; ++ii)
1569 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1570 QVarLengthArray<void *, 9> argData(args.count());
1571 for (int ii = 0; ii < args.count(); ++ii)
1572 argData[ii] = args[ii].dataPtr();
1574 // Reinstate saved value type object value if required.
1575 if (valueTypeObject)
1576 valueTypeObject->setValue(valueTypeValue);
1578 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1580 return args[0].toValue(engine);
1582 } else if (returnType != QMetaType::Void) {
1585 arg.initAsType(returnType);
1587 void *args[] = { arg.dataPtr() };
1589 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1591 return arg.toValue(engine);
1595 void *args[] = { 0 };
1596 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1597 return v8::Undefined();
1603 Returns the match score for converting \a actual to be of type \a conversionType. A
1604 zero score means "perfect match" whereas a higher score is worse.
1606 The conversion table is copied out of the QtScript callQtMethod() function.
1608 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1610 if (actual->IsNumber()) {
1611 switch (conversionType) {
1612 case QMetaType::Double:
1614 case QMetaType::Float:
1616 case QMetaType::LongLong:
1617 case QMetaType::ULongLong:
1619 case QMetaType::Long:
1620 case QMetaType::ULong:
1622 case QMetaType::Int:
1623 case QMetaType::UInt:
1625 case QMetaType::Short:
1626 case QMetaType::UShort:
1629 case QMetaType::Char:
1630 case QMetaType::UChar:
1632 case QMetaType::QJsonValue:
1637 } else if (actual->IsString()) {
1638 switch (conversionType) {
1639 case QMetaType::QString:
1641 case QMetaType::QJsonValue:
1646 } else if (actual->IsBoolean()) {
1647 switch (conversionType) {
1648 case QMetaType::Bool:
1650 case QMetaType::QJsonValue:
1655 } else if (actual->IsDate()) {
1656 switch (conversionType) {
1657 case QMetaType::QDateTime:
1659 case QMetaType::QDate:
1661 case QMetaType::QTime:
1666 } else if (actual->IsRegExp()) {
1667 switch (conversionType) {
1668 case QMetaType::QRegExp:
1673 } else if (actual->IsArray()) {
1674 switch (conversionType) {
1675 case QMetaType::QJsonArray:
1677 case QMetaType::QStringList:
1678 case QMetaType::QVariantList:
1680 case QMetaType::QVector4D:
1681 case QMetaType::QMatrix4x4:
1683 case QMetaType::QVector3D:
1688 } else if (actual->IsNull()) {
1689 switch (conversionType) {
1690 case QMetaType::VoidStar:
1691 case QMetaType::QObjectStar:
1692 case QMetaType::QJsonValue:
1695 const char *typeName = QMetaType::typeName(conversionType);
1696 if (typeName && typeName[strlen(typeName) - 1] == '*')
1702 } else if (actual->IsObject()) {
1703 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1705 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1706 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1707 switch (conversionType) {
1708 case QMetaType::QObjectStar:
1713 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1714 if (conversionType == qMetaTypeId<QVariant>())
1716 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1720 } else if (r && r->resourceType() == QV8ObjectResource::ValueTypeType) {
1721 if (r->engine->toVariant(actual, -1).userType() == conversionType)
1724 } else if (conversionType == QMetaType::QJsonObject) {
1735 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1741 int classInfoCount, classInfoData;
1742 int methodCount, methodData;
1745 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1749 Returns the next related method, if one, or 0.
1751 static const QQmlPropertyData * RelatedMethod(QObject *object,
1752 const QQmlPropertyData *current,
1753 QQmlPropertyData &dummy)
1755 QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1756 if (!current->isOverload())
1759 Q_ASSERT(!current->overrideIndexIsProperty);
1762 return cache->method(current->overrideIndex);
1764 const QMetaObject *mo = object->metaObject();
1765 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1767 while (methodOffset > current->overrideIndex) {
1768 mo = mo->superClass();
1769 methodOffset -= QMetaObject_methods(mo);
1772 QMetaMethod method = mo->method(current->overrideIndex);
1775 // Look for overloaded methods
1776 QByteArray methodName = method.name();
1777 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1778 if (methodName == mo->method(ii).name()) {
1779 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1780 dummy.overrideIndexIsProperty = 0;
1781 dummy.overrideIndex = ii;
1790 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1791 QV8Engine *engine, CallArgs &callArgs)
1793 QByteArray unknownTypeError;
1795 int returnType = QQmlPropertyCache::methodReturnType(object, data, &unknownTypeError);
1797 if (returnType == QMetaType::UnknownType) {
1798 QString typeName = QString::fromLatin1(unknownTypeError);
1799 QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName);
1800 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1801 return v8::Handle<v8::Value>();
1804 if (data.hasArguments()) {
1807 QVarLengthArray<int, 9> dummy;
1809 args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1813 QString typeName = QString::fromLatin1(unknownTypeError);
1814 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1815 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1816 return v8::Handle<v8::Value>();
1819 if (args[0] > callArgs.Length()) {
1820 QString error = QLatin1String("Insufficient arguments");
1821 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1822 return v8::Handle<v8::Value>();
1825 return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs);
1829 return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs);
1835 Resolve the overloaded method to call. The algorithm works conceptually like this:
1836 1. Resolve the set of overloads it is *possible* to call.
1837 Impossible overloads include those that have too many parameters or have parameters
1839 2. Filter the set of overloads to only contain those with the closest number of
1841 For example, if we are called with 3 parameters and there are 2 overloads that
1842 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1843 3. Find the best remaining overload based on its match score.
1844 If two or more overloads have the same match score, call the last one. The match
1845 score is constructed by adding the matchScore() result for each of the parameters.
1847 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1848 QV8Engine *engine, CallArgs &callArgs)
1850 int argumentCount = callArgs.Length();
1852 const QQmlPropertyData *best = 0;
1853 int bestParameterScore = INT_MAX;
1854 int bestMatchScore = INT_MAX;
1856 // Special handling is required for value types.
1857 // We need to save the current value in a temporary,
1858 // and reapply it after converting all arguments.
1859 // This avoids the "overwriting copy-value-type-value"
1860 // problem during Q_INVOKABLE function invocation.
1861 QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1862 QVariant valueTypeValue;
1863 if (valueTypeObject)
1864 valueTypeValue = valueTypeObject->value();
1866 QQmlPropertyData dummy;
1867 const QQmlPropertyData *attempt = &data;
1870 QVarLengthArray<int, 9> dummy;
1871 int methodArgumentCount = 0;
1872 int *methodArgTypes = 0;
1873 if (attempt->hasArguments()) {
1874 typedef QQmlPropertyCache PC;
1875 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1876 if (!args) // Must be an unknown argument
1879 methodArgumentCount = args[0];
1880 methodArgTypes = args + 1;
1883 if (methodArgumentCount > argumentCount)
1884 continue; // We don't have sufficient arguments to call this method
1886 int methodParameterScore = argumentCount - methodArgumentCount;
1887 if (methodParameterScore > bestParameterScore)
1888 continue; // We already have a better option
1890 int methodMatchScore = 0;
1891 for (int ii = 0; ii < methodArgumentCount; ++ii)
1892 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1894 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1896 bestParameterScore = methodParameterScore;
1897 bestMatchScore = methodMatchScore;
1900 if (bestParameterScore == 0 && bestMatchScore == 0)
1901 break; // We can't get better than that
1903 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1906 if (valueTypeObject)
1907 valueTypeObject->setValue(valueTypeValue);
1908 return CallPrecise(object, *best, engine, callArgs);
1910 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1911 const QQmlPropertyData *candidate = &data;
1913 error += QLatin1String("\n ") +
1914 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData());
1915 candidate = RelatedMethod(object, candidate, dummy);
1918 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1919 return v8::Handle<v8::Value>();
1923 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1927 QString objectName = object->objectName();
1929 result += QString::fromUtf8(object->metaObject()->className());
1930 result += QLatin1String("(0x");
1931 result += QString::number((quintptr)object,16);
1933 if (!objectName.isEmpty()) {
1934 result += QLatin1String(", \"");
1935 result += objectName;
1936 result += QLatin1Char('\"');
1939 result += QLatin1Char(')');
1941 result = QLatin1String("null");
1944 return engine->toString(result);
1947 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1949 QQmlData *ddata = QQmlData::get(object, false);
1950 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation) {
1951 const char *error = "Invalid attempt to destroy() an indestructible object";
1952 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1953 return v8::Undefined();
1958 delay = args->Get(0)->Uint32Value();
1961 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1963 object->deleteLater();
1965 return v8::Undefined();
1968 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1970 // object, index, qmlglobal, argCount, args
1971 Q_ASSERT(args.Length() == 5);
1972 Q_ASSERT(args[0]->IsObject());
1974 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1977 return v8::Undefined();
1979 int argCount = args[3]->Int32Value();
1980 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1982 // Special hack to return info about this closure.
1983 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1984 v8::Local<v8::Array> data = v8::Array::New(2);
1985 data->Set(0, args[0]);
1986 data->Set(1, args[1]);
1990 QObject *object = resource->object;
1991 int index = args[1]->Int32Value();
1994 return v8::Undefined();
1997 // Builtin functions
1998 if (index == QOBJECT_TOSTRING_INDEX) {
1999 return ToString(resource->engine, object, argCount, arguments);
2000 } else if (index == QOBJECT_DESTROY_INDEX) {
2001 return Destroy(resource->engine, object, argCount, arguments);
2003 return v8::Undefined();
2007 QQmlPropertyData method;
2009 if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
2010 if (ddata->propertyCache) {
2011 QQmlPropertyData *d = ddata->propertyCache->method(index);
2013 return v8::Undefined();
2018 if (method.coreIndex == -1) {
2019 method.load(object->metaObject()->method(index));
2021 if (method.coreIndex == -1)
2022 return v8::Undefined();
2025 if (method.isV8Function()) {
2026 v8::Handle<v8::Value> rv;
2027 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
2029 QQmlV8Function func(argCount, arguments, rv, qmlglobal,
2030 resource->engine->contextWrapper()->context(qmlglobal),
2032 QQmlV8Function *funcptr = &func;
2034 void *args[] = { 0, &funcptr };
2035 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
2037 if (rv.IsEmpty()) return v8::Undefined();
2041 CallArgs callArgs(argCount, &arguments);
2042 if (!method.isOverload()) {
2043 return CallPrecise(object, method, resource->engine, callArgs);
2045 return CallOverloaded(object, method, resource->engine, callArgs);
2049 CallArgument::CallArgument()
2050 : type(QVariant::Invalid)
2054 CallArgument::~CallArgument()
2059 void CallArgument::cleanup()
2061 if (type == QMetaType::QString) {
2062 qstringPtr->~QString();
2063 } else if (type == -1 || type == QMetaType::QVariant) {
2064 qvariantPtr->~QVariant();
2065 } else if (type == qMetaTypeId<QJSValue>()) {
2066 qjsValuePtr->~QJSValue();
2067 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2068 qlistPtr->~QList<QObject *>();
2069 } else if (type == QMetaType::QJsonArray) {
2070 jsonArrayPtr->~QJsonArray();
2071 } else if (type == QMetaType::QJsonObject) {
2072 jsonObjectPtr->~QJsonObject();
2073 } else if (type == QMetaType::QJsonValue) {
2074 jsonValuePtr->~QJsonValue();
2078 void *CallArgument::dataPtr()
2081 return qvariantPtr->data();
2083 return (void *)&allocData;
2086 void CallArgument::initAsType(int callType)
2088 if (type != 0) { cleanup(); type = 0; }
2089 if (callType == QMetaType::UnknownType) return;
2091 if (callType == qMetaTypeId<QJSValue>()) {
2092 qjsValuePtr = new (&allocData) QJSValue();
2094 } else if (callType == QMetaType::Int ||
2095 callType == QMetaType::UInt ||
2096 callType == QMetaType::Bool ||
2097 callType == QMetaType::Double ||
2098 callType == QMetaType::Float) {
2100 } else if (callType == QMetaType::QObjectStar) {
2103 } else if (callType == QMetaType::QString) {
2104 qstringPtr = new (&allocData) QString();
2106 } else if (callType == QMetaType::QVariant) {
2108 qvariantPtr = new (&allocData) QVariant();
2109 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2111 qlistPtr = new (&allocData) QList<QObject *>();
2112 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2114 handlePtr = new (&allocData) QQmlV8Handle;
2115 } else if (callType == QMetaType::QJsonArray) {
2117 jsonArrayPtr = new (&allocData) QJsonArray();
2118 } else if (callType == QMetaType::QJsonObject) {
2120 jsonObjectPtr = new (&allocData) QJsonObject();
2121 } else if (callType == QMetaType::QJsonValue) {
2123 jsonValuePtr = new (&allocData) QJsonValue();
2124 } else if (callType == QMetaType::Void) {
2126 qvariantPtr = new (&allocData) QVariant();
2129 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2133 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2135 if (type != 0) { cleanup(); type = 0; }
2137 if (callType == qMetaTypeId<QJSValue>()) {
2138 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2139 type = qMetaTypeId<QJSValue>();
2140 } else if (callType == QMetaType::Int) {
2141 intValue = quint32(value->Int32Value());
2143 } else if (callType == QMetaType::UInt) {
2144 intValue = quint32(value->Uint32Value());
2146 } else if (callType == QMetaType::Bool) {
2147 boolValue = value->BooleanValue();
2149 } else if (callType == QMetaType::Double) {
2150 doubleValue = double(value->NumberValue());
2152 } else if (callType == QMetaType::Float) {
2153 floatValue = float(value->NumberValue());
2155 } else if (callType == QMetaType::QString) {
2156 if (value->IsNull() || value->IsUndefined())
2157 qstringPtr = new (&allocData) QString();
2159 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2161 } else if (callType == QMetaType::QObjectStar) {
2162 qobjectPtr = engine->toQObject(value);
2164 } else if (callType == qMetaTypeId<QVariant>()) {
2165 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2167 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2168 qlistPtr = new (&allocData) QList<QObject *>();
2169 if (value->IsArray()) {
2170 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2171 uint32_t length = array->Length();
2172 for (uint32_t ii = 0; ii < length; ++ii)
2173 qlistPtr->append(engine->toQObject(array->Get(ii)));
2175 qlistPtr->append(engine->toQObject(value));
2178 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2179 handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2181 } else if (callType == QMetaType::QJsonArray) {
2182 jsonArrayPtr = new (&allocData) QJsonArray(engine->jsonArrayFromJS(value));
2184 } else if (callType == QMetaType::QJsonObject) {
2185 jsonObjectPtr = new (&allocData) QJsonObject(engine->jsonObjectFromJS(value));
2187 } else if (callType == QMetaType::QJsonValue) {
2188 jsonValuePtr = new (&allocData) QJsonValue(engine->jsonValueFromJS(value));
2190 } else if (callType == QMetaType::Void) {
2191 *qvariantPtr = QVariant();
2193 qvariantPtr = new (&allocData) QVariant();
2196 QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2197 QVariant v = engine->toVariant(value, -1); // why -1 instead of callType?
2199 if (v.userType() == callType) {
2201 } else if (v.canConvert(callType)) {
2203 qvariantPtr->convert(callType);
2204 } else if (engine->sequenceWrapper()->isSequenceType(callType) && v.userType() == qMetaTypeId<QVariantList>()) {
2205 // convert the JS array to a sequence of the correct type.
2206 QVariant seqV = engine->toVariant(value, callType);
2207 *qvariantPtr = seqV;
2209 QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
2211 QObject *obj = ep->toQObject(v);
2213 if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo))
2216 *qvariantPtr = QVariant(callType, &obj);
2218 *qvariantPtr = QVariant(callType, (void *)0);
2224 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2226 if (type == qMetaTypeId<QJSValue>()) {
2227 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2228 } else if (type == QMetaType::Int) {
2229 return v8::Integer::New(int(intValue));
2230 } else if (type == QMetaType::UInt) {
2231 return v8::Integer::NewFromUnsigned(intValue);
2232 } else if (type == QMetaType::Bool) {
2233 return v8::Boolean::New(boolValue);
2234 } else if (type == QMetaType::Double) {
2235 return v8::Number::New(doubleValue);
2236 } else if (type == QMetaType::Float) {
2237 return v8::Number::New(floatValue);
2238 } else if (type == QMetaType::QString) {
2239 return engine->toString(*qstringPtr);
2240 } else if (type == QMetaType::QObjectStar) {
2241 QObject *object = qobjectPtr;
2243 QQmlData::get(object, true)->setImplicitDestructible();
2244 return engine->newQObject(object);
2245 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2246 // XXX Can this be made more by using Array as a prototype and implementing
2247 // directly against QList<QObject*>?
2248 QList<QObject *> &list = *qlistPtr;
2249 v8::Local<v8::Array> array = v8::Array::New(list.count());
2250 for (int ii = 0; ii < list.count(); ++ii)
2251 array->Set(ii, engine->newQObject(list.at(ii)));
2253 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2254 return handlePtr->toHandle();
2255 } else if (type == QMetaType::QJsonArray) {
2256 return engine->jsonArrayToJS(*jsonArrayPtr);
2257 } else if (type == QMetaType::QJsonObject) {
2258 return engine->jsonObjectToJS(*jsonObjectPtr);
2259 } else if (type == QMetaType::QJsonValue) {
2260 return engine->jsonValueToJS(*jsonValuePtr);
2261 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2262 QVariant value = *qvariantPtr;
2263 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2264 if (QObject *object = engine->toQObject(rv))
2265 QQmlData::get(object, true)->setImplicitDestructible();
2268 return v8::Undefined();