1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 QQmlData::flushPendingBinding(object, result->coreIndex);
539 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
540 QQmlData *ddata = QQmlData::get(object);
541 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
542 return v8::Handle<v8::Value>();
545 if (result->isFunction() && !result->isVarProperty()) {
546 if (result->isVMEFunction()) {
547 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
549 return vmemo->vmeMethod(result->coreIndex);
550 } else if (result->isV8Function()) {
551 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
552 } else if (result->isSignalHandler()) {
553 v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
554 QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
555 handler->SetExternalResource(r);
558 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
562 QQmlEnginePrivate *ep =
563 engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
565 if (result->hasAccessors()) {
567 QQmlNotifier **nptr = 0;
569 if (ep && ep->propertyCapture && result->accessors->notifier)
572 v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
574 if (result->accessors->notifier) {
575 if (n) ep->captureProperty(n);
577 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
583 if (ep && !result->isConstant())
584 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
586 if (result->isVarProperty()) {
587 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
589 return vmemo->vmeProperty(result->coreIndex);
590 } else if (result->isDirect()) {
591 return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
593 return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
597 // Setter for writable properties. Shared between the interceptor and fast property accessor
598 static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
599 v8::Handle<v8::Value> value)
601 QQmlBinding *newBinding = 0;
602 if (value->IsFunction()) {
603 if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
604 if (!property->isVarProperty() && property->propType != qMetaTypeId<QJSValue>()) {
605 // assigning a JS function to a non var or QJSValue property or is not allowed.
606 QString error = QLatin1String("Cannot assign JavaScript function to ");
607 if (!QMetaType::typeName(property->propType))
608 error += QLatin1String("[unknown property type]");
610 error += QLatin1String(QMetaType::typeName(property->propType));
611 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
615 // binding assignment.
616 QQmlContextData *context = engine->callingContext();
617 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
619 v8::Local<v8::StackTrace> trace =
620 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
621 v8::StackTrace::kScriptName));
622 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
623 int lineNumber = frame->GetLineNumber();
624 int columnNumber = frame->GetColumn();
625 QString url = engine->toString(frame->GetScriptName());
627 newBinding = new QQmlBinding(&function, object, context, url, qmlSourceCoordinate(lineNumber), qmlSourceCoordinate(columnNumber));
628 newBinding->setTarget(object, *property, context);
629 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
630 QQmlBinding::RequiresThisObject);
634 QQmlAbstractBinding *oldBinding =
635 QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
637 oldBinding->destroy();
639 if (!newBinding && property->isVarProperty()) {
640 // allow assignment of "special" values (null, undefined, function) to var properties
641 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
643 vmemo->setVMEProperty(property->coreIndex, value);
647 #define PROPERTY_STORE(cpptype, value) \
651 void *argv[] = { &o, 0, &status, &flags }; \
652 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
654 if (value->IsNull() && property->isQObject()) {
655 PROPERTY_STORE(QObject*, 0);
656 } else if (value->IsUndefined() && property->isResettable()) {
658 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
659 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
660 PROPERTY_STORE(QVariant, QVariant());
661 } else if (value->IsUndefined() && property->propType == QMetaType::QJsonValue) {
662 PROPERTY_STORE(QJsonValue, QJsonValue(QJsonValue::Undefined));
663 } else if (!newBinding && property->propType == qMetaTypeId<QJSValue>()) {
664 PROPERTY_STORE(QJSValue, engine->scriptValueFromInternal(value));
665 } else if (value->IsUndefined()) {
666 QString error = QLatin1String("Cannot assign [undefined] to ");
667 if (!QMetaType::typeName(property->propType))
668 error += QLatin1String("[unknown property type]");
670 error += QLatin1String(QMetaType::typeName(property->propType));
671 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
672 } else if (value->IsFunction()) {
673 // this is handled by the binding creation above
674 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
675 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
676 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
677 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
678 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
679 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
680 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
681 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
682 } else if (property->propType == QMetaType::QString && value->IsString()) {
683 PROPERTY_STORE(QString, engine->toString(value->ToString()));
684 } else if (property->isVarProperty()) {
685 QQmlVMEMetaObject *vmemo = QQmlVMEMetaObject::get(object);
687 vmemo->setVMEProperty(property->coreIndex, value);
690 if (property->isQList())
691 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
693 v = engine->toVariant(value, property->propType);
695 QQmlContextData *context = engine->callingContext();
696 if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
697 const char *valueType = 0;
698 if (v.userType() == QVariant::Invalid) valueType = "null";
699 else valueType = QMetaType::typeName(v.userType());
701 const char *targetTypeName = QMetaType::typeName(property->propType);
703 targetTypeName = "an unregistered type";
705 QString error = QLatin1String("Cannot assign ") +
706 QLatin1String(valueType) +
707 QLatin1String(" to ") +
708 QLatin1String(targetTypeName);
709 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
714 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property, QQmlContextData *context,
715 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
717 if (engine->qobjectWrapper()->m_toStringString == property ||
718 engine->qobjectWrapper()->m_destroyString == property)
721 if (QQmlData::wasDeleted(object))
724 QQmlPropertyData local;
725 QQmlPropertyData *result = 0;
726 result = QQmlPropertyCache::property(engine->engine(), object, property, context, local);
731 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
732 QQmlData *ddata = QQmlData::get(object);
733 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
737 if (!result->isWritable() && !result->isQList()) {
738 QString error = QLatin1String("Cannot assign to read-only property \"") +
739 engine->toString(property.string()) + QLatin1Char('\"');
740 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
744 StoreProperty(engine, object, result, value);
749 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
750 const v8::AccessorInfo &info)
752 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
754 if (QQmlData::wasDeleted(resource->object))
755 return v8::Handle<v8::Value>();
757 QObject *object = resource->object;
759 QHashedV8String propertystring(property);
761 QV8Engine *v8engine = resource->engine;
762 QQmlContextData *context = v8engine->callingContext();
764 v8::Handle<v8::Value> This = info.This();
765 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
766 context, QV8QObjectWrapper::IgnoreRevision);
767 if (!result.IsEmpty())
770 if (QV8Engine::startsWithUpper(property)) {
771 // Check for attached properties
772 if (context && context->imports) {
773 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
776 if (r.scriptIndex != -1) {
777 return v8::Undefined();
779 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
780 } else if (r.importNamespace) {
781 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
782 QV8TypeWrapper::ExcludeEnums);
784 Q_ASSERT(!"Unreachable");
789 return v8::Handle<v8::Value>();
792 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
793 v8::Local<v8::Value> value,
794 const v8::AccessorInfo &info)
796 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
798 if (QQmlData::wasDeleted(resource->object))
801 QObject *object = resource->object;
803 QHashedV8String propertystring(property);
805 QV8Engine *v8engine = resource->engine;
806 QQmlContextData *context = v8engine->callingContext();
807 bool result = SetProperty(v8engine, object, propertystring, context, value, QV8QObjectWrapper::IgnoreRevision);
810 QString error = QLatin1String("Cannot assign to non-existent property \"") +
811 v8engine->toString(property) + QLatin1Char('\"');
812 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
819 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
820 const v8::AccessorInfo &info)
822 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
824 if (resource->object.isNull())
825 return v8::Handle<v8::Integer>();
827 QV8Engine *engine = resource->engine;
828 QObject *object = resource->object;
829 QQmlContextData *context = engine->callingContext();
831 QHashedV8String propertystring(property);
833 QQmlPropertyData local;
834 QQmlPropertyData *result = 0;
835 result = QQmlPropertyCache::property(engine->engine(), object, propertystring, context, local);
838 return v8::Handle<v8::Integer>();
839 else if (!result->isWritable() && !result->isQList())
840 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
842 return v8::Integer::New(v8::DontDelete);
845 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
847 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
849 if (resource->object.isNull())
850 return v8::Array::New();
852 QObject *object = resource->object;
856 QQmlEnginePrivate *ep = resource->engine->engine()
857 ? QQmlEnginePrivate::get(resource->engine->engine())
860 QQmlPropertyCache *cache = 0;
861 QQmlData *ddata = QQmlData::get(object);
863 cache = ddata->propertyCache;
866 cache = ep ? ep->cache(object) : 0;
868 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
870 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
871 const QMetaObject *mo = object->metaObject();
872 int pc = mo->propertyCount();
873 int po = mo->propertyOffset();
874 for (int i=po; i<pc; ++i)
875 result << QString::fromUtf8(mo->property(i).name());
878 result = cache->propertyNames();
881 v8::Local<v8::Array> rv = v8::Array::New(result.count());
883 for (int ii = 0; ii < result.count(); ++ii)
884 rv->Set(ii, resource->engine->toString(result.at(ii)));
889 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
890 const v8::AccessorInfo& info)
892 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
894 if (QQmlData::wasDeleted(resource->object))
897 QObject *object = resource->object;
899 QQmlPropertyData *property =
900 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
902 int index = property->coreIndex;
904 QQmlData *ddata = QQmlData::get(object, false);
906 Q_ASSERT(ddata->propertyCache);
908 QQmlPropertyData *pdata = ddata->propertyCache->property(index);
911 Q_ASSERT(pdata->isWritable() || pdata->isQList());
913 StoreProperty(resource->engine, object, pdata, value);
916 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
917 const v8::AccessorInfo& info)
919 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
921 if (QQmlData::wasDeleted(resource->object))
924 QV8Engine *v8engine = resource->engine;
926 QString error = QLatin1String("Cannot assign to read-only property \"") +
927 v8engine->toString(property) + QLatin1Char('\"');
928 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
931 void QV8QObjectWrapper::WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *wrapper)
933 Q_ASSERT(handle->IsObject());
934 v8::Handle<v8::Object> v8object = handle->ToObject();
935 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(v8object);
938 static_cast<QV8QObjectWrapper*>(wrapper)->unregisterWeakQObjectReference(resource);
939 if (static_cast<QV8QObjectWrapper*>(wrapper)->deleteWeakQObject(resource, false)) {
941 v8object->SetExternalResource(0);
943 qPersistentDispose(handle);
945 handle.MakeWeak(0, WeakQObjectReferenceCallback); // revive.
949 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
951 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
952 instance->v8object.Clear();
953 qPersistentDispose(handle);
956 v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
959 Q_ASSERT(this->engine);
961 Q_ASSERT(QQmlData::get(object, false));
962 Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
965 if (constructor.IsEmpty()) {
966 v8::Local<v8::FunctionTemplate> ft;
968 const QHashedString toString(QStringLiteral("toString"));
969 const QHashedString destroy(QStringLiteral("destroy"));
971 // As we use hash linking, or with property overrides, it is possible that iterating
972 // over the values can yield duplicates. To combat this, we must unique'ify our properties.
973 const bool checkForDuplicates = stringCache.isLinked() || _hasPropertyOverrides;
975 StringCache uniqueHash;
976 if (checkForDuplicates)
977 uniqueHash.reserve(stringCache.count());
979 // XXX TODO: Enables fast property accessors. These more than double the property access
980 // performance, but the cost of setting up this structure hasn't been measured so
981 // its not guaranteed that this is a win overall. We need to try and measure the cost.
982 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
983 if (iter.equals(toString) || iter.equals(destroy))
986 if (checkForDuplicates) {
987 if (uniqueHash.contains(iter))
989 uniqueHash.insert(iter);
992 QQmlPropertyData *property = (*iter).second;
993 if (property->notFullyResolved()) resolve(property);
995 if (property->isFunction())
998 v8::AccessorGetter fastgetter = 0;
1000 if (property->isQObject())
1001 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
1002 else if (property->propType == QMetaType::Int || property->isEnum())
1003 fastgetter = FAST_GETTER_FUNCTION(property, int);
1004 else if (property->propType == QMetaType::Bool)
1005 fastgetter = FAST_GETTER_FUNCTION(property, bool);
1006 else if (property->propType == QMetaType::QString)
1007 fastgetter = FAST_GETTER_FUNCTION(property, QString);
1008 else if (property->propType == QMetaType::UInt)
1009 fastgetter = FAST_GETTER_FUNCTION(property, uint);
1010 else if (property->propType == QMetaType::Float)
1011 fastgetter = FAST_GETTER_FUNCTION(property, float);
1012 else if (property->propType == QMetaType::Double)
1013 fastgetter = FAST_GETTER_FUNCTION(property, double);
1017 ft = v8::FunctionTemplate::New();
1018 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1019 QV8QObjectWrapper::Setter,
1020 QV8QObjectWrapper::Query,
1022 QV8QObjectWrapper::Enumerator);
1023 ft->InstanceTemplate()->SetHasExternalResource(true);
1026 v8::AccessorSetter fastsetter = FastValueSetter;
1027 if (!property->isWritable())
1028 fastsetter = FastValueSetterReadOnly;
1030 // We wrap the raw QQmlPropertyData pointer here. This is safe as the
1031 // pointer will remain valid at least as long as the lifetime of any QObject's of
1032 // this type and the property accessor checks if the object is 0 (deleted) before
1033 // dereferencing the pointer.
1034 ft->InstanceTemplate()->SetAccessor(engine->toString(iter.key()), fastgetter, fastsetter,
1035 v8::External::Wrap(property));
1040 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
1042 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1043 QV8QObjectWrapper::Setter,
1044 QV8QObjectWrapper::Query,
1046 QV8QObjectWrapper::Enumerator);
1047 ft->InstanceTemplate()->SetHasExternalResource(true);
1048 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
1051 QQmlCleanup::addToEngine(this->engine);
1054 v8::Local<v8::Object> result = constructor->NewInstance();
1055 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1056 result->SetExternalResource(r);
1060 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
1062 v8::Local<v8::Object> rv;
1064 if (!ddata->propertyCache && engine->engine()) {
1065 ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
1066 if (ddata->propertyCache) ddata->propertyCache->addref();
1069 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1070 rv = ddata->propertyCache->newQObject(object, engine);
1072 // XXX NewInstance() should be optimized
1073 rv = m_constructor->NewInstance();
1074 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1075 rv->SetExternalResource(r);
1082 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1083 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
1085 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
1086 persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that
1087 "owns" this handle by setting the QQmlData::v8objectid to the id of this
1089 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create
1090 an entry in the m_taintedObject hash where we store the handle and mark the object as
1091 "tainted" in the QQmlData::hasTaintedV8Object flag.
1092 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1093 in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has
1094 released the handle.
1096 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1098 if (QQmlData::wasDeleted(object))
1101 QQmlData *ddata = QQmlData::get(object, true);
1103 return v8::Undefined();
1105 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1106 // We own the v8object
1107 return v8::Local<v8::Object>::New(ddata->v8object);
1108 } else if (ddata->v8object.IsEmpty() &&
1109 (ddata->v8objectid == m_id || // We own the QObject
1110 ddata->v8objectid == 0 || // No one owns the QObject
1111 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1113 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1114 ddata->v8object = qPersistentNew<v8::Object>(rv);
1115 ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1116 ddata->v8objectid = m_id;
1117 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1118 registerWeakQObjectReference(resource);
1122 // If this object is tainted, we have to check to see if it is in our
1123 // tainted object list
1124 TaintedHash::Iterator iter =
1125 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1126 bool found = iter != m_taintedObjects.end();
1128 // If our tainted handle doesn't exist or has been collected, and there isn't
1129 // a handle in the ddata, we can assume ownership of the ddata->v8object
1130 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1131 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1132 ddata->v8object = qPersistentNew<v8::Object>(rv);
1133 ddata->v8object.MakeWeak(this, WeakQObjectReferenceCallback);
1134 ddata->v8objectid = m_id;
1135 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(rv);
1136 registerWeakQObjectReference(resource);
1140 m_taintedObjects.erase(iter);
1144 } else if (!found) {
1145 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1146 iter = m_taintedObjects.insert(object, instance);
1147 ddata->hasTaintedV8Object = true;
1150 if ((*iter)->v8object.IsEmpty()) {
1151 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1152 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1153 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1156 return v8::Local<v8::Object>::New((*iter)->v8object);
1160 // returns true if the object's qqmldata v8object handle should
1161 // be disposed by the caller, false if it should not be (due to
1162 // creation status, etc).
1163 bool QV8QObjectWrapper::deleteWeakQObject(QV8QObjectResource *resource, bool calledFromEngineDtor)
1165 QObject *object = resource->object;
1167 QQmlData *ddata = QQmlData::get(object, false);
1169 if (!calledFromEngineDtor && ddata->rootObjectInCreation) {
1170 // if weak ref callback is triggered (by gc) for a root object
1171 // prior to completion of creation, we should NOT delete it.
1175 ddata->v8object.Clear();
1176 if (!object->parent() && !ddata->indestructible) {
1177 // This object is notionally destroyed now
1178 if (ddata->ownContext && ddata->context)
1179 ddata->context->emitDestruction();
1180 ddata->isQueuedForDeletion = true;
1181 if (calledFromEngineDtor)
1184 object->deleteLater();
1192 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1194 if (object->IsFunction())
1195 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1197 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1198 return qMakePair(resource->object.data(), resource->index);
1200 return qMakePair((QObject *)0, -1);
1203 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1205 v8::ScriptOrigin origin = function->GetScriptOrigin();
1206 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1208 // This is one of our special QObject method wrappers
1209 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1210 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1212 if (data->IsArray()) {
1213 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1214 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1217 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1220 return qMakePair((QObject *)0, -1);
1223 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1226 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1227 ~QV8QObjectConnectionList();
1231 : needsDestroy(false) {}
1232 Connection(const Connection &other)
1233 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1234 Connection &operator=(const Connection &other) {
1235 thisObject = other.thisObject;
1236 function = other.function;
1237 needsDestroy = other.needsDestroy;
1241 v8::Persistent<v8::Object> thisObject;
1242 v8::Persistent<v8::Function> function;
1245 qPersistentDispose(thisObject);
1246 qPersistentDispose(function);
1252 struct ConnectionList : public QList<Connection> {
1253 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1254 int connectionsInUse;
1255 bool connectionsNeedClean;
1260 typedef QHash<int, ConnectionList> SlotHash;
1265 virtual void objectDestroyed(QObject *);
1266 virtual int qt_metacall(QMetaObject::Call, int, void **);
1269 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1270 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1274 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1276 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1277 QList<Connection> &connections = *iter;
1278 for (int ii = 0; ii < connections.count(); ++ii) {
1279 qPersistentDispose(connections[ii].thisObject);
1280 qPersistentDispose(connections[ii].function);
1286 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1288 engine->qobjectWrapper()->m_connections.remove(object);
1291 needsDestroy = true;
1296 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1298 if (method == QMetaObject::InvokeMetaMethod) {
1299 SlotHash::Iterator iter = slotHash.find(index);
1300 if (iter == slotHash.end())
1302 ConnectionList &connectionList = *iter;
1303 if (connectionList.isEmpty())
1308 connectionList.connectionsInUse++;
1310 QList<Connection> connections = connectionList;
1312 QVarLengthArray<int, 9> dummy;
1313 int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1315 v8::HandleScope handle_scope;
1316 v8::Context::Scope scope(engine->context());
1318 int argCount = argsTypes?argsTypes[0]:0;
1319 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1321 for (int ii = 0; ii < argCount; ++ii) {
1322 int type = argsTypes[ii + 1];
1323 if (type == qMetaTypeId<QVariant>()) {
1324 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1326 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1330 for (int ii = 0; ii < connections.count(); ++ii) {
1331 Connection &connection = connections[ii];
1332 if (connection.needsDestroy)
1335 v8::TryCatch try_catch;
1336 if (connection.thisObject.IsEmpty()) {
1337 connection.function->Call(engine->global(), argCount, args.data());
1339 connection.function->Call(connection.thisObject, argCount, args.data());
1342 if (try_catch.HasCaught()) {
1344 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1345 v8::Local<v8::Message> message = try_catch.Message();
1346 if (!message.IsEmpty())
1347 QQmlExpressionPrivate::exceptionToError(message, error);
1348 QQmlEnginePrivate::get(engine->engine())->warning(error);
1352 connectionList.connectionsInUse--;
1353 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1354 for (QList<Connection>::Iterator iter = connectionList.begin();
1355 iter != connectionList.end(); ) {
1356 if (iter->needsDestroy) {
1358 iter = connectionList.erase(iter);
1366 if (inUse == 0 && needsDestroy)
1373 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1375 if (args.Length() == 0)
1376 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1378 QV8Engine *engine = V8ENGINE();
1380 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1381 QObject *signalObject = signalInfo.first;
1382 int signalIndex = signalInfo.second;
1384 if (signalIndex < 0)
1385 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1388 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1390 if (signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1391 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1393 v8::Local<v8::Value> functionValue;
1394 v8::Local<v8::Value> functionThisValue;
1396 if (args.Length() == 1) {
1397 functionValue = args[0];
1399 functionThisValue = args[0];
1400 functionValue = args[1];
1403 if (!functionValue->IsFunction())
1404 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1406 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1407 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1409 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1410 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1411 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1412 if (iter == connections.end())
1413 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1415 QV8QObjectConnectionList *connectionList = *iter;
1416 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1417 if (slotIter == connectionList->slotHash.end()) {
1418 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1419 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1422 QV8QObjectConnectionList::Connection connection;
1423 if (!functionThisValue.IsEmpty())
1424 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1425 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1427 slotIter->append(connection);
1429 return v8::Undefined();
1432 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1434 if (args.Length() == 0)
1435 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1437 QV8Engine *engine = V8ENGINE();
1439 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1440 QObject *signalObject = signalInfo.first;
1441 int signalIndex = signalInfo.second;
1443 if (signalIndex == -1)
1444 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1447 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1449 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1450 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1452 v8::Local<v8::Value> functionValue;
1453 v8::Local<v8::Value> functionThisValue;
1455 if (args.Length() == 1) {
1456 functionValue = args[0];
1458 functionThisValue = args[0];
1459 functionValue = args[1];
1462 if (!functionValue->IsFunction())
1463 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1465 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1466 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1468 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1469 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1470 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1471 if (iter == connectionsList.end())
1472 return v8::Undefined(); // Nothing to disconnect from
1474 QV8QObjectConnectionList *connectionList = *iter;
1475 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1476 if (slotIter == connectionList->slotHash.end())
1477 return v8::Undefined(); // Nothing to disconnect from
1479 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1481 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1482 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1484 if (functionData.second != -1) {
1485 // This is a QObject function wrapper
1486 for (int ii = 0; ii < connections.count(); ++ii) {
1487 QV8QObjectConnectionList::Connection &connection = connections[ii];
1489 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1490 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1492 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1493 if (connectedFunctionData == functionData) {
1495 if (connections.connectionsInUse) {
1496 connection.needsDestroy = true;
1497 connections.connectionsNeedClean = true;
1499 connection.dispose();
1500 connections.removeAt(ii);
1502 return v8::Undefined();
1508 // This is a normal JS function
1509 for (int ii = 0; ii < connections.count(); ++ii) {
1510 QV8QObjectConnectionList::Connection &connection = connections[ii];
1511 if (connection.function->StrictEquals(function) &&
1512 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1513 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1515 if (connections.connectionsInUse) {
1516 connection.needsDestroy = true;
1517 connections.connectionsNeedClean = true;
1519 connection.dispose();
1520 connections.removeAt(ii);
1522 return v8::Undefined();
1527 return v8::Undefined();
1531 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1533 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1535 Only searches for real properties of \a object (including methods), not attached properties etc.
1539 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1541 Set the \a property of \a object to \a value.
1543 Returns true if the property was "set" - even if this results in an exception being thrown -
1544 and false if the object has no such property.
1546 Only searches for real properties of \a object (including methods), not attached properties etc.
1552 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1553 int Length() const { return _length; }
1554 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1558 v8::Handle<v8::Object> *_args;
1562 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1563 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1567 // Special handling is required for value types.
1568 // We need to save the current value in a temporary,
1569 // and reapply it after converting all arguments.
1570 // This avoids the "overwriting copy-value-type-value"
1571 // problem during Q_INVOKABLE function invocation.
1572 QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1573 QVariant valueTypeValue;
1574 if (valueTypeObject)
1575 valueTypeValue = valueTypeObject->value();
1577 // Convert all arguments.
1578 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1579 args[0].initAsType(returnType);
1580 for (int ii = 0; ii < argCount; ++ii)
1581 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1582 QVarLengthArray<void *, 9> argData(args.count());
1583 for (int ii = 0; ii < args.count(); ++ii)
1584 argData[ii] = args[ii].dataPtr();
1586 // Reinstate saved value type object value if required.
1587 if (valueTypeObject)
1588 valueTypeObject->setValue(valueTypeValue);
1590 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1592 return args[0].toValue(engine);
1594 } else if (returnType != QMetaType::Void) {
1597 arg.initAsType(returnType);
1599 void *args[] = { arg.dataPtr() };
1601 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1603 return arg.toValue(engine);
1607 void *args[] = { 0 };
1608 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1609 return v8::Undefined();
1615 Returns the match score for converting \a actual to be of type \a conversionType. A
1616 zero score means "perfect match" whereas a higher score is worse.
1618 The conversion table is copied out of the QtScript callQtMethod() function.
1620 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1622 if (actual->IsNumber()) {
1623 switch (conversionType) {
1624 case QMetaType::Double:
1626 case QMetaType::Float:
1628 case QMetaType::LongLong:
1629 case QMetaType::ULongLong:
1631 case QMetaType::Long:
1632 case QMetaType::ULong:
1634 case QMetaType::Int:
1635 case QMetaType::UInt:
1637 case QMetaType::Short:
1638 case QMetaType::UShort:
1641 case QMetaType::Char:
1642 case QMetaType::UChar:
1644 case QMetaType::QJsonValue:
1649 } else if (actual->IsString()) {
1650 switch (conversionType) {
1651 case QMetaType::QString:
1653 case QMetaType::QJsonValue:
1658 } else if (actual->IsBoolean()) {
1659 switch (conversionType) {
1660 case QMetaType::Bool:
1662 case QMetaType::QJsonValue:
1667 } else if (actual->IsDate()) {
1668 switch (conversionType) {
1669 case QMetaType::QDateTime:
1671 case QMetaType::QDate:
1673 case QMetaType::QTime:
1678 } else if (actual->IsRegExp()) {
1679 switch (conversionType) {
1680 case QMetaType::QRegExp:
1685 } else if (actual->IsArray()) {
1686 switch (conversionType) {
1687 case QMetaType::QJsonArray:
1689 case QMetaType::QStringList:
1690 case QMetaType::QVariantList:
1692 case QMetaType::QVector4D:
1693 case QMetaType::QMatrix4x4:
1695 case QMetaType::QVector3D:
1700 } else if (actual->IsNull()) {
1701 switch (conversionType) {
1702 case QMetaType::VoidStar:
1703 case QMetaType::QObjectStar:
1704 case QMetaType::QJsonValue:
1707 const char *typeName = QMetaType::typeName(conversionType);
1708 if (typeName && typeName[strlen(typeName) - 1] == '*')
1714 } else if (actual->IsObject()) {
1715 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1717 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1718 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1719 switch (conversionType) {
1720 case QMetaType::QObjectStar:
1725 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1726 if (conversionType == qMetaTypeId<QVariant>())
1728 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1732 } else if (r && r->resourceType() == QV8ObjectResource::ValueTypeType) {
1733 if (r->engine->toVariant(actual, -1).userType() == conversionType)
1736 } else if (conversionType == QMetaType::QJsonObject) {
1747 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1753 int classInfoCount, classInfoData;
1754 int methodCount, methodData;
1757 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1761 Returns the next related method, if one, or 0.
1763 static const QQmlPropertyData * RelatedMethod(QObject *object,
1764 const QQmlPropertyData *current,
1765 QQmlPropertyData &dummy)
1767 QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1768 if (!current->isOverload())
1771 Q_ASSERT(!current->overrideIndexIsProperty);
1774 return cache->method(current->overrideIndex);
1776 const QMetaObject *mo = object->metaObject();
1777 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1779 while (methodOffset > current->overrideIndex) {
1780 mo = mo->superClass();
1781 methodOffset -= QMetaObject_methods(mo);
1784 QMetaMethod method = mo->method(current->overrideIndex);
1787 // Look for overloaded methods
1788 QByteArray methodName = method.name();
1789 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1790 if (methodName == mo->method(ii).name()) {
1791 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1792 dummy.overrideIndexIsProperty = 0;
1793 dummy.overrideIndex = ii;
1802 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1803 QV8Engine *engine, CallArgs &callArgs)
1805 QByteArray unknownTypeError;
1807 int returnType = QQmlPropertyCache::methodReturnType(object, data, &unknownTypeError);
1809 if (returnType == QMetaType::UnknownType) {
1810 QString typeName = QString::fromLatin1(unknownTypeError);
1811 QString error = QString::fromLatin1("Unknown method return type: %1").arg(typeName);
1812 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1813 return v8::Handle<v8::Value>();
1816 if (data.hasArguments()) {
1819 QVarLengthArray<int, 9> dummy;
1821 args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1825 QString typeName = QString::fromLatin1(unknownTypeError);
1826 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1827 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1828 return v8::Handle<v8::Value>();
1831 if (args[0] > callArgs.Length()) {
1832 QString error = QLatin1String("Insufficient arguments");
1833 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1834 return v8::Handle<v8::Value>();
1837 return CallMethod(object, data.coreIndex, returnType, args[0], args + 1, engine, callArgs);
1841 return CallMethod(object, data.coreIndex, returnType, 0, 0, engine, callArgs);
1847 Resolve the overloaded method to call. The algorithm works conceptually like this:
1848 1. Resolve the set of overloads it is *possible* to call.
1849 Impossible overloads include those that have too many parameters or have parameters
1851 2. Filter the set of overloads to only contain those with the closest number of
1853 For example, if we are called with 3 parameters and there are 2 overloads that
1854 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1855 3. Find the best remaining overload based on its match score.
1856 If two or more overloads have the same match score, call the last one. The match
1857 score is constructed by adding the matchScore() result for each of the parameters.
1859 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1860 QV8Engine *engine, CallArgs &callArgs)
1862 int argumentCount = callArgs.Length();
1864 const QQmlPropertyData *best = 0;
1865 int bestParameterScore = INT_MAX;
1866 int bestMatchScore = INT_MAX;
1868 // Special handling is required for value types.
1869 // We need to save the current value in a temporary,
1870 // and reapply it after converting all arguments.
1871 // This avoids the "overwriting copy-value-type-value"
1872 // problem during Q_INVOKABLE function invocation.
1873 QQmlValueType *valueTypeObject = qobject_cast<QQmlValueType*>(object);
1874 QVariant valueTypeValue;
1875 if (valueTypeObject)
1876 valueTypeValue = valueTypeObject->value();
1878 QQmlPropertyData dummy;
1879 const QQmlPropertyData *attempt = &data;
1882 QVarLengthArray<int, 9> dummy;
1883 int methodArgumentCount = 0;
1884 int *methodArgTypes = 0;
1885 if (attempt->hasArguments()) {
1886 typedef QQmlPropertyCache PC;
1887 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1888 if (!args) // Must be an unknown argument
1891 methodArgumentCount = args[0];
1892 methodArgTypes = args + 1;
1895 if (methodArgumentCount > argumentCount)
1896 continue; // We don't have sufficient arguments to call this method
1898 int methodParameterScore = argumentCount - methodArgumentCount;
1899 if (methodParameterScore > bestParameterScore)
1900 continue; // We already have a better option
1902 int methodMatchScore = 0;
1903 for (int ii = 0; ii < methodArgumentCount; ++ii)
1904 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1906 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1908 bestParameterScore = methodParameterScore;
1909 bestMatchScore = methodMatchScore;
1912 if (bestParameterScore == 0 && bestMatchScore == 0)
1913 break; // We can't get better than that
1915 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1918 if (valueTypeObject)
1919 valueTypeObject->setValue(valueTypeValue);
1920 return CallPrecise(object, *best, engine, callArgs);
1922 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1923 const QQmlPropertyData *candidate = &data;
1925 error += QLatin1String("\n ") +
1926 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).methodSignature().constData());
1927 candidate = RelatedMethod(object, candidate, dummy);
1930 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1931 return v8::Handle<v8::Value>();
1935 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1939 QString objectName = object->objectName();
1941 result += QString::fromUtf8(object->metaObject()->className());
1942 result += QLatin1String("(0x");
1943 result += QString::number((quintptr)object,16);
1945 if (!objectName.isEmpty()) {
1946 result += QLatin1String(", \"");
1947 result += objectName;
1948 result += QLatin1Char('\"');
1951 result += QLatin1Char(')');
1953 result = QLatin1String("null");
1956 return engine->toString(result);
1959 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1961 QQmlData *ddata = QQmlData::get(object, false);
1962 if (!ddata || ddata->indestructible || ddata->rootObjectInCreation) {
1963 const char *error = "Invalid attempt to destroy() an indestructible object";
1964 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1965 return v8::Undefined();
1970 delay = args->Get(0)->Uint32Value();
1973 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1975 object->deleteLater();
1977 return v8::Undefined();
1980 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1982 // object, index, qmlglobal, argCount, args
1983 Q_ASSERT(args.Length() == 5);
1984 Q_ASSERT(args[0]->IsObject());
1986 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1989 return v8::Undefined();
1991 int argCount = args[3]->Int32Value();
1992 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1994 // Special hack to return info about this closure.
1995 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1996 v8::Local<v8::Array> data = v8::Array::New(2);
1997 data->Set(0, args[0]);
1998 data->Set(1, args[1]);
2002 QObject *object = resource->object;
2003 int index = args[1]->Int32Value();
2006 return v8::Undefined();
2009 // Builtin functions
2010 if (index == QOBJECT_TOSTRING_INDEX) {
2011 return ToString(resource->engine, object, argCount, arguments);
2012 } else if (index == QOBJECT_DESTROY_INDEX) {
2013 return Destroy(resource->engine, object, argCount, arguments);
2015 return v8::Undefined();
2019 QQmlPropertyData method;
2021 if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
2022 if (ddata->propertyCache) {
2023 QQmlPropertyData *d = ddata->propertyCache->method(index);
2025 return v8::Undefined();
2030 if (method.coreIndex == -1) {
2031 method.load(object->metaObject()->method(index));
2033 if (method.coreIndex == -1)
2034 return v8::Undefined();
2037 if (method.isV8Function()) {
2038 v8::Handle<v8::Value> rv;
2039 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
2041 QQmlV8Function func(argCount, arguments, rv, qmlglobal,
2042 resource->engine->contextWrapper()->context(qmlglobal),
2044 QQmlV8Function *funcptr = &func;
2046 void *args[] = { 0, &funcptr };
2047 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
2049 if (rv.IsEmpty()) return v8::Undefined();
2053 CallArgs callArgs(argCount, &arguments);
2054 if (!method.isOverload()) {
2055 return CallPrecise(object, method, resource->engine, callArgs);
2057 return CallOverloaded(object, method, resource->engine, callArgs);
2061 CallArgument::CallArgument()
2062 : type(QVariant::Invalid)
2066 CallArgument::~CallArgument()
2071 void CallArgument::cleanup()
2073 if (type == QMetaType::QString) {
2074 qstringPtr->~QString();
2075 } else if (type == -1 || type == QMetaType::QVariant) {
2076 qvariantPtr->~QVariant();
2077 } else if (type == qMetaTypeId<QJSValue>()) {
2078 qjsValuePtr->~QJSValue();
2079 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2080 qlistPtr->~QList<QObject *>();
2081 } else if (type == QMetaType::QJsonArray) {
2082 jsonArrayPtr->~QJsonArray();
2083 } else if (type == QMetaType::QJsonObject) {
2084 jsonObjectPtr->~QJsonObject();
2085 } else if (type == QMetaType::QJsonValue) {
2086 jsonValuePtr->~QJsonValue();
2090 void *CallArgument::dataPtr()
2093 return qvariantPtr->data();
2095 return (void *)&allocData;
2098 void CallArgument::initAsType(int callType)
2100 if (type != 0) { cleanup(); type = 0; }
2101 if (callType == QMetaType::UnknownType) return;
2103 if (callType == qMetaTypeId<QJSValue>()) {
2104 qjsValuePtr = new (&allocData) QJSValue();
2106 } else if (callType == QMetaType::Int ||
2107 callType == QMetaType::UInt ||
2108 callType == QMetaType::Bool ||
2109 callType == QMetaType::Double ||
2110 callType == QMetaType::Float) {
2112 } else if (callType == QMetaType::QObjectStar) {
2115 } else if (callType == QMetaType::QString) {
2116 qstringPtr = new (&allocData) QString();
2118 } else if (callType == QMetaType::QVariant) {
2120 qvariantPtr = new (&allocData) QVariant();
2121 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2123 qlistPtr = new (&allocData) QList<QObject *>();
2124 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2126 handlePtr = new (&allocData) QQmlV8Handle;
2127 } else if (callType == QMetaType::QJsonArray) {
2129 jsonArrayPtr = new (&allocData) QJsonArray();
2130 } else if (callType == QMetaType::QJsonObject) {
2132 jsonObjectPtr = new (&allocData) QJsonObject();
2133 } else if (callType == QMetaType::QJsonValue) {
2135 jsonValuePtr = new (&allocData) QJsonValue();
2136 } else if (callType == QMetaType::Void) {
2138 qvariantPtr = new (&allocData) QVariant();
2141 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2145 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2147 if (type != 0) { cleanup(); type = 0; }
2149 if (callType == qMetaTypeId<QJSValue>()) {
2150 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2151 type = qMetaTypeId<QJSValue>();
2152 } else if (callType == QMetaType::Int) {
2153 intValue = quint32(value->Int32Value());
2155 } else if (callType == QMetaType::UInt) {
2156 intValue = quint32(value->Uint32Value());
2158 } else if (callType == QMetaType::Bool) {
2159 boolValue = value->BooleanValue();
2161 } else if (callType == QMetaType::Double) {
2162 doubleValue = double(value->NumberValue());
2164 } else if (callType == QMetaType::Float) {
2165 floatValue = float(value->NumberValue());
2167 } else if (callType == QMetaType::QString) {
2168 if (value->IsNull() || value->IsUndefined())
2169 qstringPtr = new (&allocData) QString();
2171 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2173 } else if (callType == QMetaType::QObjectStar) {
2174 qobjectPtr = engine->toQObject(value);
2176 } else if (callType == qMetaTypeId<QVariant>()) {
2177 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2179 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2180 qlistPtr = new (&allocData) QList<QObject *>();
2181 if (value->IsArray()) {
2182 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2183 uint32_t length = array->Length();
2184 for (uint32_t ii = 0; ii < length; ++ii)
2185 qlistPtr->append(engine->toQObject(array->Get(ii)));
2187 qlistPtr->append(engine->toQObject(value));
2190 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2191 handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2193 } else if (callType == QMetaType::QJsonArray) {
2194 jsonArrayPtr = new (&allocData) QJsonArray(engine->jsonArrayFromJS(value));
2196 } else if (callType == QMetaType::QJsonObject) {
2197 jsonObjectPtr = new (&allocData) QJsonObject(engine->jsonObjectFromJS(value));
2199 } else if (callType == QMetaType::QJsonValue) {
2200 jsonValuePtr = new (&allocData) QJsonValue(engine->jsonValueFromJS(value));
2202 } else if (callType == QMetaType::Void) {
2203 *qvariantPtr = QVariant();
2205 qvariantPtr = new (&allocData) QVariant();
2208 QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2209 QVariant v = engine->toVariant(value, -1); // why -1 instead of callType?
2211 if (v.userType() == callType) {
2213 } else if (v.canConvert(callType)) {
2215 qvariantPtr->convert(callType);
2216 } else if (engine->sequenceWrapper()->isSequenceType(callType) && v.userType() == qMetaTypeId<QVariantList>()) {
2217 // convert the JS array to a sequence of the correct type.
2218 QVariant seqV = engine->toVariant(value, callType);
2219 *qvariantPtr = seqV;
2221 QQmlMetaObject mo = ep ? ep->rawMetaObjectForType(callType) : QQmlMetaObject();
2223 QObject *obj = ep->toQObject(v);
2225 if (obj != 0 && !QQmlMetaObject::canConvert(obj, mo))
2228 *qvariantPtr = QVariant(callType, &obj);
2230 *qvariantPtr = QVariant(callType, (void *)0);
2236 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2238 if (type == qMetaTypeId<QJSValue>()) {
2239 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2240 } else if (type == QMetaType::Int) {
2241 return v8::Integer::New(int(intValue));
2242 } else if (type == QMetaType::UInt) {
2243 return v8::Integer::NewFromUnsigned(intValue);
2244 } else if (type == QMetaType::Bool) {
2245 return v8::Boolean::New(boolValue);
2246 } else if (type == QMetaType::Double) {
2247 return v8::Number::New(doubleValue);
2248 } else if (type == QMetaType::Float) {
2249 return v8::Number::New(floatValue);
2250 } else if (type == QMetaType::QString) {
2251 return engine->toString(*qstringPtr);
2252 } else if (type == QMetaType::QObjectStar) {
2253 QObject *object = qobjectPtr;
2255 QQmlData::get(object, true)->setImplicitDestructible();
2256 return engine->newQObject(object);
2257 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2258 // XXX Can this be made more by using Array as a prototype and implementing
2259 // directly against QList<QObject*>?
2260 QList<QObject *> &list = *qlistPtr;
2261 v8::Local<v8::Array> array = v8::Array::New(list.count());
2262 for (int ii = 0; ii < list.count(); ++ii)
2263 array->Set(ii, engine->newQObject(list.at(ii)));
2265 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2266 return handlePtr->toHandle();
2267 } else if (type == QMetaType::QJsonArray) {
2268 return engine->jsonArrayToJS(*jsonArrayPtr);
2269 } else if (type == QMetaType::QJsonObject) {
2270 return engine->jsonObjectToJS(*jsonObjectPtr);
2271 } else if (type == QMetaType::QJsonValue) {
2272 return engine->jsonValueToJS(*jsonValuePtr);
2273 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2274 QVariant value = *qvariantPtr;
2275 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2276 if (QObject *object = engine->toQObject(rv))
2277 QQmlData::get(object, true)->setImplicitDestructible();
2280 return v8::Undefined();