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()) {
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;
583 if (value->IsFunction()) {
584 QQmlContextData *context = engine->callingContext();
585 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
587 v8::Local<v8::StackTrace> trace =
588 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
589 v8::StackTrace::kScriptName));
590 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
591 int lineNumber = frame->GetLineNumber();
592 int columNumber = frame->GetColumn();
593 QString url = engine->toString(frame->GetScriptName());
595 newBinding = new QQmlBinding(&function, object, context);
596 newBinding->setSourceLocation(url, lineNumber, columNumber);
597 newBinding->setTarget(object, *property, context);
598 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
599 QQmlBinding::RequiresThisObject);
602 QQmlAbstractBinding *oldBinding =
603 QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
605 oldBinding->destroy();
607 #define PROPERTY_STORE(cpptype, value) \
611 void *argv[] = { &o, 0, &status, &flags }; \
612 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
615 if (value->IsNull() && property->isQObject()) {
616 PROPERTY_STORE(QObject*, 0);
617 } else if (value->IsUndefined() && property->isResettable()) {
619 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
620 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
621 PROPERTY_STORE(QVariant, QVariant());
622 } else if (value->IsUndefined()) {
623 QString error = QLatin1String("Cannot assign [undefined] to ") +
624 QLatin1String(QMetaType::typeName(property->propType));
625 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
626 } else if (value->IsFunction()) {
627 // this is handled by the binding creation above
628 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
629 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
630 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
631 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
632 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
633 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
634 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
635 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
636 } else if (property->propType == QMetaType::QString && value->IsString()) {
637 PROPERTY_STORE(QString, engine->toString(value->ToString()));
638 } else if (property->isVMEProperty()) {
639 static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value);
642 if (property->isQList())
643 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
645 v = engine->toVariant(value, property->propType);
647 QQmlContextData *context = engine->callingContext();
648 if (!QQmlPropertyPrivate::write(object, *property, v, context)) {
649 const char *valueType = 0;
650 if (v.userType() == QVariant::Invalid) valueType = "null";
651 else valueType = QMetaType::typeName(v.userType());
653 QString error = QLatin1String("Cannot assign ") +
654 QLatin1String(valueType) +
655 QLatin1String(" to ") +
656 QLatin1String(QMetaType::typeName(property->propType));
657 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
662 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
663 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
665 if (engine->qobjectWrapper()->m_toStringString == property ||
666 engine->qobjectWrapper()->m_destroyString == property)
669 QQmlPropertyData local;
670 QQmlPropertyData *result = 0;
671 result = QQmlPropertyCache::property(engine->engine(), object, property, local);
676 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->hasRevision()) {
677 QQmlData *ddata = QQmlData::get(object);
678 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
682 if (!result->isWritable() && !result->isQList()) {
683 QString error = QLatin1String("Cannot assign to read-only property \"") +
684 engine->toString(property.string()) + QLatin1Char('\"');
685 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
689 StoreProperty(engine, object, result, value);
694 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
695 const v8::AccessorInfo &info)
697 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
699 if (resource->object.isNull())
700 return v8::Handle<v8::Value>();
702 QObject *object = resource->object;
704 QHashedV8String propertystring(property);
706 QV8Engine *v8engine = resource->engine;
707 v8::Handle<v8::Value> This = info.This();
708 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
709 QV8QObjectWrapper::IgnoreRevision);
710 if (!result.IsEmpty())
713 if (QV8Engine::startsWithUpper(property)) {
714 // Check for attached properties
715 QQmlContextData *context = v8engine->callingContext();
717 if (context && context->imports) {
718 QQmlTypeNameCache::Result r = context->imports->query(propertystring);
721 if (r.scriptIndex != -1) {
722 return v8::Undefined();
724 return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums);
725 } else if (r.importNamespace) {
726 return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace,
727 QV8TypeWrapper::ExcludeEnums);
729 Q_ASSERT(!"Unreachable");
734 return v8::Handle<v8::Value>();
737 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
738 v8::Local<v8::Value> value,
739 const v8::AccessorInfo &info)
741 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
743 if (resource->object.isNull())
746 QObject *object = resource->object;
748 QHashedV8String propertystring(property);
750 QV8Engine *v8engine = resource->engine;
751 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
754 QString error = QLatin1String("Cannot assign to non-existent property \"") +
755 v8engine->toString(property) + QLatin1Char('\"');
756 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
763 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
764 const v8::AccessorInfo &info)
766 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
768 if (resource->object.isNull())
769 return v8::Handle<v8::Integer>();
771 QV8Engine *engine = resource->engine;
772 QObject *object = resource->object;
774 QHashedV8String propertystring(property);
776 QQmlPropertyData local;
777 QQmlPropertyData *result = 0;
778 result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local);
781 return v8::Handle<v8::Integer>();
782 else if (!result->isWritable() && !result->isQList())
783 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
785 return v8::Integer::New(v8::DontDelete);
788 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
790 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
792 if (resource->object.isNull())
793 return v8::Array::New();
795 QObject *object = resource->object;
799 QQmlEnginePrivate *ep = resource->engine->engine()
800 ? QQmlEnginePrivate::get(resource->engine->engine())
803 QQmlPropertyCache *cache = 0;
804 QQmlData *ddata = QQmlData::get(object);
806 cache = ddata->propertyCache;
809 cache = ep ? ep->cache(object) : 0;
811 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
813 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
814 const QMetaObject *mo = object->metaObject();
815 int pc = mo->propertyCount();
816 int po = mo->propertyOffset();
817 for (int i=po; i<pc; ++i)
818 result << QString::fromUtf8(mo->property(i).name());
821 result = cache->propertyNames();
824 v8::Local<v8::Array> rv = v8::Array::New(result.count());
826 for (int ii = 0; ii < result.count(); ++ii)
827 rv->Set(ii, resource->engine->toString(result.at(ii)));
832 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
833 const v8::AccessorInfo& info)
835 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
837 if (resource->object.isNull())
840 QObject *object = resource->object;
842 QQmlPropertyData *property =
843 (QQmlPropertyData *)v8::External::Unwrap(info.Data());
845 int index = property->coreIndex;
847 QQmlData *ddata = QQmlData::get(object, false);
849 Q_ASSERT(ddata->propertyCache);
851 QQmlPropertyData *pdata = ddata->propertyCache->property(index);
854 Q_ASSERT(pdata->isWritable() || pdata->isQList());
856 StoreProperty(resource->engine, object, pdata, value);
859 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
860 const v8::AccessorInfo& info)
862 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
864 if (resource->object.isNull())
867 QV8Engine *v8engine = resource->engine;
869 QString error = QLatin1String("Cannot assign to read-only property \"") +
870 v8engine->toString(property) + QLatin1Char('\"');
871 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
874 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
876 Q_ASSERT(handle->IsObject());
878 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
882 QObject *object = resource->object;
884 QQmlData *ddata = QQmlData::get(object, false);
886 ddata->v8object.Clear();
887 if (!object->parent() && !ddata->indestructible)
888 object->deleteLater();
892 qPersistentDispose(handle);
895 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
897 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
898 instance->v8object.Clear();
899 qPersistentDispose(handle);
902 v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine)
905 Q_ASSERT(this->engine);
907 Q_ASSERT(QQmlData::get(object, false));
908 Q_ASSERT(QQmlData::get(object, false)->propertyCache == this);
911 if (constructor.IsEmpty()) {
912 v8::Local<v8::FunctionTemplate> ft;
914 QString toString = QLatin1String("toString");
915 QString destroy = QLatin1String("destroy");
917 // As we use hash linking, it is possible that iterating over the values can give duplicates.
918 // To combat this, we must unique'ify our properties.
919 StringCache uniqueHash;
920 if (stringCache.isLinked())
921 uniqueHash.reserve(stringCache.count());
923 // XXX TODO: Enables fast property accessors. These more than double the property access
924 // performance, but the cost of setting up this structure hasn't been measured so
925 // its not guarenteed that this is a win overall. We need to try and measure the cost.
926 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
927 if (stringCache.isLinked()) {
928 if (uniqueHash.contains(iter))
930 uniqueHash.insert(iter);
933 QQmlPropertyData *property = *iter;
934 if (property->notFullyResolved()) resolve(property);
936 if (property->isFunction())
939 v8::AccessorGetter fastgetter = 0;
940 v8::AccessorSetter fastsetter = FastValueSetter;
941 if (!property->isWritable())
942 fastsetter = FastValueSetterReadOnly;
944 if (property->isQObject())
945 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
946 else if (property->propType == QMetaType::Int || property->isEnum())
947 fastgetter = FAST_GETTER_FUNCTION(property, int);
948 else if (property->propType == QMetaType::Bool)
949 fastgetter = FAST_GETTER_FUNCTION(property, bool);
950 else if (property->propType == QMetaType::QString)
951 fastgetter = FAST_GETTER_FUNCTION(property, QString);
952 else if (property->propType == QMetaType::UInt)
953 fastgetter = FAST_GETTER_FUNCTION(property, uint);
954 else if (property->propType == QMetaType::Float)
955 fastgetter = FAST_GETTER_FUNCTION(property, float);
956 else if (property->propType == QMetaType::Double)
957 fastgetter = FAST_GETTER_FUNCTION(property, double);
960 QString name = iter.key();
961 if (name == toString || name == destroy)
965 ft = v8::FunctionTemplate::New();
966 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
967 QV8QObjectWrapper::Setter,
968 QV8QObjectWrapper::Query,
970 QV8QObjectWrapper::Enumerator);
971 ft->InstanceTemplate()->SetHasExternalResource(true);
974 // We wrap the raw QQmlPropertyData pointer here. This is safe as the
975 // pointer will remain valid at least as long as the lifetime of any QObject's of
976 // this type and the property accessor checks if the object is 0 (deleted) before
977 // dereferencing the pointer.
978 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
979 v8::External::Wrap(property));
984 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
986 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
987 QV8QObjectWrapper::Setter,
988 QV8QObjectWrapper::Query,
990 QV8QObjectWrapper::Enumerator);
991 ft->InstanceTemplate()->SetHasExternalResource(true);
992 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
995 QQmlCleanup::addToEngine(this->engine);
998 v8::Local<v8::Object> result = constructor->NewInstance();
999 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1000 result->SetExternalResource(r);
1004 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine)
1006 v8::Local<v8::Object> rv;
1008 if (!ddata->propertyCache && engine->engine()) {
1009 ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object);
1010 if (ddata->propertyCache) ddata->propertyCache->addref();
1013 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1014 rv = ddata->propertyCache->newQObject(object, engine);
1016 // XXX NewInstance() should be optimized
1017 rv = m_constructor->NewInstance();
1018 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1019 rv->SetExternalResource(r);
1026 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1027 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
1029 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
1030 persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that
1031 "owns" this handle by setting the QQmlData::v8objectid to the id of this
1033 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create
1034 an entry in the m_taintedObject hash where we store the handle and mark the object as
1035 "tainted" in the QQmlData::hasTaintedV8Object flag.
1036 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1037 in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has
1038 released the handle.
1040 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1045 if (QObjectPrivate::get(object)->wasDeleted)
1046 return v8::Undefined();
1048 QQmlData *ddata = QQmlData::get(object, true);
1051 return v8::Undefined();
1053 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1054 // We own the v8object
1055 return v8::Local<v8::Object>::New(ddata->v8object);
1056 } else if (ddata->v8object.IsEmpty() &&
1057 (ddata->v8objectid == m_id || // We own the QObject
1058 ddata->v8objectid == 0 || // No one owns the QObject
1059 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1061 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1062 ddata->v8object = qPersistentNew<v8::Object>(rv);
1063 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1064 ddata->v8objectid = m_id;
1068 // If this object is tainted, we have to check to see if it is in our
1069 // tainted object list
1070 TaintedHash::Iterator iter =
1071 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1072 bool found = iter != m_taintedObjects.end();
1074 // If our tainted handle doesn't exist or has been collected, and there isn't
1075 // a handle in the ddata, we can assume ownership of the ddata->v8object
1076 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
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 m_taintedObjects.erase(iter);
1088 } else if (!found) {
1089 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1090 iter = m_taintedObjects.insert(object, instance);
1091 ddata->hasTaintedV8Object = true;
1094 if ((*iter)->v8object.IsEmpty()) {
1095 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1096 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1097 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1100 return v8::Local<v8::Object>::New((*iter)->v8object);
1104 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1106 if (object->IsFunction())
1107 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1109 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1110 return qMakePair(resource->object.data(), resource->index);
1112 return qMakePair((QObject *)0, -1);
1115 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1117 v8::ScriptOrigin origin = function->GetScriptOrigin();
1118 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1120 // This is one of our special QObject method wrappers
1121 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1122 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1124 if (data->IsArray()) {
1125 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1126 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1129 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1132 return qMakePair((QObject *)0, -1);
1135 class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject>
1138 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1139 ~QV8QObjectConnectionList();
1143 : needsDestroy(false) {}
1144 Connection(const Connection &other)
1145 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1146 Connection &operator=(const Connection &other) {
1147 thisObject = other.thisObject;
1148 function = other.function;
1149 needsDestroy = other.needsDestroy;
1153 v8::Persistent<v8::Object> thisObject;
1154 v8::Persistent<v8::Function> function;
1157 qPersistentDispose(thisObject);
1158 qPersistentDispose(function);
1164 struct ConnectionList : public QList<Connection> {
1165 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1166 int connectionsInUse;
1167 bool connectionsNeedClean;
1172 typedef QHash<int, ConnectionList> SlotHash;
1177 virtual void objectDestroyed(QObject *);
1178 virtual int qt_metacall(QMetaObject::Call, int, void **);
1181 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1182 : QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1186 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1188 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1189 QList<Connection> &connections = *iter;
1190 for (int ii = 0; ii < connections.count(); ++ii) {
1191 qPersistentDispose(connections[ii].thisObject);
1192 qPersistentDispose(connections[ii].function);
1198 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1200 engine->qobjectWrapper()->m_connections.remove(object);
1203 needsDestroy = true;
1208 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1210 if (method == QMetaObject::InvokeMetaMethod) {
1211 SlotHash::Iterator iter = slotHash.find(index);
1212 if (iter == slotHash.end())
1214 ConnectionList &connectionList = *iter;
1215 if (connectionList.isEmpty())
1220 connectionList.connectionsInUse++;
1222 QList<Connection> connections = connectionList;
1224 QVarLengthArray<int, 9> dummy;
1225 int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0);
1227 v8::HandleScope handle_scope;
1228 v8::Context::Scope scope(engine->context());
1230 int argCount = argsTypes?argsTypes[0]:0;
1231 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1233 for (int ii = 0; ii < argCount; ++ii) {
1234 int type = argsTypes[ii + 1];
1235 if (type == qMetaTypeId<QVariant>()) {
1236 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1238 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1242 for (int ii = 0; ii < connections.count(); ++ii) {
1243 Connection &connection = connections[ii];
1244 if (connection.needsDestroy)
1247 v8::TryCatch try_catch;
1248 if (connection.thisObject.IsEmpty()) {
1249 connection.function->Call(engine->global(), argCount, args.data());
1251 connection.function->Call(connection.thisObject, argCount, args.data());
1254 if (try_catch.HasCaught()) {
1256 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1257 v8::Local<v8::Message> message = try_catch.Message();
1258 if (!message.IsEmpty())
1259 QQmlExpressionPrivate::exceptionToError(message, error);
1260 QQmlEnginePrivate::get(engine->engine())->warning(error);
1264 connectionList.connectionsInUse--;
1265 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1266 for (QList<Connection>::Iterator iter = connectionList.begin();
1267 iter != connectionList.end(); ) {
1268 if (iter->needsDestroy) {
1270 iter = connectionList.erase(iter);
1278 if (inUse == 0 && needsDestroy)
1285 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1287 if (args.Length() == 0)
1288 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1290 QV8Engine *engine = V8ENGINE();
1292 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1293 QObject *signalObject = signalInfo.first;
1294 int signalIndex = signalInfo.second;
1296 if (signalIndex == -1)
1297 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1300 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1302 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1303 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1305 v8::Local<v8::Value> functionValue;
1306 v8::Local<v8::Value> functionThisValue;
1308 if (args.Length() == 1) {
1309 functionValue = args[0];
1311 functionThisValue = args[0];
1312 functionValue = args[1];
1315 if (!functionValue->IsFunction())
1316 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1318 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1319 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1321 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1322 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1323 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1324 if (iter == connections.end())
1325 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1327 QV8QObjectConnectionList *connectionList = *iter;
1328 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1329 if (slotIter == connectionList->slotHash.end()) {
1330 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1331 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1334 QV8QObjectConnectionList::Connection connection;
1335 if (!functionThisValue.IsEmpty())
1336 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1337 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1339 slotIter->append(connection);
1341 return v8::Undefined();
1344 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1346 if (args.Length() == 0)
1347 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1349 QV8Engine *engine = V8ENGINE();
1351 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1352 QObject *signalObject = signalInfo.first;
1353 int signalIndex = signalInfo.second;
1355 if (signalIndex == -1)
1356 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1359 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1361 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1362 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1364 v8::Local<v8::Value> functionValue;
1365 v8::Local<v8::Value> functionThisValue;
1367 if (args.Length() == 1) {
1368 functionValue = args[0];
1370 functionThisValue = args[0];
1371 functionValue = args[1];
1374 if (!functionValue->IsFunction())
1375 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1377 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1378 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1380 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1381 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1382 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1383 if (iter == connectionsList.end())
1384 return v8::Undefined(); // Nothing to disconnect from
1386 QV8QObjectConnectionList *connectionList = *iter;
1387 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1388 if (slotIter == connectionList->slotHash.end())
1389 return v8::Undefined(); // Nothing to disconnect from
1391 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1393 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1394 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1396 if (functionData.second != -1) {
1397 // This is a QObject function wrapper
1398 for (int ii = 0; ii < connections.count(); ++ii) {
1399 QV8QObjectConnectionList::Connection &connection = connections[ii];
1401 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1402 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1404 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1405 if (connectedFunctionData == functionData) {
1407 if (connections.connectionsInUse) {
1408 connection.needsDestroy = true;
1409 connections.connectionsNeedClean = true;
1411 connection.dispose();
1412 connections.removeAt(ii);
1414 return v8::Undefined();
1420 // This is a normal JS function
1421 for (int ii = 0; ii < connections.count(); ++ii) {
1422 QV8QObjectConnectionList::Connection &connection = connections[ii];
1423 if (connection.function->StrictEquals(function) &&
1424 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1425 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1427 if (connections.connectionsInUse) {
1428 connection.needsDestroy = true;
1429 connections.connectionsNeedClean = true;
1431 connection.dispose();
1432 connections.removeAt(ii);
1434 return v8::Undefined();
1439 return v8::Undefined();
1443 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1445 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1447 Only searches for real properties of \a object (including methods), not attached properties etc.
1451 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1453 Set the \a property of \a object to \a value.
1455 Returns true if the property was "set" - even if this results in an exception being thrown -
1456 and false if the object has no such property.
1458 Only searches for real properties of \a object (including methods), not attached properties etc.
1464 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1465 int Length() const { return _length; }
1466 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1470 v8::Handle<v8::Object> *_args;
1474 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1475 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1479 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1480 args[0].initAsType(returnType);
1482 for (int ii = 0; ii < argCount; ++ii)
1483 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1485 QVarLengthArray<void *, 9> argData(args.count());
1486 for (int ii = 0; ii < args.count(); ++ii)
1487 argData[ii] = args[ii].dataPtr();
1489 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1491 return args[0].toValue(engine);
1493 } else if (returnType != 0) {
1496 arg.initAsType(returnType);
1498 void *args[] = { arg.dataPtr() };
1500 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1502 return arg.toValue(engine);
1506 void *args[] = { 0 };
1507 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1508 return v8::Undefined();
1514 Returns the match score for converting \a actual to be of type \a conversionType. A
1515 zero score means "perfect match" whereas a higher score is worse.
1517 The conversion table is copied out of the QtScript callQtMethod() function.
1519 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1521 if (actual->IsNumber()) {
1522 switch (conversionType) {
1523 case QMetaType::Double:
1525 case QMetaType::Float:
1527 case QMetaType::LongLong:
1528 case QMetaType::ULongLong:
1530 case QMetaType::Long:
1531 case QMetaType::ULong:
1533 case QMetaType::Int:
1534 case QMetaType::UInt:
1536 case QMetaType::Short:
1537 case QMetaType::UShort:
1540 case QMetaType::Char:
1541 case QMetaType::UChar:
1546 } else if (actual->IsString()) {
1547 switch (conversionType) {
1548 case QMetaType::QString:
1553 } else if (actual->IsBoolean()) {
1554 switch (conversionType) {
1555 case QMetaType::Bool:
1560 } else if (actual->IsDate()) {
1561 switch (conversionType) {
1562 case QMetaType::QDateTime:
1564 case QMetaType::QDate:
1566 case QMetaType::QTime:
1571 } else if (actual->IsRegExp()) {
1572 switch (conversionType) {
1573 case QMetaType::QRegExp:
1578 } else if (actual->IsArray()) {
1579 switch (conversionType) {
1580 case QMetaType::QStringList:
1581 case QMetaType::QVariantList:
1586 } else if (actual->IsNull()) {
1587 switch (conversionType) {
1588 case QMetaType::VoidStar:
1589 case QMetaType::QObjectStar:
1592 const char *typeName = QMetaType::typeName(conversionType);
1593 if (typeName && typeName[strlen(typeName) - 1] == '*')
1599 } else if (actual->IsObject()) {
1600 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1602 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1603 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1604 switch (conversionType) {
1605 case QMetaType::QObjectStar:
1610 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1611 if (conversionType == qMetaTypeId<QVariant>())
1613 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1626 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1632 int classInfoCount, classInfoData;
1633 int methodCount, methodData;
1636 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1639 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1641 QByteArray sig = m.signature();
1642 int paren = sig.indexOf('(');
1646 return sig.left(paren);
1650 Returns the next related method, if one, or 0.
1652 static const QQmlPropertyData * RelatedMethod(QObject *object,
1653 const QQmlPropertyData *current,
1654 QQmlPropertyData &dummy)
1656 QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache;
1657 if (!current->isOverload())
1660 Q_ASSERT(!current->overrideIndexIsProperty);
1663 return cache->method(current->overrideIndex);
1665 const QMetaObject *mo = object->metaObject();
1666 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1668 while (methodOffset > current->overrideIndex) {
1669 mo = mo->superClass();
1670 methodOffset -= QMetaObject_methods(mo);
1673 QMetaMethod method = mo->method(current->overrideIndex);
1676 // Look for overloaded methods
1677 QByteArray methodName = QMetaMethod_name(method);
1678 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1679 if (methodName == QMetaMethod_name(mo->method(ii))) {
1680 dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload);
1681 dummy.overrideIndexIsProperty = 0;
1682 dummy.overrideIndex = ii;
1691 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data,
1692 QV8Engine *engine, CallArgs &callArgs)
1694 if (data.hasArguments()) {
1697 QVarLengthArray<int, 9> dummy;
1698 QByteArray unknownTypeError;
1700 args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1704 QString typeName = QString::fromLatin1(unknownTypeError);
1705 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1706 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1707 return v8::Handle<v8::Value>();
1710 if (args[0] > callArgs.Length()) {
1711 QString error = QLatin1String("Insufficient arguments");
1712 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1713 return v8::Handle<v8::Value>();
1716 return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1720 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1726 Resolve the overloaded method to call. The algorithm works conceptually like this:
1727 1. Resolve the set of overloads it is *possible* to call.
1728 Impossible overloads include those that have too many parameters or have parameters
1730 2. Filter the set of overloads to only contain those with the closest number of
1732 For example, if we are called with 3 parameters and there are 2 overloads that
1733 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1734 3. Find the best remaining overload based on its match score.
1735 If two or more overloads have the same match score, call the last one. The match
1736 score is constructed by adding the matchScore() result for each of the parameters.
1738 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data,
1739 QV8Engine *engine, CallArgs &callArgs)
1741 int argumentCount = callArgs.Length();
1743 const QQmlPropertyData *best = 0;
1744 int bestParameterScore = INT_MAX;
1745 int bestMatchScore = INT_MAX;
1747 QQmlPropertyData dummy;
1748 const QQmlPropertyData *attempt = &data;
1751 QVarLengthArray<int, 9> dummy;
1752 int methodArgumentCount = 0;
1753 int *methodArgTypes = 0;
1754 if (attempt->hasArguments()) {
1755 typedef QQmlPropertyCache PC;
1756 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1757 if (!args) // Must be an unknown argument
1760 methodArgumentCount = args[0];
1761 methodArgTypes = args + 1;
1764 if (methodArgumentCount > argumentCount)
1765 continue; // We don't have sufficient arguments to call this method
1767 int methodParameterScore = argumentCount - methodArgumentCount;
1768 if (methodParameterScore > bestParameterScore)
1769 continue; // We already have a better option
1771 int methodMatchScore = 0;
1772 for (int ii = 0; ii < methodArgumentCount; ++ii)
1773 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1775 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1777 bestParameterScore = methodParameterScore;
1778 bestMatchScore = methodMatchScore;
1781 if (bestParameterScore == 0 && bestMatchScore == 0)
1782 break; // We can't get better than that
1784 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1787 return CallPrecise(object, *best, engine, callArgs);
1789 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1790 const QQmlPropertyData *candidate = &data;
1792 error += QLatin1String("\n ") +
1793 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1794 candidate = RelatedMethod(object, candidate, dummy);
1797 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1798 return v8::Handle<v8::Value>();
1802 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1806 QString objectName = object->objectName();
1808 result += QString::fromUtf8(object->metaObject()->className());
1809 result += QLatin1String("(0x");
1810 result += QString::number((quintptr)object,16);
1812 if (!objectName.isEmpty()) {
1813 result += QLatin1String(", \"");
1814 result += objectName;
1815 result += QLatin1Char('\"');
1818 result += QLatin1Char(')');
1820 result = QLatin1String("null");
1823 return engine->toString(result);
1826 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1828 QQmlData *ddata = QQmlData::get(object, false);
1829 if (!ddata || ddata->indestructible) {
1830 const char *error = "Invalid attempt to destroy() an indestructible object";
1831 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1832 return v8::Undefined();
1837 delay = args->Get(0)->Uint32Value();
1840 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1842 object->deleteLater();
1844 return v8::Undefined();
1847 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1849 // object, index, qmlglobal, argCount, args
1850 Q_ASSERT(args.Length() == 5);
1851 Q_ASSERT(args[0]->IsObject());
1853 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1856 return v8::Undefined();
1858 int argCount = args[3]->Int32Value();
1859 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1861 // Special hack to return info about this closure.
1862 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1863 v8::Local<v8::Array> data = v8::Array::New(2);
1864 data->Set(0, args[0]);
1865 data->Set(1, args[1]);
1869 QObject *object = resource->object;
1870 int index = args[1]->Int32Value();
1873 return v8::Undefined();
1876 // Builtin functions
1877 if (index == QOBJECT_TOSTRING_INDEX) {
1878 return ToString(resource->engine, object, argCount, arguments);
1879 } else if (index == QOBJECT_DESTROY_INDEX) {
1880 return Destroy(resource->engine, object, argCount, arguments);
1882 return v8::Undefined();
1886 QQmlPropertyData method;
1888 if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) {
1889 if (ddata->propertyCache) {
1890 QQmlPropertyData *d = ddata->propertyCache->method(index);
1892 return v8::Undefined();
1897 if (method.coreIndex == -1) {
1898 method.load(object->metaObject()->method(index));
1900 if (method.coreIndex == -1)
1901 return v8::Undefined();
1904 if (method.isV8Function()) {
1905 v8::Handle<v8::Value> rv;
1906 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1908 QQmlV8Function func(argCount, arguments, rv, qmlglobal,
1909 resource->engine->contextWrapper()->context(qmlglobal),
1911 QQmlV8Function *funcptr = &func;
1913 void *args[] = { 0, &funcptr };
1914 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1916 if (rv.IsEmpty()) return v8::Undefined();
1920 CallArgs callArgs(argCount, &arguments);
1921 if (!method.isOverload()) {
1922 return CallPrecise(object, method, resource->engine, callArgs);
1924 return CallOverloaded(object, method, resource->engine, callArgs);
1928 CallArgument::CallArgument()
1929 : type(QVariant::Invalid)
1933 CallArgument::~CallArgument()
1938 void CallArgument::cleanup()
1940 if (type == QMetaType::QString) {
1941 qstringPtr->~QString();
1942 } else if (type == -1 || type == QMetaType::QVariant) {
1943 qvariantPtr->~QVariant();
1944 } else if (type == qMetaTypeId<QJSValue>()) {
1945 qjsValuePtr->~QJSValue();
1946 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1947 qlistPtr->~QList<QObject *>();
1951 void *CallArgument::dataPtr()
1954 return qvariantPtr->data();
1956 return (void *)&allocData;
1959 void CallArgument::initAsType(int callType)
1961 if (type != 0) { cleanup(); type = 0; }
1962 if (callType == 0) return;
1964 if (callType == qMetaTypeId<QJSValue>()) {
1965 qjsValuePtr = new (&allocData) QJSValue();
1967 } else if (callType == QMetaType::Int ||
1968 callType == QMetaType::UInt ||
1969 callType == QMetaType::Bool ||
1970 callType == QMetaType::Double ||
1971 callType == QMetaType::Float) {
1973 } else if (callType == QMetaType::QObjectStar) {
1976 } else if (callType == QMetaType::QString) {
1977 qstringPtr = new (&allocData) QString();
1979 } else if (callType == QMetaType::QVariant) {
1981 qvariantPtr = new (&allocData) QVariant();
1982 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1984 qlistPtr = new (&allocData) QList<QObject *>();
1985 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
1987 handlePtr = new (&allocData) QQmlV8Handle;
1990 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
1994 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1996 if (type != 0) { cleanup(); type = 0; }
1998 if (callType == qMetaTypeId<QJSValue>()) {
1999 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
2000 type = qMetaTypeId<QJSValue>();
2001 } else if (callType == QMetaType::Int) {
2002 intValue = quint32(value->Int32Value());
2004 } else if (callType == QMetaType::UInt) {
2005 intValue = quint32(value->Uint32Value());
2007 } else if (callType == QMetaType::Bool) {
2008 boolValue = value->BooleanValue();
2010 } else if (callType == QMetaType::Double) {
2011 doubleValue = double(value->NumberValue());
2013 } else if (callType == QMetaType::Float) {
2014 floatValue = float(value->NumberValue());
2016 } else if (callType == QMetaType::QString) {
2017 if (value->IsNull() || value->IsUndefined())
2018 qstringPtr = new (&allocData) QString();
2020 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2022 } else if (callType == QMetaType::QObjectStar) {
2023 qobjectPtr = engine->toQObject(value);
2025 } else if (callType == qMetaTypeId<QVariant>()) {
2026 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2028 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2029 qlistPtr = new (&allocData) QList<QObject *>();
2030 if (value->IsArray()) {
2031 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2032 uint32_t length = array->Length();
2033 for (uint32_t ii = 0; ii < length; ++ii)
2034 qlistPtr->append(engine->toQObject(array->Get(ii)));
2036 qlistPtr->append(engine->toQObject(value));
2039 } else if (callType == qMetaTypeId<QQmlV8Handle>()) {
2040 handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value));
2043 qvariantPtr = new (&allocData) QVariant();
2046 QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0;
2047 QVariant v = engine->toVariant(value, -1);
2049 if (v.userType() == callType) {
2051 } else if (v.canConvert((QVariant::Type)callType)) {
2053 qvariantPtr->convert((QVariant::Type)callType);
2054 } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
2055 QObject *obj = ep->toQObject(v);
2058 const QMetaObject *objMo = obj->metaObject();
2059 while (objMo && objMo != mo) objMo = objMo->superClass();
2060 if (!objMo) obj = 0;
2063 *qvariantPtr = QVariant(callType, &obj);
2065 *qvariantPtr = QVariant(callType, (void *)0);
2070 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2072 if (type == qMetaTypeId<QJSValue>()) {
2073 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2074 } else if (type == QMetaType::Int) {
2075 return v8::Integer::New(int(intValue));
2076 } else if (type == QMetaType::UInt) {
2077 return v8::Integer::NewFromUnsigned(intValue);
2078 } else if (type == QMetaType::Bool) {
2079 return v8::Boolean::New(boolValue);
2080 } else if (type == QMetaType::Double) {
2081 return v8::Number::New(doubleValue);
2082 } else if (type == QMetaType::Float) {
2083 return v8::Number::New(floatValue);
2084 } else if (type == QMetaType::QString) {
2085 return engine->toString(*qstringPtr);
2086 } else if (type == QMetaType::QObjectStar) {
2087 QObject *object = qobjectPtr;
2089 QQmlData::get(object, true)->setImplicitDestructible();
2090 return engine->newQObject(object);
2091 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2092 // XXX Can this be made more by using Array as a prototype and implementing
2093 // directly against QList<QObject*>?
2094 QList<QObject *> &list = *qlistPtr;
2095 v8::Local<v8::Array> array = v8::Array::New(list.count());
2096 for (int ii = 0; ii < list.count(); ++ii)
2097 array->Set(ii, engine->newQObject(list.at(ii)));
2099 } else if (type == qMetaTypeId<QQmlV8Handle>()) {
2100 return handlePtr->toHandle();
2101 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2102 QVariant value = *qvariantPtr;
2103 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2104 if (QObject *object = engine->toQObject(rv))
2105 QQmlData::get(object, true)->setImplicitDestructible();
2108 return v8::Undefined();