1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qv8qobjectwrapper_p.h"
43 #include "qv8contextwrapper_p.h"
44 #include "qv8engine_p.h"
46 #include <private/qqmlguard_p.h>
47 #include <private/qqmlpropertycache_p.h>
48 #include <private/qqmlengine_p.h>
49 #include <private/qqmlvmemetaobject_p.h>
50 #include <private/qqmlbinding_p.h>
51 #include <private/qjsvalue_p.h>
52 #include <private/qscript_impl_p.h>
53 #include <private/qqmlaccessors_p.h>
54 #include <private/qqmlexpression_p.h>
56 #include <QtQml/qjsvalue.h>
57 #include <QtCore/qvarlengtharray.h>
58 #include <QtCore/qtimer.h>
59 #include <QtCore/qatomic.h>
61 Q_DECLARE_METATYPE(QJSValue);
62 Q_DECLARE_METATYPE(QQmlV8Handle);
67 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
68 // The code in this file does not violate strict aliasing, but GCC thinks it does
69 // so turn off the warnings for us to have a clean build
70 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
74 #define QOBJECT_TOSTRING_INDEX -2
75 #define QOBJECT_DESTROY_INDEX -3
77 // XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work
78 // correctly in a worker thread
80 class QV8QObjectResource : public QV8ObjectResource
82 V8_RESOURCE_TYPE(QObjectType);
85 QV8QObjectResource(QV8Engine *engine, QObject *object);
87 QQmlGuard<QObject> object;
90 class QV8QObjectInstance : public QQmlGuard<QObject>
93 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
94 : QQmlGuard<QObject>(o), wrapper(w)
100 qPersistentDispose(v8object);
103 virtual void objectDestroyed(QObject *o)
106 wrapper->m_taintedObjects.remove(o);
110 v8::Persistent<v8::Object> v8object;
111 QV8QObjectWrapper *wrapper;
114 class QV8SignalHandlerResource : public QV8ObjectResource
116 V8_RESOURCE_TYPE(SignalHandlerType)
118 QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index);
120 QQmlGuard<QObject> object;
126 template<typename A, typename B, typename C, typename D, typename E>
128 template<typename Z, typename X>
130 static const size_t Size = sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X);
133 static const size_t Size = SMax<A, SMax<B, SMax<C, SMax<D, E> > > >::Size;
136 struct CallArgument {
137 inline CallArgument();
138 inline ~CallArgument();
139 inline void *dataPtr();
141 inline void initAsType(int type);
142 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
143 inline v8::Handle<v8::Value> toValue(QV8Engine *);
146 CallArgument(const CallArgument &);
148 inline void cleanup();
157 char allocData[MaxSizeOf5<QVariant,
161 QQmlV8Handle>::Size];
162 qint64 q_for_alignment;
165 // Pointers to allocData
168 QVariant *qvariantPtr;
169 QList<QObject *> *qlistPtr;
170 QJSValue *qjsValuePtr;
171 QQmlV8Handle *handlePtr;
178 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
179 : QV8ObjectResource(engine), object(object)
183 QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index)
184 : QV8ObjectResource(engine), object(object), index(index)
188 static QAtomicInt objectIdCounter(1);
190 QV8QObjectWrapper::QV8QObjectWrapper()
191 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
195 QV8QObjectWrapper::~QV8QObjectWrapper()
197 for (TaintedHash::Iterator iter = m_taintedObjects.begin();
198 iter != m_taintedObjects.end();
200 (*iter)->wrapper = 0;
202 m_taintedObjects.clear();
205 void QV8QObjectWrapper::destroy()
207 qDeleteAll(m_connections);
208 m_connections.clear();
210 qPersistentDispose(m_hiddenObject);
211 qPersistentDispose(m_destroySymbol);
212 qPersistentDispose(m_toStringSymbol);
213 qPersistentDispose(m_signalHandlerConstructor);
214 qPersistentDispose(m_methodConstructor);
215 qPersistentDispose(m_constructor);
218 struct ReadAccessor {
219 static inline void Indirect(QObject *object, const QQmlPropertyData &property,
220 void *output, QQmlNotifier **n)
225 void *args[] = { output, 0 };
226 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
229 static inline void Direct(QObject *object, const QQmlPropertyData &property,
230 void *output, QQmlNotifier **n)
235 void *args[] = { output, 0 };
236 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
239 static inline void Accessor(QObject *object, const QQmlPropertyData &property,
240 void *output, QQmlNotifier **n)
242 Q_ASSERT(property.accessors);
244 property.accessors->read(object, property.accessorData, output);
245 if (n) property.accessors->notifier(object, property.accessorData, n);
249 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, int v)
250 { return v8::Integer::New(v); }
251 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, uint v)
252 { return v8::Integer::NewFromUnsigned(v); }
253 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, bool v)
254 { return v8::Boolean::New(v); }
255 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, const QString &v)
256 { return e->toString(v); }
257 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, float v)
258 { return v8::Number::New(v); }
259 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, double v)
260 { return v8::Number::New(v); }
261 static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, QObject *v)
262 { return e->newQObject(v); }
264 template<typename T, void (*ReadFunction)(QObject *, const QQmlPropertyData &,
265 void *, QQmlNotifier **)>
266 static v8::Handle<v8::Value> GenericValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info)
268 v8::Handle<v8::Object> This = info.This();
269 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This);
271 QObject *object = resource->object;
272 if (!object) return v8::Undefined();
274 QQmlPropertyData *property =
275 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
277 QQmlEngine *engine = resource->engine->engine();
278 QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0;
282 if (ep && ep->propertyCapture) {
283 if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) {
284 QQmlNotifier *notifier = 0;
285 ReadFunction(object, *property, &value, ¬ifier);
286 if (notifier) ep->captureProperty(notifier);
287 } else if (!property->isConstant()) {
288 ep->captureProperty(object, property->coreIndex, property->notifyIndex);
289 ReadFunction(object, *property, &value, 0);
291 ReadFunction(object, *property, &value, 0);
294 ReadFunction(object, *property, &value, 0);
297 return valueToHandle(resource->engine, value);
300 #define FAST_GETTER_FUNCTION(property, cpptype) \
301 (property->hasAccessors()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Accessor>):(property->isDirect()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Direct>):((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Indirect>)))
303 static quint32 toStringHash = -1;
304 static quint32 destroyHash = -1;
306 void QV8QObjectWrapper::init(QV8Engine *engine)
310 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
311 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
312 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
314 m_toStringString = QHashedV8String(m_toStringSymbol);
315 m_destroyString = QHashedV8String(m_destroySymbol);
317 toStringHash = m_toStringString.hash();
318 destroyHash = m_destroyString.hash();
321 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
322 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
323 ft->InstanceTemplate()->SetHasExternalResource(true);
324 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
327 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
328 #define CREATE_FUNCTION_SOURCE \
329 "(function(method) { "\
330 "return (function(object, data, qmlglobal) { "\
331 "return (function() { "\
332 "return method(object, data, qmlglobal, arguments.length, arguments); "\
336 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION_SOURCE), &origin, 0,
337 v8::Handle<v8::String>(), v8::Script::NativeMode);
338 #undef CREATE_FUNCTION_SOURCE
339 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
340 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
341 v8::Handle<v8::Value> args[] = { invokeFn };
342 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
343 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
346 v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine);
347 v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine);
350 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
351 ft->InstanceTemplate()->SetHasExternalResource(true);
352 ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum);
353 ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
354 m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction());
358 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
359 prototype->Set(v8::String::New("connect"), connect, v8::DontEnum);
360 prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum);
364 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
366 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
369 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
371 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
372 return r?r->object:0;
375 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
376 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
378 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
379 return static_cast<QV8QObjectResource *>(r)->object;
382 // Load value properties
383 template<void (*ReadFunction)(QObject *, const QQmlPropertyData &,
384 void *, QQmlNotifier **)>
385 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
386 const QQmlPropertyData &property,
387 QQmlNotifier **notifier)
389 Q_ASSERT(!property.isFunction());
391 if (property.isQObject()) {
393 ReadFunction(object, property, &rv, notifier);
394 return engine->newQObject(rv);
395 } else if (property.isQList()) {
396 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
397 } else if (property.propType == QMetaType::QReal) {
399 ReadFunction(object, property, &v, notifier);
400 return valueToHandle(engine, v);
401 } else if (property.propType == QMetaType::Int || property.isEnum()) {
403 ReadFunction(object, property, &v, notifier);
404 return valueToHandle(engine, v);
405 } else if (property.propType == QMetaType::Bool) {
407 ReadFunction(object, property, &v, notifier);
408 return valueToHandle(engine, v);
409 } else if (property.propType == QMetaType::QString) {
411 ReadFunction(object, property, &v, notifier);
412 return valueToHandle(engine, v);
413 } else if (property.propType == QMetaType::UInt) {
415 ReadFunction(object, property, &v, notifier);
416 return valueToHandle(engine, v);
417 } else if (property.propType == QMetaType::Float) {
419 ReadFunction(object, property, &v, notifier);
420 return valueToHandle(engine, v);
421 } else if (property.propType == QMetaType::Double) {
423 ReadFunction(object, property, &v, notifier);
424 return valueToHandle(engine, v);
425 } else if (property.isV8Handle()) {
427 ReadFunction(object, property, &handle, notifier);
428 return handle.toHandle();
429 } else if (property.isQVariant()) {
431 ReadFunction(object, property, &v, notifier);
432 return engine->fromVariant(v);
433 } else if (QQmlValueTypeFactory::isValueType((uint)property.propType)
434 && engine->engine()) {
435 Q_ASSERT(notifier == 0);
437 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine());
438 QQmlValueType *valueType = ep->valueTypes[property.propType];
440 return engine->newValueType(object, property.coreIndex, valueType);
442 Q_ASSERT(notifier == 0);
444 // see if it's a sequence type
445 bool succeeded = false;
446 v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex,
452 if (property.propType == QVariant::Invalid) {
453 QMetaProperty p = object->metaObject()->property(property.coreIndex);
454 qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property "
455 "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name());
456 return v8::Undefined();
458 QVariant v(property.propType, (void *)0);
459 ReadFunction(object, property, v.data(), notifier);
460 return engine->fromVariant(v);
464 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
465 v8::Handle<v8::Value> *objectHandle,
466 const QHashedV8String &property,
467 QV8QObjectWrapper::RevisionMode revisionMode)
469 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
470 // will be a faster way of creating QObject method objects.
471 struct MethodClosure {
472 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
473 v8::Handle<v8::Value> *objectHandle,
475 v8::Handle<v8::Value> argv[] = {
476 objectHandle?*objectHandle:engine->newQObject(object),
477 v8::Integer::New(index)
479 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
481 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
482 v8::Handle<v8::Value> *objectHandle,
484 v8::Handle<v8::Value> argv[] = {
485 objectHandle?*objectHandle:engine->newQObject(object),
486 v8::Integer::New(index),
487 v8::Context::GetCallingQmlGlobal()
489 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
494 // Comparing the hash first actually makes a measurable difference here, at least on x86
495 quint32 hash = property.hash();
496 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
497 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
498 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
499 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
503 QQmlPropertyData local;
504 QQmlPropertyData *result = 0;
506 QQmlData *ddata = QQmlData::get(object, false);
507 if (ddata && ddata->propertyCache)
508 result = ddata->propertyCache->property(property);
510 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
514 return v8::Handle<v8::Value>();
516 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
517 QQmlData *ddata = QQmlData::get(object);
518 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
519 return v8::Handle<v8::Value>();
522 if (result->isFunction() && !result->isVMEProperty()) {
523 if (result->isVMEFunction()) {
524 return ((QQmlVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
525 } else if (result->isV8Function()) {
526 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
527 } else if (result->isSignalHandler()) {
528 v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance();
529 QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex);
530 handler->SetExternalResource(r);
533 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
537 QQmlEnginePrivate *ep =
538 engine->engine()?QQmlEnginePrivate::get(engine->engine()):0;
540 if (result->hasAccessors()) {
542 QQmlNotifier **nptr = 0;
544 if (ep && ep->propertyCapture && result->accessors->notifier)
547 v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr);
549 if (result->accessors->notifier) {
550 if (n) ep->captureProperty(n);
552 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
558 if (ep && !result->isConstant()) {
560 if (result->coreIndex == 0)
561 ep->captureProperty(QQmlData::get(object, true)->objectNameNotifier());
563 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
566 if (result->isVMEProperty()) {
567 typedef QQmlVMEMetaObject VMEMO;
568 VMEMO *vmemo = const_cast<VMEMO *>(static_cast<const VMEMO *>(object->metaObject()));
569 return vmemo->vmeProperty(result->coreIndex);
570 } else if (result->isDirect()) {
571 return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0);
573 return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0);
577 // Setter for writable properties. Shared between the interceptor and fast property accessor
578 static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property,
579 v8::Handle<v8::Value> value)
581 QQmlBinding *newBinding = 0;
582 if (value->IsFunction()) {
583 if (value->ToObject()->GetHiddenValue(engine->bindingFlagKey()).IsEmpty()) {
584 if (!property->isVMEProperty()) {
585 // assigning a JS function to a non-var-property is not allowed.
586 QString error = QLatin1String("Cannot assign JavaScript function to ") +
587 QLatin1String(QMetaType::typeName(property->propType));
588 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
592 // binding assignment.
593 QQmlContextData *context = engine->callingContext();
594 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
596 v8::Local<v8::StackTrace> trace =
597 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
598 v8::StackTrace::kScriptName));
599 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
600 int lineNumber = frame->GetLineNumber();
601 int columNumber = frame->GetColumn();
602 QString url = engine->toString(frame->GetScriptName());
604 newBinding = new QQmlBinding(&function, object, context);
605 newBinding->setSourceLocation(url, lineNumber, columNumber);
606 newBinding->setTarget(object, *property, context);
607 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
608 QQmlBinding::RequiresThisObject);
612 QQmlAbstractBinding *oldBinding =
613 QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
615 oldBinding->destroy();
617 if (!newBinding && property->isVMEProperty()) {
618 // allow assignment of "special" values (null, undefined, function) to var properties
619 static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
623 #define PROPERTY_STORE(cpptype, value) \
627 void *argv[] = { &o, 0, &status, &flags }; \
628 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
631 if (value->IsNull() && property->isQObject()) {
632 PROPERTY_STORE(QObject*, 0);
633 } else if (value->IsUndefined() && property->isResettable()) {
635 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
636 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
637 PROPERTY_STORE(QVariant, QVariant());
638 } else if (value->IsUndefined()) {
639 QString error = QLatin1String("Cannot assign [undefined] to ") +
640 QLatin1String(QMetaType::typeName(property->propType));
641 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
642 } else if (value->IsFunction()) {
643 // this is handled by the binding creation above
644 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
645 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
646 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
647 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
648 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
649 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
650 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
651 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
652 } else if (property->propType == QMetaType::QString && value->IsString()) {
653 PROPERTY_STORE(QString, engine->toString(value->ToString()));
654 } else if (property->isVMEProperty()) {
655 static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
658 if (property->isQList())
659 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
661 v = engine->toVariant(value, property->propType);
663 QQmlContextData *context = engine->callingContext();
664 if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
665 const char *valueType = 0;
666 if (v.userType() == QVariant::Invalid) valueType = "null";
667 else valueType = QMetaType::typeName(v.userType());
669 QString error = QLatin1String("Cannot assign ") +
670 QLatin1String(valueType) +
671 QLatin1String(" to ") +
672 QLatin1String(QMetaType::typeName(property->propType));
673 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
678 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
679 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
681 if (engine->qobjectWrapper()->m_toStringString == property ||
682 engine->qobjectWrapper()->m_destroyString == property)
685 QQmlPropertyData local;
686 QQmlPropertyData *result = 0;
687 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
692 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
693 QQmlData *ddata = QQmlData::get(object);
694 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
698 if (!result->isWritable() && !result->isQList()) {
699 QString error = QLatin1String("Cannot assign to read-only property \"") +
700 engine->toString(property.string()) + QLatin1Char('\"');
701 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
705 StoreProperty(engine, object, result, value);
710 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
711 const v8::AccessorInfo &info)
713 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
715 if (resource->object.isNull())
716 return v8::Handle<v8::Value>();
718 QObject *object = resource->object;
720 QHashedV8String propertystring(property);
722 QV8Engine *v8engine = resource->engine;
723 v8::Handle<v8::Value> This = info.This();
724 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
725 QV8QObjectWrapper::IgnoreRevision);
726 if (!result.IsEmpty())
729 if (QV8Engine::startsWithUpper(property)) {
730 // Check for attached properties
731 QQmlContextData *context = v8engine->callingContext();
733 if (context && context->imports) {
734 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
737 if (r.scriptIndex != -1) {
738 return v8::Undefined();
740 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
741 } else if (r.importNamespace) {
742 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
743 QV8TypeWrapper::ExcludeEnums);
745 Q_ASSERT(!"Unreachable");
750 return v8::Handle<v8::Value>();
753 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
754 v8::Local<v8::Value> value,
755 const v8::AccessorInfo &info)
757 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
759 if (resource->object.isNull())
762 QObject *object = resource->object;
764 QHashedV8String propertystring(property);
766 QV8Engine *v8engine = resource->engine;
767 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
770 QString error = QLatin1String("Cannot assign to non-existent property \"") +
771 v8engine->toString(property) + QLatin1Char('\"');
772 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
779 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
780 const v8::AccessorInfo &info)
782 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
784 if (resource->object.isNull())
785 return v8::Handle<v8::Integer>();
787 QV8Engine *engine = resource->engine;
788 QObject *object = resource->object;
790 QHashedV8String propertystring(property);
792 QQmlPropertyData local;
793 QQmlPropertyData *result = 0;
794 result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local);
797 return v8::Handle<v8::Integer>();
798 else if (!result->isWritable() && !result->isQList())
799 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
801 return v8::Integer::New(v8::DontDelete);
804 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
806 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
808 if (resource->object.isNull())
809 return v8::Array::New();
811 QObject *object = resource->object;
815 QQmlEnginePrivate *ep = resource->engine->engine()
816 ? QQmlEnginePrivate::get(resource->engine->engine())
819 QQmlPropertyCache *cache = 0;
820 QQmlData *ddata = QQmlData::get(object);
822 cache = ddata->propertyCache;
825 cache = ep ? ep->cache(object) : 0;
827 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
829 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
830 const QMetaObject *mo = object->metaObject();
831 int pc = mo->propertyCount();
832 int po = mo->propertyOffset();
833 for (int i=po; i<pc; ++i)
834 result << QString::fromUtf8(mo->property(i).name());
837 result = cache->propertyNames();
840 v8::Local<v8::Array> rv = v8::Array::New(result.count());
842 for (int ii = 0; ii < result.count(); ++ii)
843 rv->Set(ii, resource->engine->toString(result.at(ii)));
848 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
849 const v8::AccessorInfo& info)
851 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
853 if (resource->object.isNull())
856 QObject *object = resource->object;
858 QQmlPropertyData *property =
859 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
861 int index = property->coreIndex;
863 QQmlData *ddata = QQmlData::get(object, false);
865 Q_ASSERT(ddata->propertyCache);
867 QQmlPropertyData *pdata = ddata->propertyCache->property(index);
870 Q_ASSERT(pdata->isWritable() || pdata->isQList());
872 StoreProperty(resource->engine, object, pdata, value);
875 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
876 const v8::AccessorInfo& info)
878 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
880 if (resource->object.isNull())
883 QV8Engine *v8engine = resource->engine;
885 QString error = QLatin1String("Cannot assign to read-only property \"") +
886 v8engine->toString(property) + QLatin1Char('\"');
887 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
890 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
892 Q_ASSERT(handle->IsObject());
894 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
898 QObject *object = resource->object;
900 QQmlData *ddata = QQmlData::get(object, false);
902 ddata->v8object.Clear();
903 if (!object->parent() && !ddata->indestructible)
904 object->deleteLater();
908 qPersistentDispose(handle);
911 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
913 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
914 instance->v8object.Clear();
915 qPersistentDispose(handle);
918 v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
921 Q_ASSERT(this->engine);
923 Q_ASSERT(QQmlData::get(object, false));
924 Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
927 if (constructor.IsEmpty()) {
928 v8::Local<v8::FunctionTemplate> ft;
930 QString toString = QLatin1String("toString");
931 QString destroy = QLatin1String("destroy");
933 // As we use hash linking, it is possible that iterating over the values can give duplicates.
934 // To combat this, we must unique'ify our properties.
935 StringCache uniqueHash;
936 if (stringCache.isLinked())
937 uniqueHash.reserve(stringCache.count());
939 // XXX TODO: Enables fast property accessors. These more than double the property access
940 // performance, but the cost of setting up this structure hasn't been measured so
941 // its not guarenteed that this is a win overall. We need to try and measure the cost.
942 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
943 if (stringCache.isLinked()) {
944 if (uniqueHash.contains(iter))
946 uniqueHash.insert(iter);
949 QQmlPropertyData *property = *iter;
950 if (property->notFullyResolved()) resolve(property);
952 if (property->isFunction())
955 v8::AccessorGetter fastgetter = 0;
956 v8::AccessorSetter fastsetter = FastValueSetter;
957 if (!property->isWritable())
958 fastsetter = FastValueSetterReadOnly;
960 if (property->isQObject())
961 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
962 else if (property->propType == QMetaType::Int || property->isEnum())
963 fastgetter = FAST_GETTER_FUNCTION(property, int);
964 else if (property->propType == QMetaType::Bool)
965 fastgetter = FAST_GETTER_FUNCTION(property, bool);
966 else if (property->propType == QMetaType::QString)
967 fastgetter = FAST_GETTER_FUNCTION(property, QString);
968 else if (property->propType == QMetaType::UInt)
969 fastgetter = FAST_GETTER_FUNCTION(property, uint);
970 else if (property->propType == QMetaType::Float)
971 fastgetter = FAST_GETTER_FUNCTION(property, float);
972 else if (property->propType == QMetaType::Double)
973 fastgetter = FAST_GETTER_FUNCTION(property, double);
976 QString name = iter.key();
977 if (name == toString || name == destroy)
981 ft = v8::FunctionTemplate::New();
982 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
983 QV8QObjectWrapper::Setter,
984 QV8QObjectWrapper::Query,
986 QV8QObjectWrapper::Enumerator);
987 ft->InstanceTemplate()->SetHasExternalResource(true);
990 // We wrap the raw QQmlPropertyData pointer here. This is safe as the
991 // pointer will remain valid at least as long as the lifetime of any QObject's of
992 // this type and the property accessor checks if the object is 0 (deleted) before
993 // dereferencing the pointer.
994 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
995 v8::External::Wrap(property));
1000 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
1002 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
1003 QV8QObjectWrapper::Setter,
1004 QV8QObjectWrapper::Query,
1006 QV8QObjectWrapper::Enumerator);
1007 ft->InstanceTemplate()->SetHasExternalResource(true);
1008 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
1011 QQmlCleanup::addToEngine(this->engine);
1014 v8::Local<v8::Object> result = constructor->NewInstance();
1015 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1016 result->SetExternalResource(r);
1020 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
1022 v8::Local<v8::Object> rv;
1024 if (!ddata->propertyCache && engine->engine()) {
1025 ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
1026 if (ddata->propertyCache) ddata->propertyCache->addref();
1029 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1030 rv = ddata->propertyCache->newQObject(object, engine);
1032 // XXX NewInstance() should be optimized
1033 rv = m_constructor->NewInstance();
1034 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1035 rv->SetExternalResource(r);
1042 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1043 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
1045 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
1046 persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that
1047 "owns" this handle by setting the QQmlData::v8objectid to the id of this
1049 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create
1050 an entry in the m_taintedObject hash where we store the handle and mark the object as
1051 "tainted" in the QQmlData::hasTaintedV8Object flag.
1052 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1053 in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has
1054 released the handle.
1056 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1061 if (QObjectPrivate::get(object)->wasDeleted)
1062 return v8::Undefined();
1064 QQmlData *ddata = QQmlData::get(object, true);
1067 return v8::Undefined();
1069 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1070 // We own the v8object
1071 return v8::Local<v8::Object>::New(ddata->v8object);
1072 } else if (ddata->v8object.IsEmpty() &&
1073 (ddata->v8objectid == m_id || // We own the QObject
1074 ddata->v8objectid == 0 || // No one owns the QObject
1075 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1077 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1078 ddata->v8object = qPersistentNew<v8::Object>(rv);
1079 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1080 ddata->v8objectid = m_id;
1084 // If this object is tainted, we have to check to see if it is in our
1085 // tainted object list
1086 TaintedHash::Iterator iter =
1087 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1088 bool found = iter != m_taintedObjects.end();
1090 // If our tainted handle doesn't exist or has been collected, and there isn't
1091 // a handle in the ddata, we can assume ownership of the ddata->v8object
1092 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1093 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1094 ddata->v8object = qPersistentNew<v8::Object>(rv);
1095 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1096 ddata->v8objectid = m_id;
1100 m_taintedObjects.erase(iter);
1104 } else if (!found) {
1105 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1106 iter = m_taintedObjects.insert(object, instance);
1107 ddata->hasTaintedV8Object = true;
1110 if ((*iter)->v8object.IsEmpty()) {
1111 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1112 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1113 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1116 return v8::Local<v8::Object>::New((*iter)->v8object);
1120 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1122 if (object->IsFunction())
1123 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1125 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1126 return qMakePair(resource->object.data(), resource->index);
1128 return qMakePair((QObject *)0, -1);
1131 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1133 v8::ScriptOrigin origin = function->GetScriptOrigin();
1134 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1136 // This is one of our special QObject method wrappers
1137 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1138 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1140 if (data->IsArray()) {
1141 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1142 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1145 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1148 return qMakePair((QObject *)0, -1);
1151 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1154 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1155 ~QV8QObjectConnectionList();
1159 : needsDestroy(false) {}
1160 Connection(const Connection &other)
1161 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1162 Connection &operator=(const Connection &other) {
1163 thisObject = other.thisObject;
1164 function = other.function;
1165 needsDestroy = other.needsDestroy;
1169 v8::Persistent<v8::Object> thisObject;
1170 v8::Persistent<v8::Function> function;
1173 qPersistentDispose(thisObject);
1174 qPersistentDispose(function);
1180 struct ConnectionList : public QList<Connection> {
1181 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1182 int connectionsInUse;
1183 bool connectionsNeedClean;
1188 typedef QHash<int, ConnectionList> SlotHash;
1193 virtual void objectDestroyed(QObject *);
1194 virtual int qt_metacall(QMetaObject::Call, int, void **);
1197 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1198 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1202 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1204 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1205 QList<Connection> &connections = *iter;
1206 for (int ii = 0; ii < connections.count(); ++ii) {
1207 qPersistentDispose(connections[ii].thisObject);
1208 qPersistentDispose(connections[ii].function);
1214 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1216 engine->qobjectWrapper()->m_connections.remove(object);
1219 needsDestroy = true;
1224 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1226 if (method == QMetaObject::InvokeMetaMethod) {
1227 SlotHash::Iterator iter = slotHash.find(index);
1228 if (iter == slotHash.end())
1230 ConnectionList &connectionList = *iter;
1231 if (connectionList.isEmpty())
1236 connectionList.connectionsInUse++;
1238 QList<Connection> connections = connectionList;
1240 QVarLengthArray<int, 9> dummy;
1241 int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1243 v8::HandleScope handle_scope;
1244 v8::Context::Scope scope(engine->context());
1246 int argCount = argsTypes?argsTypes[0]:0;
1247 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1249 for (int ii = 0; ii < argCount; ++ii) {
1250 int type = argsTypes[ii + 1];
1251 if (type == qMetaTypeId<QVariant>()) {
1252 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1254 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1258 for (int ii = 0; ii < connections.count(); ++ii) {
1259 Connection &connection = connections[ii];
1260 if (connection.needsDestroy)
1263 v8::TryCatch try_catch;
1264 if (connection.thisObject.IsEmpty()) {
1265 connection.function->Call(engine->global(), argCount, args.data());
1267 connection.function->Call(connection.thisObject, argCount, args.data());
1270 if (try_catch.HasCaught()) {
1272 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1273 v8::Local<v8::Message> message = try_catch.Message();
1274 if (!message.IsEmpty())
1275 QQmlExpressionPrivate::exceptionToError(message, error);
1276 QQmlEnginePrivate::get(engine->engine())->warning(error);
1280 connectionList.connectionsInUse--;
1281 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1282 for (QList<Connection>::Iterator iter = connectionList.begin();
1283 iter != connectionList.end(); ) {
1284 if (iter->needsDestroy) {
1286 iter = connectionList.erase(iter);
1294 if (inUse == 0 && needsDestroy)
1301 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1303 if (args.Length() == 0)
1304 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1306 QV8Engine *engine = V8ENGINE();
1308 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1309 QObject *signalObject = signalInfo.first;
1310 int signalIndex = signalInfo.second;
1312 if (signalIndex == -1)
1313 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1316 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1318 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1319 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1321 v8::Local<v8::Value> functionValue;
1322 v8::Local<v8::Value> functionThisValue;
1324 if (args.Length() == 1) {
1325 functionValue = args[0];
1327 functionThisValue = args[0];
1328 functionValue = args[1];
1331 if (!functionValue->IsFunction())
1332 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1334 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1335 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1337 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1338 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1339 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1340 if (iter == connections.end())
1341 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1343 QV8QObjectConnectionList *connectionList = *iter;
1344 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1345 if (slotIter == connectionList->slotHash.end()) {
1346 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1347 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1350 QV8QObjectConnectionList::Connection connection;
1351 if (!functionThisValue.IsEmpty())
1352 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1353 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1355 slotIter->append(connection);
1357 return v8::Undefined();
1360 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1362 if (args.Length() == 0)
1363 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1365 QV8Engine *engine = V8ENGINE();
1367 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1368 QObject *signalObject = signalInfo.first;
1369 int signalIndex = signalInfo.second;
1371 if (signalIndex == -1)
1372 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1375 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1377 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1378 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1380 v8::Local<v8::Value> functionValue;
1381 v8::Local<v8::Value> functionThisValue;
1383 if (args.Length() == 1) {
1384 functionValue = args[0];
1386 functionThisValue = args[0];
1387 functionValue = args[1];
1390 if (!functionValue->IsFunction())
1391 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1393 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1394 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1396 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1397 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1398 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1399 if (iter == connectionsList.end())
1400 return v8::Undefined(); // Nothing to disconnect from
1402 QV8QObjectConnectionList *connectionList = *iter;
1403 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1404 if (slotIter == connectionList->slotHash.end())
1405 return v8::Undefined(); // Nothing to disconnect from
1407 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1409 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1410 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1412 if (functionData.second != -1) {
1413 // This is a QObject function wrapper
1414 for (int ii = 0; ii < connections.count(); ++ii) {
1415 QV8QObjectConnectionList::Connection &connection = connections[ii];
1417 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1418 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1420 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1421 if (connectedFunctionData == functionData) {
1423 if (connections.connectionsInUse) {
1424 connection.needsDestroy = true;
1425 connections.connectionsNeedClean = true;
1427 connection.dispose();
1428 connections.removeAt(ii);
1430 return v8::Undefined();
1436 // This is a normal JS function
1437 for (int ii = 0; ii < connections.count(); ++ii) {
1438 QV8QObjectConnectionList::Connection &connection = connections[ii];
1439 if (connection.function->StrictEquals(function) &&
1440 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1441 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1443 if (connections.connectionsInUse) {
1444 connection.needsDestroy = true;
1445 connections.connectionsNeedClean = true;
1447 connection.dispose();
1448 connections.removeAt(ii);
1450 return v8::Undefined();
1455 return v8::Undefined();
1459 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1461 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1463 Only searches for real properties of \a object (including methods), not attached properties etc.
1467 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1469 Set the \a property of \a object to \a value.
1471 Returns true if the property was "set" - even if this results in an exception being thrown -
1472 and false if the object has no such property.
1474 Only searches for real properties of \a object (including methods), not attached properties etc.
1480 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1481 int Length() const { return _length; }
1482 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1486 v8::Handle<v8::Object> *_args;
1490 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1491 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1495 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1496 args[0].initAsType(returnType);
1498 for (int ii = 0; ii < argCount; ++ii)
1499 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1501 QVarLengthArray<void *, 9> argData(args.count());
1502 for (int ii = 0; ii < args.count(); ++ii)
1503 argData[ii] = args[ii].dataPtr();
1505 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1507 return args[0].toValue(engine);
1509 } else if (returnType != 0) {
1512 arg.initAsType(returnType);
1514 void *args[] = { arg.dataPtr() };
1516 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1518 return arg.toValue(engine);
1522 void *args[] = { 0 };
1523 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1524 return v8::Undefined();
1530 Returns the match score for converting \a actual to be of type \a conversionType. A
1531 zero score means "perfect match" whereas a higher score is worse.
1533 The conversion table is copied out of the QtScript callQtMethod() function.
1535 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1537 if (actual->IsNumber()) {
1538 switch (conversionType) {
1539 case QMetaType::Double:
1541 case QMetaType::Float:
1543 case QMetaType::LongLong:
1544 case QMetaType::ULongLong:
1546 case QMetaType::Long:
1547 case QMetaType::ULong:
1549 case QMetaType::Int:
1550 case QMetaType::UInt:
1552 case QMetaType::Short:
1553 case QMetaType::UShort:
1556 case QMetaType::Char:
1557 case QMetaType::UChar:
1562 } else if (actual->IsString()) {
1563 switch (conversionType) {
1564 case QMetaType::QString:
1569 } else if (actual->IsBoolean()) {
1570 switch (conversionType) {
1571 case QMetaType::Bool:
1576 } else if (actual->IsDate()) {
1577 switch (conversionType) {
1578 case QMetaType::QDateTime:
1580 case QMetaType::QDate:
1582 case QMetaType::QTime:
1587 } else if (actual->IsRegExp()) {
1588 switch (conversionType) {
1589 case QMetaType::QRegExp:
1594 } else if (actual->IsArray()) {
1595 switch (conversionType) {
1596 case QMetaType::QStringList:
1597 case QMetaType::QVariantList:
1602 } else if (actual->IsNull()) {
1603 switch (conversionType) {
1604 case QMetaType::VoidStar:
1605 case QMetaType::QObjectStar:
1608 const char *typeName = QMetaType::typeName(conversionType);
1609 if (typeName && typeName[strlen(typeName) - 1] == '*')
1615 } else if (actual->IsObject()) {
1616 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1618 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1619 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1620 switch (conversionType) {
1621 case QMetaType::QObjectStar:
1626 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1627 if (conversionType == qMetaTypeId<QVariant>())
1629 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1642 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1648 int classInfoCount, classInfoData;
1649 int methodCount, methodData;
1652 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1655 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1657 QByteArray sig = m.signature();
1658 int paren = sig.indexOf('(');
1662 return sig.left(paren);
1666 Returns the next related method, if one, or 0.
1668 static const QQmlPropertyData * RelatedMethod(QObject *object,
1669 const QQmlPropertyData *current,
1670 QQmlPropertyData &dummy)
1672 QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1673 if (!current->isOverload())
1676 Q_ASSERT(!current->overrideIndexIsProperty);
1679 return cache->method(current->overrideIndex);
1681 const QMetaObject *mo = object->metaObject();
1682 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1684 while (methodOffset > current->overrideIndex) {
1685 mo = mo->superClass();
1686 methodOffset -= QMetaObject_methods(mo);
1689 QMetaMethod method = mo->method(current->overrideIndex);
1692 // Look for overloaded methods
1693 QByteArray methodName = QMetaMethod_name(method);
1694 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1695 if (methodName == QMetaMethod_name(mo->method(ii))) {
1696 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1697 dummy.overrideIndexIsProperty = 0;
1698 dummy.overrideIndex = ii;
1707 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1708 QV8Engine *engine, CallArgs &callArgs)
1710 if (data.hasArguments()) {
1713 QVarLengthArray<int, 9> dummy;
1714 QByteArray unknownTypeError;
1716 args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1720 QString typeName = QString::fromLatin1(unknownTypeError);
1721 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1722 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1723 return v8::Handle<v8::Value>();
1726 if (args[0] > callArgs.Length()) {
1727 QString error = QLatin1String("Insufficient arguments");
1728 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1729 return v8::Handle<v8::Value>();
1732 return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1736 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1742 Resolve the overloaded method to call. The algorithm works conceptually like this:
1743 1. Resolve the set of overloads it is *possible* to call.
1744 Impossible overloads include those that have too many parameters or have parameters
1746 2. Filter the set of overloads to only contain those with the closest number of
1748 For example, if we are called with 3 parameters and there are 2 overloads that
1749 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1750 3. Find the best remaining overload based on its match score.
1751 If two or more overloads have the same match score, call the last one. The match
1752 score is constructed by adding the matchScore() result for each of the parameters.
1754 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1755 QV8Engine *engine, CallArgs &callArgs)
1757 int argumentCount = callArgs.Length();
1759 const QQmlPropertyData *best = 0;
1760 int bestParameterScore = INT_MAX;
1761 int bestMatchScore = INT_MAX;
1763 QQmlPropertyData dummy;
1764 const QQmlPropertyData *attempt = &data;
1767 QVarLengthArray<int, 9> dummy;
1768 int methodArgumentCount = 0;
1769 int *methodArgTypes = 0;
1770 if (attempt->hasArguments()) {
1771 typedef QQmlPropertyCache PC;
1772 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1773 if (!args) // Must be an unknown argument
1776 methodArgumentCount = args[0];
1777 methodArgTypes = args + 1;
1780 if (methodArgumentCount > argumentCount)
1781 continue; // We don't have sufficient arguments to call this method
1783 int methodParameterScore = argumentCount - methodArgumentCount;
1784 if (methodParameterScore > bestParameterScore)
1785 continue; // We already have a better option
1787 int methodMatchScore = 0;
1788 for (int ii = 0; ii < methodArgumentCount; ++ii)
1789 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1791 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1793 bestParameterScore = methodParameterScore;
1794 bestMatchScore = methodMatchScore;
1797 if (bestParameterScore == 0 && bestMatchScore == 0)
1798 break; // We can't get better than that
1800 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1803 return CallPrecise(object, *best, engine, callArgs);
1805 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1806 const QQmlPropertyData *candidate = &data;
1808 error += QLatin1String("\n ") +
1809 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1810 candidate = RelatedMethod(object, candidate, dummy);
1813 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1814 return v8::Handle<v8::Value>();
1818 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1822 QString objectName = object->objectName();
1824 result += QString::fromUtf8(object->metaObject()->className());
1825 result += QLatin1String("(0x");
1826 result += QString::number((quintptr)object,16);
1828 if (!objectName.isEmpty()) {
1829 result += QLatin1String(", \"");
1830 result += objectName;
1831 result += QLatin1Char('\"');
1834 result += QLatin1Char(')');
1836 result = QLatin1String("null");
1839 return engine->toString(result);
1842 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1844 QQmlData *ddata = QQmlData::get(object, false);
1845 if (!ddata || ddata->indestructible) {
1846 const char *error = "Invalid attempt to destroy() an indestructible object";
1847 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1848 return v8::Undefined();
1853 delay = args->Get(0)->Uint32Value();
1856 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1858 object->deleteLater();
1860 return v8::Undefined();
1863 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1865 // object, index, qmlglobal, argCount, args
1866 Q_ASSERT(args.Length() == 5);
1867 Q_ASSERT(args[0]->IsObject());
1869 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1872 return v8::Undefined();
1874 int argCount = args[3]->Int32Value();
1875 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1877 // Special hack to return info about this closure.
1878 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1879 v8::Local<v8::Array> data = v8::Array::New(2);
1880 data->Set(0, args[0]);
1881 data->Set(1, args[1]);
1885 QObject *object = resource->object;
1886 int index = args[1]->Int32Value();
1889 return v8::Undefined();
1892 // Builtin functions
1893 if (index == QOBJECT_TOSTRING_INDEX) {
1894 return ToString(resource->engine, object, argCount, arguments);
1895 } else if (index == QOBJECT_DESTROY_INDEX) {
1896 return Destroy(resource->engine, object, argCount, arguments);
1898 return v8::Undefined();
1902 QQmlPropertyData method;
1904 if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
1905 if (ddata->propertyCache) {
1906 QQmlPropertyData *d = ddata->propertyCache->method(index);
1908 return v8::Undefined();
1913 if (method.coreIndex == -1) {
1914 method.load(object->metaObject()->method(index));
1916 if (method.coreIndex == -1)
1917 return v8::Undefined();
1920 if (method.isV8Function()) {
1921 v8::Handle<v8::Value> rv;
1922 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1924 QQmlV8Function func(argCount, arguments, rv, qmlglobal,
1925 resource->engine->contextWrapper()->context(qmlglobal),
1927 QQmlV8Function *funcptr = &func;
1929 void *args[] = { 0, &funcptr };
1930 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1932 if (rv.IsEmpty()) return v8::Undefined();
1936 CallArgs callArgs(argCount, &arguments);
1937 if (!method.isOverload()) {
1938 return CallPrecise(object, method, resource->engine, callArgs);
1940 return CallOverloaded(object, method, resource->engine, callArgs);
1944 CallArgument::CallArgument()
1945 : type(QVariant::Invalid)
1949 CallArgument::~CallArgument()
1954 void CallArgument::cleanup()
1956 if (type == QMetaType::QString) {
1957 qstringPtr->~QString();
1958 } else if (type == -1 || type == QMetaType::QVariant) {
1959 qvariantPtr->~QVariant();
1960 } else if (type == qMetaTypeId<QJSValue>()) {
1961 qjsValuePtr->~QJSValue();
1962 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1963 qlistPtr->~QList<QObject *>();
1967 void *CallArgument::dataPtr()
1970 return qvariantPtr->data();
1972 return (void *)&allocData;
1975 void CallArgument::initAsType(int callType)
1977 if (type != 0) { cleanup(); type = 0; }
1978 if (callType == 0) return;
1980 if (callType == qMetaTypeId<QJSValue>()) {
1981 qjsValuePtr = new (&allocData) QJSValue();
1983 } else if (callType == QMetaType::Int ||
1984 callType == QMetaType::UInt ||
1985 callType == QMetaType::Bool ||
1986 callType == QMetaType::Double ||
1987 callType == QMetaType::Float) {
1989 } else if (callType == QMetaType::QObjectStar) {
1992 } else if (callType == QMetaType::QString) {
1993 qstringPtr = new (&allocData) QString();
1995 } else if (callType == QMetaType::QVariant) {
1997 qvariantPtr = new (&allocData) QVariant();
1998 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
2000 qlistPtr = new (&allocData) QList<QObject *>();
2001 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2003 handlePtr = new (&allocData) QQmlV8Handle;
2006 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
2010 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
2012 if (type != 0) { cleanup(); type = 0; }
2014 if (callType == qMetaTypeId<QJSValue>()) {
2015 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2016 type = qMetaTypeId<QJSValue>();
2017 } else if (callType == QMetaType::Int) {
2018 intValue = quint32(value->Int32Value());
2020 } else if (callType == QMetaType::UInt) {
2021 intValue = quint32(value->Uint32Value());
2023 } else if (callType == QMetaType::Bool) {
2024 boolValue = value->BooleanValue();
2026 } else if (callType == QMetaType::Double) {
2027 doubleValue = double(value->NumberValue());
2029 } else if (callType == QMetaType::Float) {
2030 floatValue = float(value->NumberValue());
2032 } else if (callType == QMetaType::QString) {
2033 if (value->IsNull() || value->IsUndefined())
2034 qstringPtr = new (&allocData) QString();
2036 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2038 } else if (callType == QMetaType::QObjectStar) {
2039 qobjectPtr = engine->toQObject(value);
2041 } else if (callType == qMetaTypeId<QVariant>()) {
2042 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2044 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2045 qlistPtr = new (&allocData) QList<QObject *>();
2046 if (value->IsArray()) {
2047 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2048 uint32_t length = array->Length();
2049 for (uint32_t ii = 0; ii < length; ++ii)
2050 qlistPtr->append(engine->toQObject(array->Get(ii)));
2052 qlistPtr->append(engine->toQObject(value));
2055 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2056 handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2059 qvariantPtr = new (&allocData) QVariant();
2062 QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2063 QVariant v = engine->toVariant(value, -1);
2065 if (v.userType() == callType) {
2067 } else if (v.canConvert((QVariant::Type)callType)) {
2069 qvariantPtr->convert((QVariant::Type)callType);
2070 } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
2071 QObject *obj = ep->toQObject(v);
2074 const QMetaObject *objMo = obj->metaObject();
2075 while (objMo && objMo != mo) objMo = objMo->superClass();
2076 if (!objMo) obj = 0;
2079 *qvariantPtr = QVariant(callType, &obj);
2081 *qvariantPtr = QVariant(callType, (void *)0);
2086 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2088 if (type == qMetaTypeId<QJSValue>()) {
2089 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2090 } else if (type == QMetaType::Int) {
2091 return v8::Integer::New(int(intValue));
2092 } else if (type == QMetaType::UInt) {
2093 return v8::Integer::NewFromUnsigned(intValue);
2094 } else if (type == QMetaType::Bool) {
2095 return v8::Boolean::New(boolValue);
2096 } else if (type == QMetaType::Double) {
2097 return v8::Number::New(doubleValue);
2098 } else if (type == QMetaType::Float) {
2099 return v8::Number::New(floatValue);
2100 } else if (type == QMetaType::QString) {
2101 return engine->toString(*qstringPtr);
2102 } else if (type == QMetaType::QObjectStar) {
2103 QObject *object = qobjectPtr;
2105 QQmlData::get(object, true)->setImplicitDestructible();
2106 return engine->newQObject(object);
2107 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2108 // XXX Can this be made more by using Array as a prototype and implementing
2109 // directly against QList<QObject*>?
2110 QList<QObject *> &list = *qlistPtr;
2111 v8::Local<v8::Array> array = v8::Array::New(list.count());
2112 for (int ii = 0; ii < list.count(); ++ii)
2113 array->Set(ii, engine->newQObject(list.at(ii)));
2115 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2116 return handlePtr->toHandle();
2117 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2118 QVariant value = *qvariantPtr;
2119 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2120 if (QObject *object = engine->toQObject(rv))
2121 QQmlData::get(object, true)->setImplicitDestructible();
2124 return v8::Undefined();