aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/qml/v8/qv8qobjectwrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/qml/v8/qv8qobjectwrapper.cpp')
-rw-r--r--src/qml/qml/v8/qv8qobjectwrapper.cpp2113
1 files changed, 2113 insertions, 0 deletions
diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp
new file mode 100644
index 0000000000..78b2cb719c
--- /dev/null
+++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp
@@ -0,0 +1,2113 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv8qobjectwrapper_p.h"
+#include "qv8contextwrapper_p.h"
+#include "qv8engine_p.h"
+
+#include <private/qqmlguard_p.h>
+#include <private/qqmlpropertycache_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlvmemetaobject_p.h>
+#include <private/qqmlbinding_p.h>
+#include <private/qjsvalue_p.h>
+#include <private/qscript_impl_p.h>
+#include <private/qqmlaccessors_p.h>
+#include <private/qqmlexpression_p.h>
+
+#include <QtQml/qjsvalue.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qatomic.h>
+
+Q_DECLARE_METATYPE(QJSValue);
+Q_DECLARE_METATYPE(QQmlV8Handle);
+
+QT_BEGIN_NAMESPACE
+
+#if defined(__GNUC__)
+# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
+// The code in this file does not violate strict aliasing, but GCC thinks it does
+// so turn off the warnings for us to have a clean build
+# pragma GCC diagnostic ignored "-Wstrict-aliasing"
+# endif
+#endif
+
+#define QOBJECT_TOSTRING_INDEX -2
+#define QOBJECT_DESTROY_INDEX -3
+
+// XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work
+// correctly in a worker thread
+
+class QV8QObjectResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(QObjectType);
+
+public:
+ QV8QObjectResource(QV8Engine *engine, QObject *object);
+
+ QQmlGuard<QObject> object;
+};
+
+class QV8QObjectInstance : public QQmlGuard<QObject>
+{
+public:
+ QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
+ : QQmlGuard<QObject>(o), wrapper(w)
+ {
+ }
+
+ ~QV8QObjectInstance()
+ {
+ qPersistentDispose(v8object);
+ }
+
+ virtual void objectDestroyed(QObject *o)
+ {
+ if (wrapper)
+ wrapper->m_taintedObjects.remove(o);
+ delete this;
+ }
+
+ v8::Persistent<v8::Object> v8object;
+ QV8QObjectWrapper *wrapper;
+};
+
+class QV8SignalHandlerResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(SignalHandlerType)
+public:
+ QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index);
+
+ QQmlGuard<QObject> object;
+ int index;
+};
+
+namespace {
+
+template<typename A, typename B, typename C, typename D, typename E>
+class MaxSizeOf5 {
+ template<typename Z, typename X>
+ struct SMax {
+ static const size_t Size = sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X);
+ };
+public:
+ static const size_t Size = SMax<A, SMax<B, SMax<C, SMax<D, E> > > >::Size;
+};
+
+struct CallArgument {
+ inline CallArgument();
+ inline ~CallArgument();
+ inline void *dataPtr();
+
+ inline void initAsType(int type);
+ inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
+ inline v8::Handle<v8::Value> toValue(QV8Engine *);
+
+private:
+ CallArgument(const CallArgument &);
+
+ inline void cleanup();
+
+ union {
+ float floatValue;
+ double doubleValue;
+ quint32 intValue;
+ bool boolValue;
+ QObject *qobjectPtr;
+
+ char allocData[MaxSizeOf5<QVariant,
+ QString,
+ QList<QObject *>,
+ QJSValue,
+ QQmlV8Handle>::Size];
+ qint64 q_for_alignment;
+ };
+
+ // Pointers to allocData
+ union {
+ QString *qstringPtr;
+ QVariant *qvariantPtr;
+ QList<QObject *> *qlistPtr;
+ QJSValue *qjsValuePtr;
+ QQmlV8Handle *handlePtr;
+ };
+
+ int type;
+};
+}
+
+QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
+: QV8ObjectResource(engine), object(object)
+{
+}
+
+QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index)
+: QV8ObjectResource(engine), object(object), index(index)
+{
+}
+
+static QAtomicInt objectIdCounter(1);
+
+QV8QObjectWrapper::QV8QObjectWrapper()
+: m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
+{
+}
+
+QV8QObjectWrapper::~QV8QObjectWrapper()
+{
+ for (TaintedHash::Iterator iter = m_taintedObjects.begin();
+ iter != m_taintedObjects.end();
+ ++iter) {
+ (*iter)->wrapper = 0;
+ }
+ m_taintedObjects.clear();
+}
+
+void QV8QObjectWrapper::destroy()
+{
+ qDeleteAll(m_connections);
+ m_connections.clear();
+
+ qPersistentDispose(m_hiddenObject);
+ qPersistentDispose(m_destroySymbol);
+ qPersistentDispose(m_toStringSymbol);
+ qPersistentDispose(m_signalHandlerConstructor);
+ qPersistentDispose(m_methodConstructor);
+ qPersistentDispose(m_constructor);
+}
+
+struct ReadAccessor {
+ static inline void Indirect(QObject *object, const QQmlPropertyData &property,
+ void *output, QQmlNotifier **n)
+ {
+ Q_ASSERT(n == 0);
+ Q_UNUSED(n);
+
+ void *args[] = { output, 0 };
+ QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
+ }
+
+ static inline void Direct(QObject *object, const QQmlPropertyData &property,
+ void *output, QQmlNotifier **n)
+ {
+ Q_ASSERT(n == 0);
+ Q_UNUSED(n);
+
+ void *args[] = { output, 0 };
+ object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
+ }
+
+ static inline void Accessor(QObject *object, const QQmlPropertyData &property,
+ void *output, QQmlNotifier **n)
+ {
+ Q_ASSERT(property.accessors);
+
+ property.accessors->read(object, property.accessorData, output);
+ if (n) property.accessors->notifier(object, property.accessorData, n);
+ }
+};
+
+static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, int v)
+{ return v8::Integer::New(v); }
+static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, uint v)
+{ return v8::Integer::NewFromUnsigned(v); }
+static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, bool v)
+{ return v8::Boolean::New(v); }
+static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, const QString &v)
+{ return e->toString(v); }
+static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, float v)
+{ return v8::Number::New(v); }
+static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, double v)
+{ return v8::Number::New(v); }
+static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, QObject *v)
+{ return e->newQObject(v); }
+
+template<typename T, void (*ReadFunction)(QObject *, const QQmlPropertyData &,
+ void *, QQmlNotifier **)>
+static v8::Handle<v8::Value> GenericValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ v8::Handle<v8::Object> This = info.This();
+ QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This);
+
+ QObject *object = resource->object;
+ if (!object) return v8::Undefined();
+
+ QQmlPropertyData *property =
+ (QQmlPropertyData *)v8::External::Unwrap(info.Data());
+
+ QQmlEngine *engine = resource->engine->engine();
+ QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0;
+
+ T value = T();
+
+ if (ep && ep->propertyCapture) {
+ if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) {
+ QQmlNotifier *notifier = 0;
+ ReadFunction(object, *property, &value, &notifier);
+ if (notifier) ep->captureProperty(notifier);
+ } else if (!property->isConstant()) {
+ ep->captureProperty(object, property->coreIndex, property->notifyIndex);
+ ReadFunction(object, *property, &value, 0);
+ } else {
+ ReadFunction(object, *property, &value, 0);
+ }
+ } else {
+ ReadFunction(object, *property, &value, 0);
+ }
+
+ return valueToHandle(resource->engine, value);
+}
+
+#define FAST_GETTER_FUNCTION(property, cpptype) \
+ (property->hasAccessors()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Accessor>):(property->isDirect()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Direct>):((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Indirect>)))
+
+static quint32 toStringHash = -1;
+static quint32 destroyHash = -1;
+
+void QV8QObjectWrapper::init(QV8Engine *engine)
+{
+ m_engine = engine;
+
+ m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
+ m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
+ m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
+
+ m_toStringString = QHashedV8String(m_toStringSymbol);
+ m_destroyString = QHashedV8String(m_destroySymbol);
+
+ toStringHash = m_toStringString.hash();
+ destroyHash = m_destroyString.hash();
+
+ {
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
+ }
+ {
+ v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
+#define CREATE_FUNCTION_SOURCE \
+ "(function(method) { "\
+ "return (function(object, data, qmlglobal) { "\
+ "return (function() { "\
+ "return method(object, data, qmlglobal, arguments.length, arguments); "\
+ "});"\
+ "});"\
+ "})"
+ v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION_SOURCE), &origin, 0,
+ v8::Handle<v8::String>(), v8::Script::NativeMode);
+#undef CREATE_FUNCTION_SOURCE
+ v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
+ v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
+ v8::Handle<v8::Value> args[] = { invokeFn };
+ v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
+ m_methodConstructor = qPersistentNew<v8::Function>(createFn);
+ }
+
+ v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine);
+ v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine);
+
+ {
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum);
+ ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
+ m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
+ }
+
+ {
+ v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
+ prototype->Set(v8::String::New("connect"), connect, v8::DontEnum);
+ prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
+ }
+}
+
+bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
+{
+ return v8_resource_cast<QV8QObjectResource>(obj) != 0;
+}
+
+QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
+{
+ QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
+ return r?r->object:0;
+}
+
+// r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
+QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
+{
+ Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
+ return static_cast<QV8QObjectResource *>(r)->object;
+}
+
+// Load value properties
+template<void (*ReadFunction)(QObject *, const QQmlPropertyData &,
+ void *, QQmlNotifier **)>
+static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
+ const QQmlPropertyData &property,
+ QQmlNotifier **notifier)
+{
+ Q_ASSERT(!property.isFunction());
+
+ if (property.isQObject()) {
+ QObject *rv = 0;
+ ReadFunction(object, property, &rv, notifier);
+ return engine->newQObject(rv);
+ } else if (property.isQList()) {
+ return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
+ } else if (property.propType == QMetaType::QReal) {
+ qreal v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(engine, v);
+ } else if (property.propType == QMetaType::Int || property.isEnum()) {
+ int v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(engine, v);
+ } else if (property.propType == QMetaType::Bool) {
+ bool v = false;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(engine, v);
+ } else if (property.propType == QMetaType::QString) {
+ QString v;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(engine, v);
+ } else if (property.propType == QMetaType::UInt) {
+ uint v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(engine, v);
+ } else if (property.propType == QMetaType::Float) {
+ float v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(engine, v);
+ } else if (property.propType == QMetaType::Double) {
+ double v = 0;
+ ReadFunction(object, property, &v, notifier);
+ return valueToHandle(engine, v);
+ } else if (property.isV8Handle()) {
+ QQmlV8Handle handle;
+ ReadFunction(object, property, &handle, notifier);
+ return handle.toHandle();
+ } else if (property.isQVariant()) {
+ QVariant v;
+ ReadFunction(object, property, &v, notifier);
+ return engine->fromVariant(v);
+ } else if (QQmlValueTypeFactory::isValueType((uint)property.propType)
+ && engine->engine()) {
+ Q_ASSERT(notifier == 0);
+
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
+ QQmlValueType *valueType = ep->valueTypes[property.propType];
+ if (valueType)
+ return engine->newValueType(object, property.coreIndex, valueType);
+ } else {
+ Q_ASSERT(notifier == 0);
+
+ // see if it's a sequence type
+ bool succeeded = false;
+ v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex,
+ &succeeded);
+ if (succeeded)
+ return retn;
+ }
+
+ if (property.propType == QVariant::Invalid) {
+ QMetaProperty p = object->metaObject()->property(property.coreIndex);
+ qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
+ "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
+ return v8::Undefined();
+ } else {
+ QVariant v(property.propType, (void *)0);
+ ReadFunction(object, property, v.data(), notifier);
+ return engine->fromVariant(v);
+ }
+}
+
+v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
+ v8::Handle<v8::Value> *objectHandle,
+ const QHashedV8String &property,
+ QV8QObjectWrapper::RevisionMode revisionMode)
+{
+ // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
+ // will be a faster way of creating QObject method objects.
+ struct MethodClosure {
+ static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
+ v8::Handle<v8::Value> *objectHandle,
+ int index) {
+ v8::Handle<v8::Value> argv[] = {
+ objectHandle?*objectHandle:engine->newQObject(object),
+ v8::Integer::New(index)
+ };
+ return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
+ }
+ static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
+ v8::Handle<v8::Value> *objectHandle,
+ int index) {
+ v8::Handle<v8::Value> argv[] = {
+ objectHandle?*objectHandle:engine->newQObject(object),
+ v8::Integer::New(index),
+ v8::Context::GetCallingQmlGlobal()
+ };
+ return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
+ }
+ };
+
+ {
+ // Comparing the hash first actually makes a measurable difference here, at least on x86
+ quint32 hash = property.hash();
+ if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
+ return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
+ } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
+ return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
+ }
+ }
+
+ QQmlPropertyData local;
+ QQmlPropertyData *result = 0;
+ {
+ QQmlData *ddata = QQmlData::get(object, false);
+ if (ddata && ddata->propertyCache)
+ result = ddata->propertyCache->property(property);
+ else
+ result = QQmlPropertyCache::property(engine->engine(), object, property, local);
+ }
+
+ if (!result)
+ return v8::Handle<v8::Value>();
+
+ if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
+ QQmlData *ddata = QQmlData::get(object);
+ if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
+ return v8::Handle<v8::Value>();
+ }
+
+ if (result->isFunction()) {
+ if (result->isVMEFunction()) {
+ return ((QQmlVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
+ } else if (result->isV8Function()) {
+ return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
+ } else if (result->isSignalHandler()) {
+ v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
+ QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
+ handler->SetExternalResource(r);
+ return handler;
+ } else {
+ return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
+ }
+ }
+
+ QQmlEnginePrivate *ep =
+ engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
+
+ if (result->hasAccessors()) {
+ QQmlNotifier *n = 0;
+ QQmlNotifier **nptr = 0;
+
+ if (ep && ep->propertyCapture && result->accessors->notifier)
+ nptr = &n;
+
+ v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
+
+ if (result->accessors->notifier) {
+ if (n) ep->captureProperty(n);
+ } else {
+ ep->captureProperty(object, result->coreIndex, result->notifyIndex);
+ }
+
+ return rv;
+ }
+
+ if (ep && !result->isConstant()) {
+
+ if (result->coreIndex == 0)
+ ep->captureProperty(QQmlData::get(object, true)->objectNameNotifier());
+ else
+ ep->captureProperty(object, result->coreIndex, result->notifyIndex);
+ }
+
+ if (result->isVMEProperty()) {
+ typedef QQmlVMEMetaObject VMEMO;
+ VMEMO *vmemo = const_cast<VMEMO *>(static_cast<const VMEMO *>(object->metaObject()));
+ return vmemo->vmeProperty(result->coreIndex);
+ } else if (result->isDirect()) {
+ return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
+ } else {
+ return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
+ }
+}
+
+// Setter for writable properties. Shared between the interceptor and fast property accessor
+static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
+ v8::Handle<v8::Value> value)
+{
+ QQmlBinding *newBinding = 0;
+
+ if (value->IsFunction()) {
+ QQmlContextData *context = engine->callingContext();
+ v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
+
+ v8::Local<v8::StackTrace> trace =
+ v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
+ v8::StackTrace::kScriptName));
+ v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
+ int lineNumber = frame->GetLineNumber();
+ int columNumber = frame->GetColumn();
+ QString url = engine->toString(frame->GetScriptName());
+
+ newBinding = new QQmlBinding(&function, object, context);
+ newBinding->setSourceLocation(url, lineNumber, columNumber);
+ newBinding->setTarget(object, *property, context);
+ newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
+ QQmlBinding::RequiresThisObject);
+ }
+
+ QQmlAbstractBinding *oldBinding =
+ QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
+ if (oldBinding)
+ oldBinding->destroy();
+
+#define PROPERTY_STORE(cpptype, value) \
+ cpptype o = value; \
+ int status = -1; \
+ int flags = 0; \
+ void *argv[] = { &o, 0, &status, &flags }; \
+ QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
+
+
+ if (value->IsNull() && property->isQObject()) {
+ PROPERTY_STORE(QObject*, 0);
+ } else if (value->IsUndefined() && property->isResettable()) {
+ void *a[] = { 0 };
+ QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
+ } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
+ PROPERTY_STORE(QVariant, QVariant());
+ } else if (value->IsUndefined()) {
+ QString error = QLatin1String("Cannot assign [undefined] to ") +
+ QLatin1String(QMetaType::typeName(property->propType));
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ } else if (value->IsFunction()) {
+ // this is handled by the binding creation above
+ } else if (property->propType == QMetaType::Int && value->IsNumber()) {
+ PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
+ } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
+ PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
+ } else if (property->propType == QMetaType::Float && value->IsNumber()) {
+ PROPERTY_STORE(float, float(value->ToNumber()->Value()));
+ } else if (property->propType == QMetaType::Double && value->IsNumber()) {
+ PROPERTY_STORE(double, double(value->ToNumber()->Value()));
+ } else if (property->propType == QMetaType::QString && value->IsString()) {
+ PROPERTY_STORE(QString, engine->toString(value->ToString()));
+ } else if (property->isVMEProperty()) {
+ static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
+ } else {
+ QVariant v;
+ if (property->isQList())
+ v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
+ else
+ v = engine->toVariant(value, property->propType);
+
+ QQmlContextData *context = engine->callingContext();
+ if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
+ const char *valueType = 0;
+ if (v.userType() == QVariant::Invalid) valueType = "null";
+ else valueType = QMetaType::typeName(v.userType());
+
+ QString error = QLatin1String("Cannot assign ") +
+ QLatin1String(valueType) +
+ QLatin1String(" to ") +
+ QLatin1String(QMetaType::typeName(property->propType));
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ }
+ }
+}
+
+bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
+ v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
+{
+ if (engine->qobjectWrapper()->m_toStringString == property ||
+ engine->qobjectWrapper()->m_destroyString == property)
+ return true;
+
+ QQmlPropertyData local;
+ QQmlPropertyData *result = 0;
+ result = QQmlPropertyCache::property(engine->engine(), object, property, local);
+
+ if (!result)
+ return false;
+
+ if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
+ QQmlData *ddata = QQmlData::get(object);
+ if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
+ return false;
+ }
+
+ if (!result->isWritable() && !result->isQList()) {
+ QString error = QLatin1String("Cannot assign to read-only property \"") +
+ engine->toString(property.string()) + QLatin1Char('\"');
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ return true;
+ }
+
+ StoreProperty(engine, object, result, value);
+
+ return true;
+}
+
+v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
+ const v8::AccessorInfo &info)
+{
+ QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
+
+ if (resource->object.isNull())
+ return v8::Handle<v8::Value>();
+
+ QObject *object = resource->object;
+
+ QHashedV8String propertystring(property);
+
+ QV8Engine *v8engine = resource->engine;
+ v8::Handle<v8::Value> This = info.This();
+ v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
+ QV8QObjectWrapper::IgnoreRevision);
+ if (!result.IsEmpty())
+ return result;
+
+ if (QV8Engine::startsWithUpper(property)) {
+ // Check for attached properties
+ QQmlContextData *context = v8engine->callingContext();
+
+ if (context && context->imports) {
+ QQmlTypeNameCache::Result r = context->imports->query(propertystring);
+
+ if (r.isValid()) {
+ if (r.scriptIndex != -1) {
+ return v8::Undefined();
+ } else if (r.type) {
+ return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
+ } else if (r.importNamespace) {
+ return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
+ QV8TypeWrapper::ExcludeEnums);
+ }
+ Q_ASSERT(!"Unreachable");
+ }
+ }
+ }
+
+ return v8::Handle<v8::Value>();
+}
+
+v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
+ v8::Local<v8::Value> value,
+ const v8::AccessorInfo &info)
+{
+ QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
+
+ if (resource->object.isNull())
+ return value;
+
+ QObject *object = resource->object;
+
+ QHashedV8String propertystring(property);
+
+ QV8Engine *v8engine = resource->engine;
+ bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
+
+ if (!result) {
+ QString error = QLatin1String("Cannot assign to non-existent property \"") +
+ v8engine->toString(property) + QLatin1Char('\"');
+ v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
+ return value;
+ }
+
+ return value;
+}
+
+v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
+ const v8::AccessorInfo &info)
+{
+ QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
+
+ if (resource->object.isNull())
+ return v8::Handle<v8::Integer>();
+
+ QV8Engine *engine = resource->engine;
+ QObject *object = resource->object;
+
+ QHashedV8String propertystring(property);
+
+ QQmlPropertyData local;
+ QQmlPropertyData *result = 0;
+ result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local);
+
+ if (!result)
+ return v8::Handle<v8::Integer>();
+ else if (!result->isWritable() && !result->isQList())
+ return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
+ else
+ return v8::Integer::New(v8::DontDelete);
+}
+
+v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
+{
+ QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
+
+ if (resource->object.isNull())
+ return v8::Array::New();
+
+ QObject *object = resource->object;
+
+ QStringList result;
+
+ QQmlEnginePrivate *ep = resource->engine->engine()
+ ? QQmlEnginePrivate::get(resource->engine->engine())
+ : 0;
+
+ QQmlPropertyCache *cache = 0;
+ QQmlData *ddata = QQmlData::get(object);
+ if (ddata)
+ cache = ddata->propertyCache;
+
+ if (!cache) {
+ cache = ep ? ep->cache(object) : 0;
+ if (cache) {
+ if (ddata) { cache->addref(); ddata->propertyCache = cache; }
+ } else {
+ // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
+ const QMetaObject *mo = object->metaObject();
+ int pc = mo->propertyCount();
+ int po = mo->propertyOffset();
+ for (int i=po; i<pc; ++i)
+ result << QString::fromUtf8(mo->property(i).name());
+ }
+ } else {
+ result = cache->propertyNames();
+ }
+
+ v8::Local<v8::Array> rv = v8::Array::New(result.count());
+
+ for (int ii = 0; ii < result.count(); ++ii)
+ rv->Set(ii, resource->engine->toString(result.at(ii)));
+
+ return rv;
+}
+
+static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
+ const v8::AccessorInfo& info)
+{
+ QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
+
+ if (resource->object.isNull())
+ return;
+
+ QObject *object = resource->object;
+
+ QQmlPropertyData *property =
+ (QQmlPropertyData *)v8::External::Unwrap(info.Data());
+
+ int index = property->coreIndex;
+
+ QQmlData *ddata = QQmlData::get(object, false);
+ Q_ASSERT(ddata);
+ Q_ASSERT(ddata->propertyCache);
+
+ QQmlPropertyData *pdata = ddata->propertyCache->property(index);
+ Q_ASSERT(pdata);
+
+ Q_ASSERT(pdata->isWritable() || pdata->isQList());
+
+ StoreProperty(resource->engine, object, pdata, value);
+}
+
+static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
+ const v8::AccessorInfo& info)
+{
+ QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
+
+ if (resource->object.isNull())
+ return;
+
+ QV8Engine *v8engine = resource->engine;
+
+ QString error = QLatin1String("Cannot assign to read-only property \"") +
+ v8engine->toString(property) + QLatin1Char('\"');
+ v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
+}
+
+static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
+{
+ Q_ASSERT(handle->IsObject());
+
+ QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
+
+ Q_ASSERT(resource);
+
+ QObject *object = resource->object;
+ if (object) {
+ QQmlData *ddata = QQmlData::get(object, false);
+ if (ddata) {
+ ddata->v8object.Clear();
+ if (!object->parent() && !ddata->indestructible)
+ object->deleteLater();
+ }
+ }
+
+ qPersistentDispose(handle);
+}
+
+static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
+{
+ QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
+ instance->v8object.Clear();
+ qPersistentDispose(handle);
+}
+
+v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
+{
+ Q_ASSERT(object);
+ Q_ASSERT(this->engine);
+
+ Q_ASSERT(QQmlData::get(object, false));
+ Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
+
+ // Setup constructor
+ if (constructor.IsEmpty()) {
+ v8::Local<v8::FunctionTemplate> ft;
+
+ QString toString = QLatin1String("toString");
+ QString destroy = QLatin1String("destroy");
+
+ // As we use hash linking, it is possible that iterating over the values can give duplicates.
+ // To combat this, we must unique'ify our properties.
+ StringCache uniqueHash;
+ if (stringCache.isLinked())
+ uniqueHash.reserve(stringCache.count());
+
+ // XXX TODO: Enables fast property accessors. These more than double the property access
+ // performance, but the cost of setting up this structure hasn't been measured so
+ // its not guarenteed that this is a win overall. We need to try and measure the cost.
+ for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
+ if (stringCache.isLinked()) {
+ if (uniqueHash.contains(iter))
+ continue;
+ uniqueHash.insert(iter);
+ }
+
+ QQmlPropertyData *property = *iter;
+ if (property->notFullyResolved()) resolve(property);
+
+ if (property->isFunction())
+ continue;
+
+ v8::AccessorGetter fastgetter = 0;
+ v8::AccessorSetter fastsetter = FastValueSetter;
+ if (!property->isWritable())
+ fastsetter = FastValueSetterReadOnly;
+
+ if (property->isQObject())
+ fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
+ else if (property->propType == QMetaType::Int || property->isEnum())
+ fastgetter = FAST_GETTER_FUNCTION(property, int);
+ else if (property->propType == QMetaType::Bool)
+ fastgetter = FAST_GETTER_FUNCTION(property, bool);
+ else if (property->propType == QMetaType::QString)
+ fastgetter = FAST_GETTER_FUNCTION(property, QString);
+ else if (property->propType == QMetaType::UInt)
+ fastgetter = FAST_GETTER_FUNCTION(property, uint);
+ else if (property->propType == QMetaType::Float)
+ fastgetter = FAST_GETTER_FUNCTION(property, float);
+ else if (property->propType == QMetaType::Double)
+ fastgetter = FAST_GETTER_FUNCTION(property, double);
+
+ if (fastgetter) {
+ QString name = iter.key();
+ if (name == toString || name == destroy)
+ continue;
+
+ if (ft.IsEmpty()) {
+ ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
+ QV8QObjectWrapper::Setter,
+ QV8QObjectWrapper::Query,
+ 0,
+ QV8QObjectWrapper::Enumerator);
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ }
+
+ // We wrap the raw QQmlPropertyData pointer here. This is safe as the
+ // pointer will remain valid at least as long as the lifetime of any QObject's of
+ // this type and the property accessor checks if the object is 0 (deleted) before
+ // dereferencing the pointer.
+ ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
+ v8::External::Wrap(property));
+ }
+ }
+
+ if (ft.IsEmpty()) {
+ constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
+ } else {
+ ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
+ QV8QObjectWrapper::Setter,
+ QV8QObjectWrapper::Query,
+ 0,
+ QV8QObjectWrapper::Enumerator);
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ constructor = qPersistentNew<v8::Function>(ft->GetFunction());
+ }
+
+ QQmlCleanup::addToEngine(this->engine);
+ }
+
+ v8::Local<v8::Object> result = constructor->NewInstance();
+ QV8QObjectResource *r = new QV8QObjectResource(engine, object);
+ result->SetExternalResource(r);
+ return result;
+}
+
+v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
+{
+ v8::Local<v8::Object> rv;
+
+ if (!ddata->propertyCache && engine->engine()) {
+ ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
+ if (ddata->propertyCache) ddata->propertyCache->addref();
+ }
+
+ if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
+ rv = ddata->propertyCache->newQObject(object, engine);
+ } else {
+ // XXX NewInstance() should be optimized
+ rv = m_constructor->NewInstance();
+ QV8QObjectResource *r = new QV8QObjectResource(engine, object);
+ rv->SetExternalResource(r);
+ }
+
+ return rv;
+}
+
+/*
+As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
+V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
+pronged strategy:
+ 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
+ persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that
+ "owns" this handle by setting the QQmlData::v8objectid to the id of this
+ QV8QObjectWrapper.
+ 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create
+ an entry in the m_taintedObject hash where we store the handle and mark the object as
+ "tainted" in the QQmlData::hasTaintedV8Object flag.
+We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
+in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has
+released the handle.
+*/
+v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
+{
+ if (!object)
+ return v8::Null();
+
+ if (QObjectPrivate::get(object)->wasDeleted)
+ return v8::Undefined();
+
+ QQmlData *ddata = QQmlData::get(object, true);
+
+ if (!ddata)
+ return v8::Undefined();
+
+ if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
+ // We own the v8object
+ return v8::Local<v8::Object>::New(ddata->v8object);
+ } else if (ddata->v8object.IsEmpty() &&
+ (ddata->v8objectid == m_id || // We own the QObject
+ ddata->v8objectid == 0 || // No one owns the QObject
+ !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
+
+ v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
+ ddata->v8object = qPersistentNew<v8::Object>(rv);
+ ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
+ ddata->v8objectid = m_id;
+ return rv;
+
+ } else {
+ // If this object is tainted, we have to check to see if it is in our
+ // tainted object list
+ TaintedHash::Iterator iter =
+ ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
+ bool found = iter != m_taintedObjects.end();
+
+ // If our tainted handle doesn't exist or has been collected, and there isn't
+ // a handle in the ddata, we can assume ownership of the ddata->v8object
+ if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
+ v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
+ ddata->v8object = qPersistentNew<v8::Object>(rv);
+ ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
+ ddata->v8objectid = m_id;
+
+ if (found) {
+ delete (*iter);
+ m_taintedObjects.erase(iter);
+ }
+
+ return rv;
+ } else if (!found) {
+ QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
+ iter = m_taintedObjects.insert(object, instance);
+ ddata->hasTaintedV8Object = true;
+ }
+
+ if ((*iter)->v8object.IsEmpty()) {
+ v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
+ (*iter)->v8object = qPersistentNew<v8::Object>(rv);
+ (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
+ }
+
+ return v8::Local<v8::Object>::New((*iter)->v8object);
+ }
+}
+
+QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
+{
+ if (object->IsFunction())
+ return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
+
+ if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
+ return qMakePair(resource->object.data(), resource->index);
+
+ return qMakePair((QObject *)0, -1);
+}
+
+QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
+{
+ v8::ScriptOrigin origin = function->GetScriptOrigin();
+ if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
+
+ // This is one of our special QObject method wrappers
+ v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
+ v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
+
+ if (data->IsArray()) {
+ v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
+ return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
+ }
+
+ // In theory this can't fall through, but I suppose V8 might run out of memory or something
+ }
+
+ return qMakePair((QObject *)0, -1);
+}
+
+class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
+{
+public:
+ QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
+ ~QV8QObjectConnectionList();
+
+ struct Connection {
+ Connection()
+ : needsDestroy(false) {}
+ Connection(const Connection &other)
+ : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
+ Connection &operator=(const Connection &other) {
+ thisObject = other.thisObject;
+ function = other.function;
+ needsDestroy = other.needsDestroy;
+ return *this;
+ }
+
+ v8::Persistent<v8::Object> thisObject;
+ v8::Persistent<v8::Function> function;
+
+ void dispose() {
+ qPersistentDispose(thisObject);
+ qPersistentDispose(function);
+ }
+
+ bool needsDestroy;
+ };
+
+ struct ConnectionList : public QList<Connection> {
+ ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
+ int connectionsInUse;
+ bool connectionsNeedClean;
+ };
+
+ QV8Engine *engine;
+
+ typedef QHash<int, ConnectionList> SlotHash;
+ SlotHash slotHash;
+ bool needsDestroy;
+ int inUse;
+
+ virtual void objectDestroyed(QObject *);
+ virtual int qt_metacall(QMetaObject::Call, int, void **);
+};
+
+QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
+: QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
+{
+}
+
+QV8QObjectConnectionList::~QV8QObjectConnectionList()
+{
+ for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
+ QList<Connection> &connections = *iter;
+ for (int ii = 0; ii < connections.count(); ++ii) {
+ qPersistentDispose(connections[ii].thisObject);
+ qPersistentDispose(connections[ii].function);
+ }
+ }
+ slotHash.clear();
+}
+
+void QV8QObjectConnectionList::objectDestroyed(QObject *object)
+{
+ engine->qobjectWrapper()->m_connections.remove(object);
+
+ if (inUse)
+ needsDestroy = true;
+ else
+ delete this;
+}
+
+int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
+{
+ if (method == QMetaObject::InvokeMetaMethod) {
+ SlotHash::Iterator iter = slotHash.find(index);
+ if (iter == slotHash.end())
+ return -1;
+ ConnectionList &connectionList = *iter;
+ if (connectionList.isEmpty())
+ return -1;
+
+ inUse++;
+
+ connectionList.connectionsInUse++;
+
+ QList<Connection> connections = connectionList;
+
+ QVarLengthArray<int, 9> dummy;
+ int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
+
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(engine->context());
+
+ int argCount = argsTypes?argsTypes[0]:0;
+ QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
+
+ for (int ii = 0; ii < argCount; ++ii) {
+ int type = argsTypes[ii + 1];
+ if (type == qMetaTypeId<QVariant>()) {
+ args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
+ } else {
+ args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
+ }
+ }
+
+ for (int ii = 0; ii < connections.count(); ++ii) {
+ Connection &connection = connections[ii];
+ if (connection.needsDestroy)
+ continue;
+
+ v8::TryCatch try_catch;
+ if (connection.thisObject.IsEmpty()) {
+ connection.function->Call(engine->global(), argCount, args.data());
+ } else {
+ connection.function->Call(connection.thisObject, argCount, args.data());
+ }
+
+ if (try_catch.HasCaught()) {
+ QQmlError error;
+ error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
+ v8::Local<v8::Message> message = try_catch.Message();
+ if (!message.IsEmpty())
+ QQmlExpressionPrivate::exceptionToError(message, error);
+ QQmlEnginePrivate::get(engine->engine())->warning(error);
+ }
+ }
+
+ connectionList.connectionsInUse--;
+ if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
+ for (QList<Connection>::Iterator iter = connectionList.begin();
+ iter != connectionList.end(); ) {
+ if (iter->needsDestroy) {
+ iter->dispose();
+ iter = connectionList.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ }
+
+ inUse--;
+ if (inUse == 0 && needsDestroy)
+ delete this;
+ }
+
+ return -1;
+}
+
+v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
+{
+ if (args.Length() == 0)
+ V8THROW_ERROR("Function.prototype.connect: no arguments given");
+
+ QV8Engine *engine = V8ENGINE();
+
+ QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
+ QObject *signalObject = signalInfo.first;
+ int signalIndex = signalInfo.second;
+
+ if (signalIndex == -1)
+ V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
+
+ if (!signalObject)
+ V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
+
+ if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
+ V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
+
+ v8::Local<v8::Value> functionValue;
+ v8::Local<v8::Value> functionThisValue;
+
+ if (args.Length() == 1) {
+ functionValue = args[0];
+ } else {
+ functionThisValue = args[0];
+ functionValue = args[1];
+ }
+
+ if (!functionValue->IsFunction())
+ V8THROW_ERROR("Function.prototype.connect: target is not a function");
+
+ if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
+ V8THROW_ERROR("Function.prototype.connect: target this is not an object");
+
+ QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
+ QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
+ QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
+ if (iter == connections.end())
+ iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
+
+ QV8QObjectConnectionList *connectionList = *iter;
+ QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
+ if (slotIter == connectionList->slotHash.end()) {
+ slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
+ QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
+ }
+
+ QV8QObjectConnectionList::Connection connection;
+ if (!functionThisValue.IsEmpty())
+ connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
+ connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
+
+ slotIter->append(connection);
+
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
+{
+ if (args.Length() == 0)
+ V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
+
+ QV8Engine *engine = V8ENGINE();
+
+ QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
+ QObject *signalObject = signalInfo.first;
+ int signalIndex = signalInfo.second;
+
+ if (signalIndex == -1)
+ V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
+
+ if (!signalObject)
+ V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
+
+ if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
+ V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
+
+ v8::Local<v8::Value> functionValue;
+ v8::Local<v8::Value> functionThisValue;
+
+ if (args.Length() == 1) {
+ functionValue = args[0];
+ } else {
+ functionThisValue = args[0];
+ functionValue = args[1];
+ }
+
+ if (!functionValue->IsFunction())
+ V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
+
+ if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
+ V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
+
+ QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
+ QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
+ QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
+ if (iter == connectionsList.end())
+ return v8::Undefined(); // Nothing to disconnect from
+
+ QV8QObjectConnectionList *connectionList = *iter;
+ QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
+ if (slotIter == connectionList->slotHash.end())
+ return v8::Undefined(); // Nothing to disconnect from
+
+ QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
+
+ v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
+ QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
+
+ if (functionData.second != -1) {
+ // This is a QObject function wrapper
+ for (int ii = 0; ii < connections.count(); ++ii) {
+ QV8QObjectConnectionList::Connection &connection = connections[ii];
+
+ if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
+ (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
+
+ QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
+ if (connectedFunctionData == functionData) {
+ // Match!
+ if (connections.connectionsInUse) {
+ connection.needsDestroy = true;
+ connections.connectionsNeedClean = true;
+ } else {
+ connection.dispose();
+ connections.removeAt(ii);
+ }
+ return v8::Undefined();
+ }
+ }
+ }
+
+ } else {
+ // This is a normal JS function
+ for (int ii = 0; ii < connections.count(); ++ii) {
+ QV8QObjectConnectionList::Connection &connection = connections[ii];
+ if (connection.function->StrictEquals(function) &&
+ connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
+ (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
+ // Match!
+ if (connections.connectionsInUse) {
+ connection.needsDestroy = true;
+ connections.connectionsNeedClean = true;
+ } else {
+ connection.dispose();
+ connections.removeAt(ii);
+ }
+ return v8::Undefined();
+ }
+ }
+ }
+
+ return v8::Undefined();
+}
+
+/*!
+ \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
+
+ Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
+
+ Only searches for real properties of \a object (including methods), not attached properties etc.
+*/
+
+/*
+ \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
+
+ Set the \a property of \a object to \a value.
+
+ Returns true if the property was "set" - even if this results in an exception being thrown -
+ and false if the object has no such property.
+
+ Only searches for real properties of \a object (including methods), not attached properties etc.
+*/
+
+namespace {
+struct CallArgs
+{
+ CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
+ int Length() const { return _length; }
+ v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
+
+private:
+ int _length;
+ v8::Handle<v8::Object> *_args;
+};
+}
+
+static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
+ int *argTypes, QV8Engine *engine, CallArgs &callArgs)
+{
+ if (argCount > 0) {
+
+ QVarLengthArray<CallArgument, 9> args(argCount + 1);
+ args[0].initAsType(returnType);
+
+ for (int ii = 0; ii < argCount; ++ii)
+ args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
+
+ QVarLengthArray<void *, 9> argData(args.count());
+ for (int ii = 0; ii < args.count(); ++ii)
+ argData[ii] = args[ii].dataPtr();
+
+ QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
+
+ return args[0].toValue(engine);
+
+ } else if (returnType != 0) {
+
+ CallArgument arg;
+ arg.initAsType(returnType);
+
+ void *args[] = { arg.dataPtr() };
+
+ QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
+
+ return arg.toValue(engine);
+
+ } else {
+
+ void *args[] = { 0 };
+ QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
+ return v8::Undefined();
+
+ }
+}
+
+/*!
+ Returns the match score for converting \a actual to be of type \a conversionType. A
+ zero score means "perfect match" whereas a higher score is worse.
+
+ The conversion table is copied out of the QtScript callQtMethod() function.
+*/
+static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
+{
+ if (actual->IsNumber()) {
+ switch (conversionType) {
+ case QMetaType::Double:
+ return 0;
+ case QMetaType::Float:
+ return 1;
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ return 2;
+ case QMetaType::Long:
+ case QMetaType::ULong:
+ return 3;
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ return 4;
+ case QMetaType::Short:
+ case QMetaType::UShort:
+ return 5;
+ break;
+ case QMetaType::Char:
+ case QMetaType::UChar:
+ return 6;
+ default:
+ return 10;
+ }
+ } else if (actual->IsString()) {
+ switch (conversionType) {
+ case QMetaType::QString:
+ return 0;
+ default:
+ return 10;
+ }
+ } else if (actual->IsBoolean()) {
+ switch (conversionType) {
+ case QMetaType::Bool:
+ return 0;
+ default:
+ return 10;
+ }
+ } else if (actual->IsDate()) {
+ switch (conversionType) {
+ case QMetaType::QDateTime:
+ return 0;
+ case QMetaType::QDate:
+ return 1;
+ case QMetaType::QTime:
+ return 2;
+ default:
+ return 10;
+ }
+ } else if (actual->IsRegExp()) {
+ switch (conversionType) {
+ case QMetaType::QRegExp:
+ return 0;
+ default:
+ return 10;
+ }
+ } else if (actual->IsArray()) {
+ switch (conversionType) {
+ case QMetaType::QStringList:
+ case QMetaType::QVariantList:
+ return 5;
+ default:
+ return 10;
+ }
+ } else if (actual->IsNull()) {
+ switch (conversionType) {
+ case QMetaType::VoidStar:
+ case QMetaType::QObjectStar:
+ return 0;
+ default: {
+ const char *typeName = QMetaType::typeName(conversionType);
+ if (typeName && typeName[strlen(typeName) - 1] == '*')
+ return 0;
+ else
+ return 10;
+ }
+ }
+ } else if (actual->IsObject()) {
+ v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
+
+ QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
+ if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
+ switch (conversionType) {
+ case QMetaType::QObjectStar:
+ return 0;
+ default:
+ return 10;
+ }
+ } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
+ if (conversionType == qMetaTypeId<QVariant>())
+ return 0;
+ else if (r->engine->toVariant(actual, -1).userType() == conversionType)
+ return 0;
+ else
+ return 10;
+ } else {
+ return 10;
+ }
+
+ } else {
+ return 10;
+ }
+}
+
+static inline int QMetaObject_methods(const QMetaObject *metaObject)
+{
+ struct Private
+ {
+ int revision;
+ int className;
+ int classInfoCount, classInfoData;
+ int methodCount, methodData;
+ };
+
+ return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
+}
+
+static QByteArray QMetaMethod_name(const QMetaMethod &m)
+{
+ QByteArray sig = m.signature();
+ int paren = sig.indexOf('(');
+ if (paren == -1)
+ return sig;
+ else
+ return sig.left(paren);
+}
+
+/*!
+Returns the next related method, if one, or 0.
+*/
+static const QQmlPropertyData * RelatedMethod(QObject *object,
+ const QQmlPropertyData *current,
+ QQmlPropertyData &dummy)
+{
+ QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
+ if (!current->isOverload())
+ return 0;
+
+ Q_ASSERT(!current->overrideIndexIsProperty);
+
+ if (cache) {
+ return cache->method(current->overrideIndex);
+ } else {
+ const QMetaObject *mo = object->metaObject();
+ int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
+
+ while (methodOffset > current->overrideIndex) {
+ mo = mo->superClass();
+ methodOffset -= QMetaObject_methods(mo);
+ }
+
+ QMetaMethod method = mo->method(current->overrideIndex);
+ dummy.load(method);
+
+ // Look for overloaded methods
+ QByteArray methodName = QMetaMethod_name(method);
+ for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
+ if (methodName == QMetaMethod_name(mo->method(ii))) {
+ dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
+ dummy.overrideIndexIsProperty = 0;
+ dummy.overrideIndex = ii;
+ return &dummy;
+ }
+ }
+
+ return &dummy;
+ }
+}
+
+static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
+ QV8Engine *engine, CallArgs &callArgs)
+{
+ if (data.hasArguments()) {
+
+ int *args = 0;
+ QVarLengthArray<int, 9> dummy;
+ QByteArray unknownTypeError;
+
+ args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
+ &unknownTypeError);
+
+ if (!args) {
+ QString typeName = QString::fromLatin1(unknownTypeError);
+ QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ return v8::Handle<v8::Value>();
+ }
+
+ if (args[0] > callArgs.Length()) {
+ QString error = QLatin1String("Insufficient arguments");
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ return v8::Handle<v8::Value>();
+ }
+
+ return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
+
+ } else {
+
+ return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
+
+ }
+}
+
+/*!
+Resolve the overloaded method to call. The algorithm works conceptually like this:
+ 1. Resolve the set of overloads it is *possible* to call.
+ Impossible overloads include those that have too many parameters or have parameters
+ of unknown type.
+ 2. Filter the set of overloads to only contain those with the closest number of
+ parameters.
+ For example, if we are called with 3 parameters and there are 2 overloads that
+ take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
+ 3. Find the best remaining overload based on its match score.
+ If two or more overloads have the same match score, call the last one. The match
+ score is constructed by adding the matchScore() result for each of the parameters.
+*/
+static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
+ QV8Engine *engine, CallArgs &callArgs)
+{
+ int argumentCount = callArgs.Length();
+
+ const QQmlPropertyData *best = 0;
+ int bestParameterScore = INT_MAX;
+ int bestMatchScore = INT_MAX;
+
+ QQmlPropertyData dummy;
+ const QQmlPropertyData *attempt = &data;
+
+ do {
+ QVarLengthArray<int, 9> dummy;
+ int methodArgumentCount = 0;
+ int *methodArgTypes = 0;
+ if (attempt->hasArguments()) {
+ typedef QQmlPropertyCache PC;
+ int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
+ if (!args) // Must be an unknown argument
+ continue;
+
+ methodArgumentCount = args[0];
+ methodArgTypes = args + 1;
+ }
+
+ if (methodArgumentCount > argumentCount)
+ continue; // We don't have sufficient arguments to call this method
+
+ int methodParameterScore = argumentCount - methodArgumentCount;
+ if (methodParameterScore > bestParameterScore)
+ continue; // We already have a better option
+
+ int methodMatchScore = 0;
+ for (int ii = 0; ii < methodArgumentCount; ++ii)
+ methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
+
+ if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
+ best = attempt;
+ bestParameterScore = methodParameterScore;
+ bestMatchScore = methodMatchScore;
+ }
+
+ if (bestParameterScore == 0 && bestMatchScore == 0)
+ break; // We can't get better than that
+
+ } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
+
+ if (best) {
+ return CallPrecise(object, *best, engine, callArgs);
+ } else {
+ QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
+ const QQmlPropertyData *candidate = &data;
+ while (candidate) {
+ error += QLatin1String("\n ") +
+ QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
+ candidate = RelatedMethod(object, candidate, dummy);
+ }
+
+ v8::ThrowException(v8::Exception::Error(engine->toString(error)));
+ return v8::Handle<v8::Value>();
+ }
+}
+
+static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
+{
+ QString result;
+ if (object) {
+ QString objectName = object->objectName();
+
+ result += QString::fromUtf8(object->metaObject()->className());
+ result += QLatin1String("(0x");
+ result += QString::number((quintptr)object,16);
+
+ if (!objectName.isEmpty()) {
+ result += QLatin1String(", \"");
+ result += objectName;
+ result += QLatin1Char('\"');
+ }
+
+ result += QLatin1Char(')');
+ } else {
+ result = QLatin1String("null");
+ }
+
+ return engine->toString(result);
+}
+
+static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
+{
+ QQmlData *ddata = QQmlData::get(object, false);
+ if (!ddata || ddata->indestructible) {
+ const char *error = "Invalid attempt to destroy() an indestructible object";
+ v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
+ return v8::Undefined();
+ }
+
+ int delay = 0;
+ if (argCount > 0)
+ delay = args->Get(0)->Uint32Value();
+
+ if (delay > 0)
+ QTimer::singleShot(delay, object, SLOT(deleteLater()));
+ else
+ object->deleteLater();
+
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
+{
+ // object, index, qmlglobal, argCount, args
+ Q_ASSERT(args.Length() == 5);
+ Q_ASSERT(args[0]->IsObject());
+
+ QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
+
+ if (!resource)
+ return v8::Undefined();
+
+ int argCount = args[3]->Int32Value();
+ v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
+
+ // Special hack to return info about this closure.
+ if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
+ v8::Local<v8::Array> data = v8::Array::New(2);
+ data->Set(0, args[0]);
+ data->Set(1, args[1]);
+ return data;
+ }
+
+ QObject *object = resource->object;
+ int index = args[1]->Int32Value();
+
+ if (!object)
+ return v8::Undefined();
+
+ if (index < 0) {
+ // Builtin functions
+ if (index == QOBJECT_TOSTRING_INDEX) {
+ return ToString(resource->engine, object, argCount, arguments);
+ } else if (index == QOBJECT_DESTROY_INDEX) {
+ return Destroy(resource->engine, object, argCount, arguments);
+ } else {
+ return v8::Undefined();
+ }
+ }
+
+ QQmlPropertyData method;
+
+ if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
+ if (ddata->propertyCache) {
+ QQmlPropertyData *d = ddata->propertyCache->method(index);
+ if (!d)
+ return v8::Undefined();
+ method = *d;
+ }
+ }
+
+ if (method.coreIndex == -1) {
+ method.load(object->metaObject()->method(index));
+
+ if (method.coreIndex == -1)
+ return v8::Undefined();
+ }
+
+ if (method.isV8Function()) {
+ v8::Handle<v8::Value> rv;
+ v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
+
+ QQmlV8Function func(argCount, arguments, rv, qmlglobal,
+ resource->engine->contextWrapper()->context(qmlglobal),
+ resource->engine);
+ QQmlV8Function *funcptr = &func;
+
+ void *args[] = { 0, &funcptr };
+ QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
+
+ if (rv.IsEmpty()) return v8::Undefined();
+ return rv;
+ }
+
+ CallArgs callArgs(argCount, &arguments);
+ if (!method.isOverload()) {
+ return CallPrecise(object, method, resource->engine, callArgs);
+ } else {
+ return CallOverloaded(object, method, resource->engine, callArgs);
+ }
+}
+
+CallArgument::CallArgument()
+: type(QVariant::Invalid)
+{
+}
+
+CallArgument::~CallArgument()
+{
+ cleanup();
+}
+
+void CallArgument::cleanup()
+{
+ if (type == QMetaType::QString) {
+ qstringPtr->~QString();
+ } else if (type == -1 || type == QMetaType::QVariant) {
+ qvariantPtr->~QVariant();
+ } else if (type == qMetaTypeId<QJSValue>()) {
+ qjsValuePtr->~QJSValue();
+ } else if (type == qMetaTypeId<QList<QObject *> >()) {
+ qlistPtr->~QList<QObject *>();
+ }
+}
+
+void *CallArgument::dataPtr()
+{
+ if (type == -1)
+ return qvariantPtr->data();
+ else
+ return (void *)&allocData;
+}
+
+void CallArgument::initAsType(int callType)
+{
+ if (type != 0) { cleanup(); type = 0; }
+ if (callType == 0) return;
+
+ if (callType == qMetaTypeId<QJSValue>()) {
+ qjsValuePtr = new (&allocData) QJSValue();
+ type = callType;
+ } else if (callType == QMetaType::Int ||
+ callType == QMetaType::UInt ||
+ callType == QMetaType::Bool ||
+ callType == QMetaType::Double ||
+ callType == QMetaType::Float) {
+ type = callType;
+ } else if (callType == QMetaType::QObjectStar) {
+ qobjectPtr = 0;
+ type = callType;
+ } else if (callType == QMetaType::QString) {
+ qstringPtr = new (&allocData) QString();
+ type = callType;
+ } else if (callType == QMetaType::QVariant) {
+ type = callType;
+ qvariantPtr = new (&allocData) QVariant();
+ } else if (callType == qMetaTypeId<QList<QObject *> >()) {
+ type = callType;
+ qlistPtr = new (&allocData) QList<QObject *>();
+ } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
+ type = callType;
+ handlePtr = new (&allocData) QQmlV8Handle;
+ } else {
+ type = -1;
+ qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
+ }
+}
+
+void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
+{
+ if (type != 0) { cleanup(); type = 0; }
+
+ if (callType == qMetaTypeId<QJSValue>()) {
+ qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
+ type = qMetaTypeId<QJSValue>();
+ } else if (callType == QMetaType::Int) {
+ intValue = quint32(value->Int32Value());
+ type = callType;
+ } else if (callType == QMetaType::UInt) {
+ intValue = quint32(value->Uint32Value());
+ type = callType;
+ } else if (callType == QMetaType::Bool) {
+ boolValue = value->BooleanValue();
+ type = callType;
+ } else if (callType == QMetaType::Double) {
+ doubleValue = double(value->NumberValue());
+ type = callType;
+ } else if (callType == QMetaType::Float) {
+ floatValue = float(value->NumberValue());
+ type = callType;
+ } else if (callType == QMetaType::QString) {
+ if (value->IsNull() || value->IsUndefined())
+ qstringPtr = new (&allocData) QString();
+ else
+ qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
+ type = callType;
+ } else if (callType == QMetaType::QObjectStar) {
+ qobjectPtr = engine->toQObject(value);
+ type = callType;
+ } else if (callType == qMetaTypeId<QVariant>()) {
+ qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
+ type = callType;
+ } else if (callType == qMetaTypeId<QList<QObject*> >()) {
+ qlistPtr = new (&allocData) QList<QObject *>();
+ if (value->IsArray()) {
+ v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
+ uint32_t length = array->Length();
+ for (uint32_t ii = 0; ii < length; ++ii)
+ qlistPtr->append(engine->toQObject(array->Get(ii)));
+ } else {
+ qlistPtr->append(engine->toQObject(value));
+ }
+ type = callType;
+ } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
+ handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
+ type = callType;
+ } else {
+ qvariantPtr = new (&allocData) QVariant();
+ type = -1;
+
+ QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
+ QVariant v = engine->toVariant(value, -1);
+
+ if (v.userType() == callType) {
+ *qvariantPtr = v;
+ } else if (v.canConvert((QVariant::Type)callType)) {
+ *qvariantPtr = v;
+ qvariantPtr->convert((QVariant::Type)callType);
+ } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
+ QObject *obj = ep->toQObject(v);
+
+ if (obj) {
+ const QMetaObject *objMo = obj->metaObject();
+ while (objMo && objMo != mo) objMo = objMo->superClass();
+ if (!objMo) obj = 0;
+ }
+
+ *qvariantPtr = QVariant(callType, &obj);
+ } else {
+ *qvariantPtr = QVariant(callType, (void *)0);
+ }
+ }
+}
+
+v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
+{
+ if (type == qMetaTypeId<QJSValue>()) {
+ return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
+ } else if (type == QMetaType::Int) {
+ return v8::Integer::New(int(intValue));
+ } else if (type == QMetaType::UInt) {
+ return v8::Integer::NewFromUnsigned(intValue);
+ } else if (type == QMetaType::Bool) {
+ return v8::Boolean::New(boolValue);
+ } else if (type == QMetaType::Double) {
+ return v8::Number::New(doubleValue);
+ } else if (type == QMetaType::Float) {
+ return v8::Number::New(floatValue);
+ } else if (type == QMetaType::QString) {
+ return engine->toString(*qstringPtr);
+ } else if (type == QMetaType::QObjectStar) {
+ QObject *object = qobjectPtr;
+ if (object)
+ QQmlData::get(object, true)->setImplicitDestructible();
+ return engine->newQObject(object);
+ } else if (type == qMetaTypeId<QList<QObject *> >()) {
+ // XXX Can this be made more by using Array as a prototype and implementing
+ // directly against QList<QObject*>?
+ QList<QObject *> &list = *qlistPtr;
+ v8::Local<v8::Array> array = v8::Array::New(list.count());
+ for (int ii = 0; ii < list.count(); ++ii)
+ array->Set(ii, engine->newQObject(list.at(ii)));
+ return array;
+ } else if (type == qMetaTypeId<QQmlV8Handle>()) {
+ return handlePtr->toHandle();
+ } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
+ QVariant value = *qvariantPtr;
+ v8::Handle<v8::Value> rv = engine->fromVariant(value);
+ if (QObject *object = engine->toQObject(rv))
+ QQmlData::get(object, true)->setImplicitDestructible();
+ return rv;
+ } else {
+ return v8::Undefined();
+ }
+}
+
+QT_END_NAMESPACE
+