1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qv8qobjectwrapper_p.h"
43 #include "qv8contextwrapper_p.h"
44 #include "qv8engine_p.h"
46 #include <private/qqmlguard_p.h>
47 #include <private/qqmlpropertycache_p.h>
48 #include <private/qqmlengine_p.h>
49 #include <private/qqmlvmemetaobject_p.h>
50 #include <private/qqmlbinding_p.h>
51 #include <private/qjsvalue_p.h>
52 #include <private/qscript_impl_p.h>
53 #include <private/qqmlaccessors_p.h>
54 #include <private/qqmlexpression_p.h>
55 #include <private/qqmlglobal_p.h>
57 #include <QtQml/qjsvalue.h>
58 #include <QtCore/qjsonarray.h>
59 #include <QtCore/qjsonobject.h>
60 #include <QtCore/qjsonvalue.h>
61 #include <QtCore/qvarlengtharray.h>
62 #include <QtCore/qtimer.h>
63 #include <QtCore/qatomic.h>
65 Q_DECLARE_METATYPE(QJSValue);
66 Q_DECLARE_METATYPE(QQmlV8Handle);
70 #if defined(__GNUC__) && !defined(__INTEL_COMPILER)
71 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
72 // The code in this file does not violate strict aliasing, but GCC thinks it does
73 // so turn off the warnings for us to have a clean build
74 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
78 #define QOBJECT_TOSTRING_INDEX -2
79 #define QOBJECT_DESTROY_INDEX -3
81 // XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work
82 // correctly in a worker thread
84 class QV8QObjectInstance : public QQmlGuard<QObject>
87 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
88 : QQmlGuard<QObject>(o), wrapper(w)
94 qPersistentDispose(v8object);
97 virtual void objectDestroyed(QObject *o)
100 wrapper->m_taintedObjects.remove(o);
104 v8::Persistent<v8::Object> v8object;
105 QV8QObjectWrapper *wrapper;
108 class QV8SignalHandlerResource : public QV8ObjectResource
110 V8_RESOURCE_TYPE(SignalHandlerType)
112 QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index);
114 QQmlGuard<QObject> object;
120 template<typename A, typename B, typename C, typename D, typename E,
121 typename F, typename G, typename H>
123 template<typename Z, typename X>
125 char dummy[sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X)];
128 static const size_t Size = sizeof(SMax<A, SMax<B, SMax<C, SMax<D, SMax<E, SMax<F, SMax<G, H> > > > > > >);
131 struct CallArgument {
132 inline CallArgument();
133 inline ~CallArgument();
134 inline void *dataPtr();
136 inline void initAsType(int type);
137 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
138 inline v8::Handle<v8::Value> toValue(QV8Engine *);
141 CallArgument(const CallArgument &);
143 inline void cleanup();
152 char allocData[MaxSizeOf8<QVariant,
160 qint64 q_for_alignment;
163 // Pointers to allocData
166 QVariant *qvariantPtr;
167 QList<QObject *> *qlistPtr;
168 QJSValue *qjsValuePtr;
169 QQmlV8Handle *handlePtr;
170 QJsonArray *jsonArrayPtr;
171 QJsonObject *jsonObjectPtr;
172 QJsonValue *jsonValuePtr;
179 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
180 : QV8ObjectResource(engine), object(object)
184 QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index)
185 : QV8ObjectResource(engine), object(object), index(index)
189 static QAtomicInt objectIdCounter(1);
191 QV8QObjectWrapper::QV8QObjectWrapper()
192 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
196 QV8QObjectWrapper::~QV8QObjectWrapper()
198 for (TaintedHash::Iterator iter = m_taintedObjects.begin();
199 iter != m_taintedObjects.end();
201 (*iter)->wrapper = 0;
203 m_taintedObjects.clear();
206 void QV8QObjectWrapper::destroy()
208 qDeleteAll(m_connections);
209 m_connections.clear();
211 qPersistentDispose(m_hiddenObject);
212 qPersistentDispose(m_destroySymbol);
213 qPersistentDispose(m_toStringSymbol);
214 qPersistentDispose(m_signalHandlerConstructor);
215 qPersistentDispose(m_methodConstructor);
216 qPersistentDispose(m_constructor);
218 QIntrusiveList<QV8QObjectResource, &QV8QObjectResource::weakResource>::iterator i = m_javaScriptOwnedWeakQObjects.begin();
219 for (; i != m_javaScriptOwnedWeakQObjects.end(); ++i) {
220 QV8QObjectResource *resource = *i;
222 deleteWeakQObject(resource, true);
226 struct ReadAccessor {
227 static inline void Indirect(QObject *object, const QQmlPropertyData &property,
228 void *output, QQmlNotifier **n)
233 void *args[] = { output, 0 };
234 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
237 static inline void Direct(QObject *object, const QQmlPropertyData &property,
238 void *output, QQmlNotifier **n)
243 void *args[] = { output, 0 };
244 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
247 static inline void Accessor(QObject *object, const QQmlPropertyData &property,
248 void *output, QQmlNotifier **n)
250 Q_ASSERT(property.accessors);
252 property.accessors->read(object, property.accessorData, output);
253 if (n) property.accessors->notifier(object, property.accessorData, n);
257 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, int v)
258 { return v8::Integer::New(v); }
259 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, uint v)
260 { return v8::Integer::NewFromUnsigned(v); }
261 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, bool v)
262 { return v8::Boolean::New(v); }
263 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, const QString &v)
264 { return e->toString(v); }
265 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, float v)
266 { return v8::Number::New(v); }
267 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, double v)
268 { return v8::Number::New(v); }
269 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, QObject *v)
270 { return e->newQObject(v); }
272 template<typename T, void (*ReadFunction)(QObject *, const QQmlPropertyData &,
273 void *, QQmlNotifier **)>
274 static v8::Handle<v8::Value> GenericValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info)
276 v8::Handle<v8::Object> This = info.This();
277 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This);
279 QObject *object = resource->object;
280 if (QQmlData::wasDeleted(object)) return v8::Undefined();
282 QQmlPropertyData *property =
283 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
285 QQmlEngine *engine = resource->engine->engine();
286 QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0;
290 if (ep && ep->propertyCapture) {
291 if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) {
292 QQmlNotifier *notifier = 0;
293 ReadFunction(object, *property, &value, ¬ifier);
294 if (notifier) ep->captureProperty(notifier);
295 } else if (!property->isConstant()) {
296 ep->captureProperty(object, property->coreIndex, property->notifyIndex);
297 ReadFunction(object, *property, &value, 0);
299 ReadFunction(object, *property, &value, 0);
302 ReadFunction(object, *property, &value, 0);
305 return valueToHandle(resource->engine, value);
308 #define FAST_GETTER_FUNCTION(property, cpptype) \
309 (property->hasAccessors()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Accessor>):(property->isDirect()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Direct>):((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Indirect>)))
311 static quint32 toStringHash = quint32(-1);
312 static quint32 destroyHash = quint32(-1);
314 void QV8QObjectWrapper::init(QV8Engine *engine)
318 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
319 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
320 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
322 m_toStringString = QHashedV8String(m_toStringSymbol);
323 m_destroyString = QHashedV8String(m_destroySymbol);
325 toStringHash = m_toStringString.hash();
326 destroyHash = m_destroyString.hash();
329 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
330 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
331 ft->InstanceTemplate()->SetHasExternalResource(true);
332 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
335 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
336 #define CREATE_FUNCTION_SOURCE \
337 "(function(method) { "\
338 "return (function(object, data, qmlglobal) { "\
339 "return (function() { "\
340 "return method(object, data, qmlglobal, arguments.length, arguments); "\
344 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION_SOURCE), &origin, 0,
345 v8::Handle<v8::String>(), v8::Script::NativeMode);
346 #undef CREATE_FUNCTION_SOURCE
347 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
348 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
349 v8::Handle<v8::Value> args[] = { invokeFn };
350 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
351 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
354 v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine);
355 v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine);
358 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
359 ft->InstanceTemplate()->SetHasExternalResource(true);
360 ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum);
361 ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
362 m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
366 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
367 prototype->Set(v8::String::New("connect"), connect, v8::DontEnum);
368 prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
372 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
374 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
377 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
379 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
380 return r?r->object:0;
383 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
384 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
386 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
387 return static_cast<QV8QObjectResource *>(r)->object;
390 // Load value properties
391 template<void (*ReadFunction)(QObject *, const QQmlPropertyData &,
392 void *, QQmlNotifier **)>
393 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
394 const QQmlPropertyData &property,
395 QQmlNotifier **notifier)
397 Q_ASSERT(!property.isFunction());
399 if (property.isQObject()) {
401 ReadFunction(object, property, &rv, notifier);
402 return engine->newQObject(rv);
403 } else if (property.isQList()) {
404 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
405 } else if (property.propType == QMetaType::QReal) {
407 ReadFunction(object, property, &v, notifier);
408 return valueToHandle(engine, v);
409 } else if (property.propType == QMetaType::Int || property.isEnum()) {
411 ReadFunction(object, property, &v, notifier);
412 return valueToHandle(engine, v);
413 } else if (property.propType == QMetaType::Bool) {
415 ReadFunction(object, property, &v, notifier);
416 return valueToHandle(engine, v);
417 } else if (property.propType == QMetaType::QString) {
419 ReadFunction(object, property, &v, notifier);
420 return valueToHandle(engine, v);
421 } else if (property.propType == QMetaType::UInt) {
423 ReadFunction(object, property, &v, notifier);
424 return valueToHandle(engine, v);
425 } else if (property.propType == QMetaType::Float) {
427 ReadFunction(object, property, &v, notifier);
428 return valueToHandle(engine, v);
429 } else if (property.propType == QMetaType::Double) {
431 ReadFunction(object, property, &v, notifier);
432 return valueToHandle(engine, v);
433 } else if (property.isV8Handle()) {
435 ReadFunction(object, property, &handle, notifier);
436 return handle.toHandle();
437 } else if (property.propType == qMetaTypeId<QJSValue>()) {
439 ReadFunction(object, property, &v, notifier);
440 return QJSValuePrivate::get(v)->asV8Value(engine);
441 } else if (property.isQVariant()) {
443 ReadFunction(object, property, &v, notifier);
445 if (QQmlValueTypeFactory::isValueType(v.userType())) {
446 if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(v.userType()))
447 return engine->newValueType(object, property.coreIndex, valueType); // VariantReference value-type.
450 return engine->fromVariant(v);
451 } else if (QQmlValueTypeFactory::isValueType(property.propType)) {
452 Q_ASSERT(notifier == 0);
454 if (QQmlValueType *valueType = QQmlValueTypeFactory::valueType(property.propType))
455 return engine->newValueType(object, property.coreIndex, valueType);
457 Q_ASSERT(notifier == 0);
459 // see if it's a sequence type
460 bool succeeded = false;
461 v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex,
467 if (property.propType == QMetaType::UnknownType) {
468 QMetaProperty p = object->metaObject()->property(property.coreIndex);
469 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
470 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
471 return v8::Undefined();
473 QVariant v(property.propType, (void *)0);
474 ReadFunction(object, property, v.data(), notifier);
475 return engine->fromVariant(v);
479 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
480 v8::Handle<v8::Value> *objectHandle,
481 const QHashedV8String &property,
482 QQmlContextData *context,
483 QV8QObjectWrapper::RevisionMode revisionMode)
485 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
486 // will be a faster way of creating QObject method objects.
487 struct MethodClosure {
488 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
489 v8::Handle<v8::Value> *objectHandle,
491 v8::Handle<v8::Value> argv[] = {
492 objectHandle?*objectHandle:engine->newQObject(object),
493 v8::Integer::New(index)
495 Q_ASSERT(argv[0]->IsObject());
496 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
498 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
499 v8::Handle<v8::Value> *objectHandle,
501 v8::Handle<v8::Value> argv[] = {
502 objectHandle?*objectHandle:engine->newQObject(object),
503 v8::Integer::New(index),
504 v8::Context::GetCallingQmlGlobal()
506 Q_ASSERT(argv[0]->IsObject());
507 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
511 if (QQmlData::wasDeleted(object))
512 return v8::Handle<v8::Value>();
515 // Comparing the hash first actually makes a measurable difference here, at least on x86
516 quint32 hash = property.hash();
517 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
518 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
519 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
520 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
524 QQmlPropertyData local;
525 QQmlPropertyData *result = 0;
527 QQmlData *ddata = QQmlData::get(object, false);
528 if (ddata && ddata->propertyCache)
529 result = ddata->propertyCache->property(property, object, context);
531 result = QQmlPropertyCache::property(engine->engine(), object, property, context, local);
535 return v8::Handle<v8::Value>();
537 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
538 QQmlData *ddata = QQmlData::get(object);
539 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
540 return v8::Handle<v8::Value>();
543 if (result->isFunction() && !result->isVarProperty()) {
544 if (result->isVMEFunction()) {
545 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
547 return vmemo->vmeMethod(result->coreIndex);
548 } else if (result->isV8Function()) {
549 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
550 } else if (result->isSignalHandler()) {
551 v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
552 QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
553 handler->SetExternalResource(r);
556 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
560 QQmlEnginePrivate *ep =
561 engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
563 if (result->hasAccessors()) {
565 QQmlNotifier **nptr = 0;
567 if (ep && ep->propertyCapture && result->accessors->notifier)
570 v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
572 if (result->accessors->notifier) {
573 if (n) ep->captureProperty(n);
575 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
581 if (ep && !result->isConstant())
582 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
584 if (result->isVarProperty()) {
585 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
587 return vmemo->vmeProperty(result->coreIndex);
588 } else if (result->isDirect()) {
589 return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
591 return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
595 // Setter for writable properties. Shared between the interceptor and fast property accessor
596 static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
597 v8::Handle<v8::Value> value)
599 QQmlBinding *newBinding = 0;
600 if (value->IsFunction()) {
601 if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
602 if (!property->isVarProperty() && property->propType != qMetaTypeId<QJSValue>()) {
603 // assigning a JS function to a non var or QJSValue property or is not allowed.
604 QString error = QLatin1String("Cannot assign JavaScript function to ");
605 if (!QMetaType::typeName(property->propType))
606 error += QLatin1String("[unknown property type]");
608 error += QLatin1String(QMetaType::typeName(property->propType));
609 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
613 // binding assignment.
614 QQmlContextData *context = engine->callingContext();
615 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
617 v8::Local<v8::StackTrace> trace =
618 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
619 v8::StackTrace::kScriptName));
620 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
621 int lineNumber = frame->GetLineNumber();
622 int columnNumber = frame->GetColumn();
623 QString url = engine->toString(frame->GetScriptName());
625 newBinding = new QQmlBinding(&function, object, context, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber));
626 newBinding->setTarget(object, *property, context);
627 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
628 QQmlBinding::RequiresThisObject);
632 QQmlAbstractBinding *oldBinding =
633 QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
635 oldBinding->destroy();
637 if (!newBinding && property->isVarProperty()) {
638 // allow assignment of "special" values (null, undefined, function) to var properties
639 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
641 vmemo->setVMEProperty(property->coreIndex, value);
645 #define PROPERTY_STORE(cpptype, value) \
649 void *argv[] = { &o, 0, &status, &flags }; \
650 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
652 if (value->IsNull() && property->isQObject()) {
653 PROPERTY_STORE(QObject*, 0);
654 } else if (value->IsUndefined() && property->isResettable()) {
656 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
657 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
658 PROPERTY_STORE(QVariant, QVariant());
659 } else if (value->IsUndefined() && property->propType == QMetaType::QJsonValue) {
660 PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
661 } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) {
662 PROPERTY_STORE(QJSValue, engine->scriptValueFromInternal(value));
663 } else if (value->IsUndefined()) {
664 QString error = QLatin1String("Cannot assign [undefined] to ");
665 if (!QMetaType::typeName(property->propType))
666 error += QLatin1String("[unknown property type]");
668 error += QLatin1String(QMetaType::typeName(property->propType));
669 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
670 } else if (value->IsFunction()) {
671 // this is handled by the binding creation above
672 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
673 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
674 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
675 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
676 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
677 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
678 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
679 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
680 } else if (property->propType == QMetaType::QString && value->IsString()) {
681 PROPERTY_STORE(QString, engine->toString(value->ToString()));
682 } else if (property->isVarProperty()) {
683 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
685 vmemo->setVMEProperty(property->coreIndex, value);
688 if (property->isQList())
689 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
691 v = engine->toVariant(value, property->propType);
693 QQmlContextData *context = engine->callingContext();
694 if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
695 const char *valueType = 0;
696 if (v.userType() == QVariant::Invalid) valueType = "null";
697 else valueType = QMetaType::typeName(v.userType());
699 const char *targetTypeName = QMetaType::typeName(property->propType);
701 targetTypeName = "an unregistered type";
703 QString error = QLatin1String("Cannot assign ") +
704 QLatin1String(valueType) +
705 QLatin1String(" to ") +
706 QLatin1String(targetTypeName);
707 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
712 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property, QQmlContextData *context,
713 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
715 if (engine->qobjectWrapper()->m_toStringString == property ||
716 engine->qobjectWrapper()->m_destroyString == property)
719 if (QQmlData::wasDeleted(object))
722 QQmlPropertyData local;
723 QQmlPropertyData *result = 0;
724 result = QQmlPropertyCache::property(engine->engine(), object, property, context, local);
729 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
730 QQmlData *ddata = QQmlData::get(object);
731 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
735 if (!result->isWritable() && !result->isQList()) {
736 QString error = QLatin1String("Cannot assign to read-only property \"") +
737 engine->toString(property.string()) + QLatin1Char('\"');
738 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
742 StoreProperty(engine, object, result, value);
747 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
748 const v8::AccessorInfo &info)
750 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
752 if (QQmlData::wasDeleted(resource->object))
753 return v8::Handle<v8::Value>();
755 QObject *object = resource->object;
757 QHashedV8String propertystring(property);
759 QV8Engine *v8engine = resource->engine;
760 QQmlContextData *context = v8engine->callingContext();
762 v8::Handle<v8::Value> This = info.This();
763 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
764 context, QV8QObjectWrapper::IgnoreRevision);
765 if (!result.IsEmpty())
768 if (QV8Engine::startsWithUpper(property)) {
769 // Check for attached properties
770 if (context && context->imports) {
771 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
774 if (r.scriptIndex != -1) {
775 return v8::Undefined();
777 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
778 } else if (r.importNamespace) {
779 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
780 QV8TypeWrapper::ExcludeEnums);
782 Q_ASSERT(!"Unreachable");
787 return v8::Handle<v8::Value>();
790 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
791 v8::Local<v8::Value> value,
792 const v8::AccessorInfo &info)
794 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
796 if (QQmlData::wasDeleted(resource->object))
799 QObject *object = resource->object;
801 QHashedV8String propertystring(property);
803 QV8Engine *v8engine = resource->engine;
804 QQmlContextData *context = v8engine->callingContext();
805 bool result = SetProperty(v8engine, object, propertystring, context, 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;
827 QQmlContextData *context = engine->callingContext();
829 QHashedV8String propertystring(property);
831 QQmlPropertyData local;
832 QQmlPropertyData *result = 0;
833 result = QQmlPropertyCache::property(engine->engine(), object, propertystring, context, local);
836 return v8::Handle<v8::Integer>();
837 else if (!result->isWritable() && !result->isQList())
838 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
840 return v8::Integer::New(v8::DontDelete);
843 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
845 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
847 if (resource->object.isNull())
848 return v8::Array::New();
850 QObject *object = resource->object;
854 QQmlEnginePrivate *ep = resource->engine->engine()
855 ? QQmlEnginePrivate::get(resource->engine->engine())
858 QQmlPropertyCache *cache = 0;
859 QQmlData *ddata = QQmlData::get(object);
861 cache = ddata->propertyCache;
864 cache = ep ? ep->cache(object) : 0;
866 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
868 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
869 const QMetaObject *mo = object->metaObject();
870 int pc = mo->propertyCount();
871 int po = mo->propertyOffset();
872 for (int i=po; i<pc; ++i)
873 result << QString::fromUtf8(mo->property(i).name());
876 result = cache->propertyNames();
879 v8::Local<v8::Array> rv = v8::Array::New(result.count());
881 for (int ii = 0; ii < result.count(); ++ii)
882 rv->Set(ii, resource->engine->toString(result.at(ii)));
887 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
888 const v8::AccessorInfo& info)
890 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
892 if (QQmlData::wasDeleted(resource->object))
895 QObject *object = resource->object;
897 QQmlPropertyData *property =
898 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
900 int index = property->coreIndex;
902 QQmlData *ddata = QQmlData::get(object, false);
904 Q_ASSERT(ddata->propertyCache);
906 QQmlPropertyData *pdata = ddata->propertyCache->property(index);
909 Q_ASSERT(pdata->isWritable() || pdata->isQList());
911 StoreProperty(resource->engine, object, pdata, value);
914 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
915 const v8::AccessorInfo& info)
917 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
919 if (QQmlData::wasDeleted(resource->object))
922 QV8Engine *v8engine = resource->engine;
924 QString error = QLatin1String("Cannot assign to read-only property \"") +
925 v8engine->toString(property) + QLatin1Char('\"');
926 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
929 void QV8QObjectWrapper::WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *wrapper)
931 Q_ASSERT(handle->IsObject());
932 v8::Handle<v8::Object> v8object = handle->ToObject();
933 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(v8object);
936 static_cast<QV8QObjectWrapper*>(wrapper)->unregisterWeakQObjectReference(resource);
937 if (static_cast<QV8QObjectWrapper*>(wrapper)->deleteWeakQObject(resource, false)) {
939 v8object->SetExternalResource(0);
941 qPersistentDispose(handle);
943 handle.MakeWeak(0, WeakQObjectReferenceCallback); // revive.
947 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
949 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
950 instance->v8object.Clear();
951 qPersistentDispose(handle);
954 v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
957 Q_ASSERT(this->engine);
959 Q_ASSERT(QQmlData::get(object, false));
960 Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
963 if (constructor.IsEmpty()) {
964 v8::Local<v8::FunctionTemplate> ft;
966 const QHashedString toString(QStringLiteral("toString"));
967 const QHashedString destroy(QStringLiteral("destroy"));
969 // As we use hash linking, or with property overrides, it is possible that iterating
970 // over the values can yield duplicates. To combat this, we must unique'ify our properties.
971 const bool checkForDuplicates = stringCache.isLinked() || _hasPropertyOverrides;
973 StringCache uniqueHash;
974 if (checkForDuplicates)
975 uniqueHash.reserve(stringCache.count());
977 // XXX TODO: Enables fast property accessors. These more than double the property access
978 // performance, but the cost of setting up this structure hasn't been measured so
979 // its not guarenteed that this is a win overall. We need to try and measure the cost.
980 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
981 if (iter.equals(toString) || iter.equals(destroy))
984 if (checkForDuplicates) {
985 if (uniqueHash.contains(iter))
987 uniqueHash.insert(iter);
990 QQmlPropertyData *property = (*iter).second;
991 if (property->notFullyResolved()) resolve(property);
993 if (property->isFunction())
996 v8::AccessorGetter fastgetter = 0;
998 if (property->isQObject())
999 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
1000 else if (property->propType == QMetaType::Int || property->isEnum())
1001 fastgetter = FAST_GETTER_FUNCTION(property, int);
1002 else if (property->propType == QMetaType::Bool)
1003 fastgetter = FAST_GETTER_FUNCTION(property, bool);
1004 else if (property->propType == QMetaType::QString)
1005 fastgetter = FAST_GETTER_FUNCTION(property, QString);
1006 else if (property->propType == QMetaType::UInt)
1007 fastgetter = FAST_GETTER_FUNCTION(property, uint);
1008 else if (property->propType == QMetaType::Float)
1009 fastgetter = FAST_GETTER_FUNCTION(property, float);
1010 else if (property->propType == QMetaType::Double)
1011 fastgetter = FAST_GETTER_FUNCTION(property, double);
1015 ft = v8::FunctionTemplate::New();
1016 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1017 QV8QObjectWrapper::Setter,
1018 QV8QObjectWrapper::Query,
1020 QV8QObjectWrapper::Enumerator);
1021 ft->InstanceTemplate()->SetHasExternalResource(true);
1024 v8::AccessorSetter fastsetter = FastValueSetter;
1025 if (!property->isWritable())
1026 fastsetter = FastValueSetterReadOnly;
1028 // We wrap the raw QQmlPropertyData pointer here. This is safe as the
1029 // pointer will remain valid at least as long as the lifetime of any QObject's of
1030 // this type and the property accessor checks if the object is 0 (deleted) before
1031 // dereferencing the pointer.
1032 ft->InstanceTemplate()->SetAccessor(engine->toString(iter.key()), fastgetter, fastsetter,
1033 v8::External::Wrap(property));
1038 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
1040 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1041 QV8QObjectWrapper::Setter,
1042 QV8QObjectWrapper::Query,
1044 QV8QObjectWrapper::Enumerator);
1045 ft->InstanceTemplate()->SetHasExternalResource(true);
1046 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
1049 QQmlCleanup::addToEngine(this->engine);
1052 v8::Local<v8::Object> result = constructor->NewInstance();
1053 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1054 result->SetExternalResource(r);
1058 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
1060 v8::Local<v8::Object> rv;
1062 if (!ddata->propertyCache && engine->engine()) {
1063 ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
1064 if (ddata->propertyCache) ddata->propertyCache->addref();
1067 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1068 rv = ddata->propertyCache->newQObject(object, engine);
1070 // XXX NewInstance() should be optimized
1071 rv = m_constructor->NewInstance();
1072 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1073 rv->SetExternalResource(r);
1080 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1081 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
1083 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
1084 persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that
1085 "owns" this handle by setting the QQmlData::v8objectid to the id of this
1087 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create
1088 an entry in the m_taintedObject hash where we store the handle and mark the object as
1089 "tainted" in the QQmlData::hasTaintedV8Object flag.
1090 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1091 in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has
1092 released the handle.
1094 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1096 if (QQmlData::wasDeleted(object))
1099 QQmlData *ddata = QQmlData::get(object, true);
1101 return v8::Undefined();
1103 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1104 // We own the v8object
1105 return v8::Local<v8::Object>::New(ddata->v8object);
1106 } else if (ddata->v8object.IsEmpty() &&
1107 (ddata->v8objectid == m_id || // We own the QObject
1108 ddata->v8objectid == 0 || // No one owns the QObject
1109 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1111 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1112 ddata->v8object = qPersistentNew<v8::Object>(rv);
1113 ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1114 ddata->v8objectid = m_id;
1115 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1116 registerWeakQObjectReference(resource);
1120 // If this object is tainted, we have to check to see if it is in our
1121 // tainted object list
1122 TaintedHash::Iterator iter =
1123 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1124 bool found = iter != m_taintedObjects.end();
1126 // If our tainted handle doesn't exist or has been collected, and there isn't
1127 // a handle in the ddata, we can assume ownership of the ddata->v8object
1128 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1129 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1130 ddata->v8object = qPersistentNew<v8::Object>(rv);
1131 ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1132 ddata->v8objectid = m_id;
1133 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1134 registerWeakQObjectReference(resource);
1138 m_taintedObjects.erase(iter);
1142 } else if (!found) {
1143 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1144 iter = m_taintedObjects.insert(object, instance);
1145 ddata->hasTaintedV8Object = true;
1148 if ((*iter)->v8object.IsEmpty()) {
1149 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1150 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1151 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1154 return v8::Local<v8::Object>::New((*iter)->v8object);
1158 // returns true if the object's qqmldata v8object handle should
1159 // be disposed by the caller, false if it should not be (due to
1160 // creation status, etc).
1161 bool QV8QObjectWrapper::deleteWeakQObject(QV8QObjectResource *resource, bool calledFromEngineDtor)
1163 QObject *object = resource->object;
1165 QQmlData *ddata = QQmlData::get(object, false);
1167 if (!calledFromEngineDtor && ddata->rootObjectInCreation) {
1168 // if weak ref callback is triggered (by gc) for a root object
1169 // prior to completion of creation, we should NOT delete it.
1173 ddata->v8object.Clear();
1174 if (!object->parent() && !ddata->indestructible) {
1175 // This object is notionally destroyed now
1176 if (ddata->ownContext && ddata->context)
1177 ddata->context->emitDestruction();
1178 ddata->isQueuedForDeletion = true;
1179 object->deleteLater();
1187 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1189 if (object->IsFunction())
1190 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1192 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1193 return qMakePair(resource->object.data(), resource->index);
1195 return qMakePair((QObject *)0, -1);
1198 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1200 v8::ScriptOrigin origin = function->GetScriptOrigin();
1201 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1203 // This is one of our special QObject method wrappers
1204 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1205 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1207 if (data->IsArray()) {
1208 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1209 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1212 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1215 return qMakePair((QObject *)0, -1);
1218 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1221 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1222 ~QV8QObjectConnectionList();
1226 : needsDestroy(false) {}
1227 Connection(const Connection &other)
1228 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1229 Connection &operator=(const Connection &other) {
1230 thisObject = other.thisObject;
1231 function = other.function;
1232 needsDestroy = other.needsDestroy;
1236 v8::Persistent<v8::Object> thisObject;
1237 v8::Persistent<v8::Function> function;
1240 qPersistentDispose(thisObject);
1241 qPersistentDispose(function);
1247 struct ConnectionList : public QList<Connection> {
1248 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1249 int connectionsInUse;
1250 bool connectionsNeedClean;
1255 typedef QHash<int, ConnectionList> SlotHash;
1260 virtual void objectDestroyed(QObject *);
1261 virtual int qt_metacall(QMetaObject::Call, int, void **);
1264 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1265 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1269 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1271 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1272 QList<Connection> &connections = *iter;
1273 for (int ii = 0; ii < connections.count(); ++ii) {
1274 qPersistentDispose(connections[ii].thisObject);
1275 qPersistentDispose(connections[ii].function);
1281 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1283 engine->qobjectWrapper()->m_connections.remove(object);
1286 needsDestroy = true;
1291 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1293 if (method == QMetaObject::InvokeMetaMethod) {
1294 SlotHash::Iterator iter = slotHash.find(index);
1295 if (iter == slotHash.end())
1297 ConnectionList &connectionList = *iter;
1298 if (connectionList.isEmpty())
1303 connectionList.connectionsInUse++;
1305 QList<Connection> connections = connectionList;
1307 QVarLengthArray<int, 9> dummy;
1308 int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1310 v8::HandleScope handle_scope;
1311 v8::Context::Scope scope(engine->context());
1313 int argCount = argsTypes?argsTypes[0]:0;
1314 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1316 for (int ii = 0; ii < argCount; ++ii) {
1317 int type = argsTypes[ii + 1];
1318 if (type == qMetaTypeId<QVariant>()) {
1319 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1321 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1325 for (int ii = 0; ii < connections.count(); ++ii) {
1326 Connection &connection = connections[ii];
1327 if (connection.needsDestroy)
1330 v8::TryCatch try_catch;
1331 if (connection.thisObject.IsEmpty()) {
1332 connection.function->Call(engine->global(), argCount, args.data());
1334 connection.function->Call(connection.thisObject, argCount, args.data());
1337 if (try_catch.HasCaught()) {
1339 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1340 v8::Local<v8::Message> message = try_catch.Message();
1341 if (!message.IsEmpty())
1342 QQmlExpressionPrivate::exceptionToError(message, error);
1343 QQmlEnginePrivate::get(engine->engine())->warning(error);
1347 connectionList.connectionsInUse--;
1348 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1349 for (QList<Connection>::Iterator iter = connectionList.begin();
1350 iter != connectionList.end(); ) {
1351 if (iter->needsDestroy) {
1353 iter = connectionList.erase(iter);
1361 if (inUse == 0 && needsDestroy)
1368 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1370 if (args.Length() == 0)
1371 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1373 QV8Engine *engine = V8ENGINE();
1375 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1376 QObject *signalObject = signalInfo.first;
1377 int signalIndex = signalInfo.second;
1379 if (signalIndex < 0)
1380 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1383 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1385 if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1386 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1388 v8::Local<v8::Value> functionValue;
1389 v8::Local<v8::Value> functionThisValue;
1391 if (args.Length() == 1) {
1392 functionValue = args[0];
1394 functionThisValue = args[0];
1395 functionValue = args[1];
1398 if (!functionValue->IsFunction())
1399 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1401 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1402 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1404 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1405 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1406 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1407 if (iter == connections.end())
1408 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1410 QV8QObjectConnectionList *connectionList = *iter;
1411 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1412 if (slotIter == connectionList->slotHash.end()) {
1413 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1414 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1417 QV8QObjectConnectionList::Connection connection;
1418 if (!functionThisValue.IsEmpty())
1419 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1420 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1422 slotIter->append(connection);
1424 return v8::Undefined();
1427 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1429 if (args.Length() == 0)
1430 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1432 QV8Engine *engine = V8ENGINE();
1434 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1435 QObject *signalObject = signalInfo.first;
1436 int signalIndex = signalInfo.second;
1438 if (signalIndex == -1)
1439 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1442 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1444 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1445 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1447 v8::Local<v8::Value> functionValue;
1448 v8::Local<v8::Value> functionThisValue;
1450 if (args.Length() == 1) {
1451 functionValue = args[0];
1453 functionThisValue = args[0];
1454 functionValue = args[1];
1457 if (!functionValue->IsFunction())
1458 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1460 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1461 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1463 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1464 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1465 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1466 if (iter == connectionsList.end())
1467 return v8::Undefined(); // Nothing to disconnect from
1469 QV8QObjectConnectionList *connectionList = *iter;
1470 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1471 if (slotIter == connectionList->slotHash.end())
1472 return v8::Undefined(); // Nothing to disconnect from
1474 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1476 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1477 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1479 if (functionData.second != -1) {
1480 // This is a QObject function wrapper
1481 for (int ii = 0; ii < connections.count(); ++ii) {
1482 QV8QObjectConnectionList::Connection &connection = connections[ii];
1484 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1485 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1487 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1488 if (connectedFunctionData == functionData) {
1490 if (connections.connectionsInUse) {
1491 connection.needsDestroy = true;
1492 connections.connectionsNeedClean = true;
1494 connection.dispose();
1495 connections.removeAt(ii);
1497 return v8::Undefined();
1503 // This is a normal JS function
1504 for (int ii = 0; ii < connections.count(); ++ii) {
1505 QV8QObjectConnectionList::Connection &connection = connections[ii];
1506 if (connection.function->StrictEquals(function) &&
1507 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1508 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1510 if (connections.connectionsInUse) {
1511 connection.needsDestroy = true;
1512 connections.connectionsNeedClean = true;
1514 connection.dispose();
1515 connections.removeAt(ii);
1517 return v8::Undefined();
1522 return v8::Undefined();
1526 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1528 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1530 Only searches for real properties of \a object (including methods), not attached properties etc.
1534 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1536 Set the \a property of \a object to \a value.
1538 Returns true if the property was "set" - even if this results in an exception being thrown -
1539 and false if the object has no such property.
1541 Only searches for real properties of \a object (including methods), not attached properties etc.
1547 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1548 int Length() const { return _length; }
1549 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1553 v8::Handle<v8::Object> *_args;
1557 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1558 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1562 // Special handling is required for value types.
1563 // We need to save the current value in a temporary,
1564 // and reapply it after converting all arguments.
1565 // This avoids the "overwriting copy-value-type-value"
1566 // problem during Q_INVOKABLE function invocation.
1567 QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1568 QVariant valueTypeValue;
1569 if (valueTypeObject)
1570 valueTypeValue = valueTypeObject->value();
1572 // Convert all arguments.
1573 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1574 args[0].initAsType(returnType);
1575 for (int ii = 0; ii < argCount; ++ii)
1576 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1577 QVarLengthArray<void *, 9> argData(args.count());
1578 for (int ii = 0; ii < args.count(); ++ii)
1579 argData[ii] = args[ii].dataPtr();
1581 // Reinstate saved value type object value if required.
1582 if (valueTypeObject)
1583 valueTypeObject->setValue(valueTypeValue);
1585 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1587 return args[0].toValue(engine);
1589 } else if (returnType != QMetaType::Void) {
1592 arg.initAsType(returnType);
1594 void *args[] = { arg.dataPtr() };
1596 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1598 return arg.toValue(engine);
1602 void *args[] = { 0 };
1603 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1604 return v8::Undefined();
1610 Returns the match score for converting \a actual to be of type \a conversionType. A
1611 zero score means "perfect match" whereas a higher score is worse.
1613 The conversion table is copied out of the QtScript callQtMethod() function.
1615 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1617 if (actual->IsNumber()) {
1618 switch (conversionType) {
1619 case QMetaType::Double:
1621 case QMetaType::Float:
1623 case QMetaType::LongLong:
1624 case QMetaType::ULongLong:
1626 case QMetaType::Long:
1627 case QMetaType::ULong:
1629 case QMetaType::Int:
1630 case QMetaType::UInt:
1632 case QMetaType::Short:
1633 case QMetaType::UShort:
1636 case QMetaType::Char:
1637 case QMetaType::UChar:
1639 case QMetaType::QJsonValue:
1644 } else if (actual->IsString()) {
1645 switch (conversionType) {
1646 case QMetaType::QString:
1648 case QMetaType::QJsonValue:
1653 } else if (actual->IsBoolean()) {
1654 switch (conversionType) {
1655 case QMetaType::Bool:
1657 case QMetaType::QJsonValue:
1662 } else if (actual->IsDate()) {
1663 switch (conversionType) {
1664 case QMetaType::QDateTime:
1666 case QMetaType::QDate:
1668 case QMetaType::QTime:
1673 } else if (actual->IsRegExp()) {
1674 switch (conversionType) {
1675 case QMetaType::QRegExp:
1680 } else if (actual->IsArray()) {
1681 switch (conversionType) {
1682 case QMetaType::QJsonArray:
1684 case QMetaType::QStringList:
1685 case QMetaType::QVariantList:
1687 case QMetaType::QVector4D:
1688 case QMetaType::QMatrix4x4:
1690 case QMetaType::QVector3D:
1695 } else if (actual->IsNull()) {
1696 switch (conversionType) {
1697 case QMetaType::VoidStar:
1698 case QMetaType::QObjectStar:
1699 case QMetaType::QJsonValue:
1702 const char *typeName = QMetaType::typeName(conversionType);
1703 if (typeName && typeName[strlen(typeName) - 1] == '*')
1709 } else if (actual->IsObject()) {
1710 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1712 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1713 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1714 switch (conversionType) {
1715 case QMetaType::QObjectStar:
1720 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1721 if (conversionType == qMetaTypeId<QVariant>())
1723 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1727 } else if (r && r->resourceType() == QV8ObjectResource::ValueTypeType) {
1728 if (r->engine->toVariant(actual, -1).userType() == conversionType)
1731 } else if (conversionType == QMetaType::QJsonObject) {
1742 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1748 int classInfoCount, classInfoData;
1749 int methodCount, methodData;
1752 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1756 Returns the next related method, if one, or 0.
1758 static const QQmlPropertyData * RelatedMethod(QObject *object,
1759 const QQmlPropertyData *current,
1760 QQmlPropertyData &dummy)
1762 QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1763 if (!current->isOverload())
1766 Q_ASSERT(!current->overrideIndexIsProperty);
1769 return cache->method(current->overrideIndex);
1771 const QMetaObject *mo = object->metaObject();
1772 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1774 while (methodOffset > current->overrideIndex) {
1775 mo = mo->superClass();
1776 methodOffset -= QMetaObject_methods(mo);
1779 QMetaMethod method = mo->method(current->overrideIndex);
1782 // Look for overloaded methods
1783 QByteArray methodName = method.name();
1784 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1785 if (methodName == mo->method(ii).name()) {
1786 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1787 dummy.overrideIndexIsProperty = 0;
1788 dummy.overrideIndex = ii;
1797 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1798 QV8Engine *engine, CallArgs &callArgs)
1800 QByteArray unknownTypeError;
1802 int returnType = QQmlPropertyCache::methodReturnType(object, data, &unknownTypeError);
1804 if (returnType == QMetaType::UnknownType) {
1805 QString typeName = QString::fromLatin1(unknownTypeError);
1806 QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName);
1807 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1808 return v8::Handle<v8::Value>();
1811 if (data.hasArguments()) {
1814 QVarLengthArray<int, 9> dummy;
1816 args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1820 QString typeName = QString::fromLatin1(unknownTypeError);
1821 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1822 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1823 return v8::Handle<v8::Value>();
1826 if (args[0] > callArgs.Length()) {
1827 QString error = QLatin1String("Insufficient arguments");
1828 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1829 return v8::Handle<v8::Value>();
1832 return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs);
1836 return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs);
1842 Resolve the overloaded method to call. The algorithm works conceptually like this:
1843 1. Resolve the set of overloads it is *possible* to call.
1844 Impossible overloads include those that have too many parameters or have parameters
1846 2. Filter the set of overloads to only contain those with the closest number of
1848 For example, if we are called with 3 parameters and there are 2 overloads that
1849 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1850 3. Find the best remaining overload based on its match score.
1851 If two or more overloads have the same match score, call the last one. The match
1852 score is constructed by adding the matchScore() result for each of the parameters.
1854 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1855 QV8Engine *engine, CallArgs &callArgs)
1857 int argumentCount = callArgs.Length();
1859 const QQmlPropertyData *best = 0;
1860 int bestParameterScore = INT_MAX;
1861 int bestMatchScore = INT_MAX;
1863 // Special handling is required for value types.
1864 // We need to save the current value in a temporary,
1865 // and reapply it after converting all arguments.
1866 // This avoids the "overwriting copy-value-type-value"
1867 // problem during Q_INVOKABLE function invocation.
1868 QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1869 QVariant valueTypeValue;
1870 if (valueTypeObject)
1871 valueTypeValue = valueTypeObject->value();
1873 QQmlPropertyData dummy;
1874 const QQmlPropertyData *attempt = &data;
1877 QVarLengthArray<int, 9> dummy;
1878 int methodArgumentCount = 0;
1879 int *methodArgTypes = 0;
1880 if (attempt->hasArguments()) {
1881 typedef QQmlPropertyCache PC;
1882 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1883 if (!args) // Must be an unknown argument
1886 methodArgumentCount = args[0];
1887 methodArgTypes = args + 1;
1890 if (methodArgumentCount > argumentCount)
1891 continue; // We don't have sufficient arguments to call this method
1893 int methodParameterScore = argumentCount - methodArgumentCount;
1894 if (methodParameterScore > bestParameterScore)
1895 continue; // We already have a better option
1897 int methodMatchScore = 0;
1898 for (int ii = 0; ii < methodArgumentCount; ++ii)
1899 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1901 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1903 bestParameterScore = methodParameterScore;
1904 bestMatchScore = methodMatchScore;
1907 if (bestParameterScore == 0 && bestMatchScore == 0)
1908 break; // We can't get better than that
1910 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1913 if (valueTypeObject)
1914 valueTypeObject->setValue(valueTypeValue);
1915 return CallPrecise(object, *best, engine, callArgs);
1917 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1918 const QQmlPropertyData *candidate = &data;
1920 error += QLatin1String("\n ") +
1921 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData());
1922 candidate = RelatedMethod(object, candidate, dummy);
1925 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1926 return v8::Handle<v8::Value>();
1930 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1934 QString objectName = object->objectName();
1936 result += QString::fromUtf8(object->metaObject()->className());
1937 result += QLatin1String("(0x");
1938 result += QString::number((quintptr)object,16);
1940 if (!objectName.isEmpty()) {
1941 result += QLatin1String(", \"");
1942 result += objectName;
1943 result += QLatin1Char('\"');
1946 result += QLatin1Char(')');
1948 result = QLatin1String("null");
1951 return engine->toString(result);
1954 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1956 QQmlData *ddata = QQmlData::get(object, false);
1957 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation) {
1958 const char *error = "Invalid attempt to destroy() an indestructible object";
1959 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1960 return v8::Undefined();
1965 delay = args->Get(0)->Uint32Value();
1968 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1970 object->deleteLater();
1972 return v8::Undefined();
1975 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1977 // object, index, qmlglobal, argCount, args
1978 Q_ASSERT(args.Length() == 5);
1979 Q_ASSERT(args[0]->IsObject());
1981 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1984 return v8::Undefined();
1986 int argCount = args[3]->Int32Value();
1987 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1989 // Special hack to return info about this closure.
1990 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1991 v8::Local<v8::Array> data = v8::Array::New(2);
1992 data->Set(0, args[0]);
1993 data->Set(1, args[1]);
1997 QObject *object = resource->object;
1998 int index = args[1]->Int32Value();
2001 return v8::Undefined();
2004 // Builtin functions
2005 if (index == QOBJECT_TOSTRING_INDEX) {
2006 return ToString(resource->engine, object, argCount, arguments);
2007 } else if (index == QOBJECT_DESTROY_INDEX) {
2008 return Destroy(resource->engine, object, argCount, arguments);
2010 return v8::Undefined();
2014 QQmlPropertyData method;
2016 if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
2017 if (ddata->propertyCache) {
2018 QQmlPropertyData *d = ddata->propertyCache->method(index);
2020 return v8::Undefined();
2025 if (method.coreIndex == -1) {
2026 method.load(object->metaObject()->method(index));
2028 if (method.coreIndex == -1)
2029 return v8::Undefined();
2032 if (method.isV8Function()) {
2033 v8::Handle<v8::Value> rv;
2034 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
2036 QQmlV8Function func(argCount, arguments, rv, qmlglobal,
2037 resource->engine->contextWrapper()->context(qmlglobal),
2039 QQmlV8Function *funcptr = &func;
2041 void *args[] = { 0, &funcptr };
2042 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
2044 if (rv.IsEmpty()) return v8::Undefined();
2048 CallArgs callArgs(argCount, &arguments);
2049 if (!method.isOverload()) {
2050 return CallPrecise(object, method, resource->engine, callArgs);
2052 return CallOverloaded(object, method, resource->engine, callArgs);
2056 CallArgument::CallArgument()
2057 : type(QVariant::Invalid)
2061 CallArgument::~CallArgument()
2066 void CallArgument::cleanup()
2068 if (type == QMetaType::QString) {
2069 qstringPtr->~QString();
2070 } else if (type == -1 || type == QMetaType::QVariant) {
2071 qvariantPtr->~QVariant();
2072 } else if (type == qMetaTypeId<QJSValue>()) {
2073 qjsValuePtr->~QJSValue();
2074 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2075 qlistPtr->~QList<QObject *>();
2076 } else if (type == QMetaType::QJsonArray) {
2077 jsonArrayPtr->~QJsonArray();
2078 } else if (type == QMetaType::QJsonObject) {
2079 jsonObjectPtr->~QJsonObject();
2080 } else if (type == QMetaType::QJsonValue) {
2081 jsonValuePtr->~QJsonValue();
2085 void *CallArgument::dataPtr()
2088 return qvariantPtr->data();
2090 return (void *)&allocData;
2093 void CallArgument::initAsType(int callType)
2095 if (type != 0) { cleanup(); type = 0; }
2096 if (callType == QMetaType::UnknownType) return;
2098 if (callType == qMetaTypeId<QJSValue>()) {
2099 qjsValuePtr = new (&allocData) QJSValue();
2101 } else if (callType == QMetaType::Int ||
2102 callType == QMetaType::UInt ||
2103 callType == QMetaType::Bool ||
2104 callType == QMetaType::Double ||
2105 callType == QMetaType::Float) {
2107 } else if (callType == QMetaType::QObjectStar) {
2110 } else if (callType == QMetaType::QString) {
2111 qstringPtr = new (&allocData) QString();
2113 } else if (callType == QMetaType::QVariant) {
2115 qvariantPtr = new (&allocData) QVariant();
2116 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2118 qlistPtr = new (&allocData) QList<QObject *>();
2119 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2121 handlePtr = new (&allocData) QQmlV8Handle;
2122 } else if (callType == QMetaType::QJsonArray) {
2124 jsonArrayPtr = new (&allocData) QJsonArray();
2125 } else if (callType == QMetaType::QJsonObject) {
2127 jsonObjectPtr = new (&allocData) QJsonObject();
2128 } else if (callType == QMetaType::QJsonValue) {
2130 jsonValuePtr = new (&allocData) QJsonValue();
2131 } else if (callType == QMetaType::Void) {
2133 qvariantPtr = new (&allocData) QVariant();
2136 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2140 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2142 if (type != 0) { cleanup(); type = 0; }
2144 if (callType == qMetaTypeId<QJSValue>()) {
2145 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2146 type = qMetaTypeId<QJSValue>();
2147 } else if (callType == QMetaType::Int) {
2148 intValue = quint32(value->Int32Value());
2150 } else if (callType == QMetaType::UInt) {
2151 intValue = quint32(value->Uint32Value());
2153 } else if (callType == QMetaType::Bool) {
2154 boolValue = value->BooleanValue();
2156 } else if (callType == QMetaType::Double) {
2157 doubleValue = double(value->NumberValue());
2159 } else if (callType == QMetaType::Float) {
2160 floatValue = float(value->NumberValue());
2162 } else if (callType == QMetaType::QString) {
2163 if (value->IsNull() || value->IsUndefined())
2164 qstringPtr = new (&allocData) QString();
2166 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2168 } else if (callType == QMetaType::QObjectStar) {
2169 qobjectPtr = engine->toQObject(value);
2171 } else if (callType == qMetaTypeId<QVariant>()) {
2172 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2174 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2175 qlistPtr = new (&allocData) QList<QObject *>();
2176 if (value->IsArray()) {
2177 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2178 uint32_t length = array->Length();
2179 for (uint32_t ii = 0; ii < length; ++ii)
2180 qlistPtr->append(engine->toQObject(array->Get(ii)));
2182 qlistPtr->append(engine->toQObject(value));
2185 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2186 handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2188 } else if (callType == QMetaType::QJsonArray) {
2189 jsonArrayPtr = new (&allocData) QJsonArray(engine->jsonArrayFromJS(value));
2191 } else if (callType == QMetaType::QJsonObject) {
2192 jsonObjectPtr = new (&allocData) QJsonObject(engine->jsonObjectFromJS(value));
2194 } else if (callType == QMetaType::QJsonValue) {
2195 jsonValuePtr = new (&allocData) QJsonValue(engine->jsonValueFromJS(value));
2197 } else if (callType == QMetaType::Void) {
2198 *qvariantPtr = QVariant();
2200 qvariantPtr = new (&allocData) QVariant();
2203 QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2204 QVariant v = engine->toVariant(value, -1); // why -1 instead of callType?
2206 if (v.userType() == callType) {
2208 } else if (v.canConvert(callType)) {
2210 qvariantPtr->convert(callType);
2211 } else if (engine->sequenceWrapper()->isSequenceType(callType) && v.userType() == qMetaTypeId<QVariantList>()) {
2212 // convert the JS array to a sequence of the correct type.
2213 QVariant seqV = engine->toVariant(value, callType);
2214 *qvariantPtr = seqV;
2216 QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
2218 QObject *obj = ep->toQObject(v);
2220 if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo))
2223 *qvariantPtr = QVariant(callType, &obj);
2225 *qvariantPtr = QVariant(callType, (void *)0);
2231 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2233 if (type == qMetaTypeId<QJSValue>()) {
2234 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2235 } else if (type == QMetaType::Int) {
2236 return v8::Integer::New(int(intValue));
2237 } else if (type == QMetaType::UInt) {
2238 return v8::Integer::NewFromUnsigned(intValue);
2239 } else if (type == QMetaType::Bool) {
2240 return v8::Boolean::New(boolValue);
2241 } else if (type == QMetaType::Double) {
2242 return v8::Number::New(doubleValue);
2243 } else if (type == QMetaType::Float) {
2244 return v8::Number::New(floatValue);
2245 } else if (type == QMetaType::QString) {
2246 return engine->toString(*qstringPtr);
2247 } else if (type == QMetaType::QObjectStar) {
2248 QObject *object = qobjectPtr;
2250 QQmlData::get(object, true)->setImplicitDestructible();
2251 return engine->newQObject(object);
2252 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2253 // XXX Can this be made more by using Array as a prototype and implementing
2254 // directly against QList<QObject*>?
2255 QList<QObject *> &list = *qlistPtr;
2256 v8::Local<v8::Array> array = v8::Array::New(list.count());
2257 for (int ii = 0; ii < list.count(); ++ii)
2258 array->Set(ii, engine->newQObject(list.at(ii)));
2260 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2261 return handlePtr->toHandle();
2262 } else if (type == QMetaType::QJsonArray) {
2263 return engine->jsonArrayToJS(*jsonArrayPtr);
2264 } else if (type == QMetaType::QJsonObject) {
2265 return engine->jsonObjectToJS(*jsonObjectPtr);
2266 } else if (type == QMetaType::QJsonValue) {
2267 return engine->jsonValueToJS(*jsonValuePtr);
2268 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2269 QVariant value = *qvariantPtr;
2270 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2271 if (QObject *object = engine->toQObject(rv))
2272 QQmlData::get(object, true)->setImplicitDestructible();
2275 return v8::Undefined();