1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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/qdeclarativeguard_p.h>
47 #include <private/qdeclarativepropertycache_p.h>
48 #include <private/qdeclarativeengine_p.h>
49 #include <private/qdeclarativevmemetaobject_p.h>
50 #include <private/qdeclarativebinding_p.h>
51 #include <private/qjsvalue_p.h>
52 #include <private/qscript_impl_p.h>
53 #include <private/qdeclarativeaccessors_p.h>
54 #include <private/qdeclarativeexpression_p.h>
56 #include <QtDeclarative/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(QDeclarativeV8Handle);
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 QDeclarativeEngine *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 QDeclarativeGuard<QObject> object;
90 class QV8QObjectInstance : public QDeclarativeGuard<QObject>
93 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
94 : QDeclarativeGuard<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 QDeclarativeGuard<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 QDeclarativeV8Handle>::Size];
162 qint64 q_for_alignment;
165 // Pointers to allocData
168 QVariant *qvariantPtr;
169 QList<QObject *> *qlistPtr;
170 QJSValue *qjsValuePtr;
171 QDeclarativeV8Handle *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 QDeclarativePropertyData &property,
220 void *output, QDeclarativeNotifier **n)
225 void *args[] = { output, 0 };
226 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
229 static inline void Direct(QObject *object, const QDeclarativePropertyData &property,
230 void *output, QDeclarativeNotifier **n)
235 void *args[] = { output, 0 };
236 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
239 static inline void Accessor(QObject *object, const QDeclarativePropertyData &property,
240 void *output, QDeclarativeNotifier **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 QDeclarativePropertyData &,
265 void *, QDeclarativeNotifier **)>
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 QDeclarativePropertyData *property =
275 (QDeclarativePropertyData *)v8::External::Unwrap(info.Data());
277 QDeclarativeEngine *engine = resource->engine->engine();
278 QDeclarativeEnginePrivate *ep = engine?QDeclarativeEnginePrivate::get(engine):0;
282 if (ep && ep->propertyCapture) {
283 if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) {
284 QDeclarativeNotifier *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 QDeclarativePropertyData &,
384 void *, QDeclarativeNotifier **)>
385 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
386 const QDeclarativePropertyData &property,
387 QDeclarativeNotifier **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()) {
426 QDeclarativeV8Handle handle;
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 (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)
434 && engine->engine()) {
435 Q_ASSERT(notifier == 0);
437 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
438 QDeclarativeValueType *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 QDeclarativePropertyData local;
504 QDeclarativePropertyData *result = 0;
506 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
507 if (ddata && ddata->propertyCache)
508 result = ddata->propertyCache->property(property);
510 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
514 return v8::Handle<v8::Value>();
516 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
517 QDeclarativeData *ddata = QDeclarativeData::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 ((QDeclarativeVMEMetaObject *)(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 QDeclarativeEnginePrivate *ep =
538 engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
540 if (result->hasAccessors()) {
541 QDeclarativeNotifier *n = 0;
542 QDeclarativeNotifier **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(QDeclarativeData::get(object, true)->objectNameNotifier());
563 ep->captureProperty(object, result->coreIndex, result->notifyIndex);
566 if (result->isVMEProperty()) {
567 typedef QDeclarativeVMEMetaObject 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, QDeclarativePropertyData *property,
579 v8::Handle<v8::Value> value)
581 QDeclarativeBinding *newBinding = 0;
583 if (value->IsFunction()) {
584 QDeclarativeContextData *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 QDeclarativeBinding(&function, object, context);
596 newBinding->setSourceLocation(url, lineNumber, columNumber);
597 newBinding->setTarget(object, *property, context);
598 newBinding->setEvaluateFlags(newBinding->evaluateFlags() |
599 QDeclarativeBinding::RequiresThisObject);
602 QDeclarativeAbstractBinding *oldBinding =
603 QDeclarativePropertyPrivate::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<QDeclarativeVMEMetaObject *>(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 QDeclarativeContextData *context = engine->callingContext();
648 if (!QDeclarativePropertyPrivate::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 QDeclarativePropertyData local;
670 QDeclarativePropertyData *result = 0;
671 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
676 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
677 QDeclarativeData *ddata = QDeclarativeData::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 QDeclarativeContextData *context = v8engine->callingContext();
717 if (context && context->imports) {
718 QDeclarativeTypeNameCache::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 QDeclarativePropertyData local;
777 QDeclarativePropertyData *result = 0;
778 result = QDeclarativePropertyCache::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 QDeclarativeEnginePrivate *ep = resource->engine->engine()
800 ? QDeclarativeEnginePrivate::get(resource->engine->engine())
803 QDeclarativePropertyCache *cache = 0;
804 QDeclarativeData *ddata = QDeclarativeData::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 QDeclarativePropertyData *property =
843 (QDeclarativePropertyData *)v8::External::Unwrap(info.Data());
845 int index = property->coreIndex;
847 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
849 Q_ASSERT(ddata->propertyCache);
851 QDeclarativePropertyData *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 QDeclarativeData *ddata = QDeclarativeData::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> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
905 Q_ASSERT(this->engine);
907 Q_ASSERT(QDeclarativeData::get(object, false));
908 Q_ASSERT(QDeclarativeData::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 // XXX TODO: Enables fast property accessors. These more than double the property access
918 // performance, but the cost of setting up this structure hasn't been measured so
919 // its not guarenteed that this is a win overall. We need to try and measure the cost.
920 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
921 QDeclarativePropertyData *property = *iter;
922 if (property->notFullyResolved()) resolve(property);
924 if (property->isFunction())
927 v8::AccessorGetter fastgetter = 0;
928 v8::AccessorSetter fastsetter = FastValueSetter;
929 if (!property->isWritable())
930 fastsetter = FastValueSetterReadOnly;
932 if (property->isQObject())
933 fastgetter = FAST_GETTER_FUNCTION(property, QObject*);
934 else if (property->propType == QMetaType::Int || property->isEnum())
935 fastgetter = FAST_GETTER_FUNCTION(property, int);
936 else if (property->propType == QMetaType::Bool)
937 fastgetter = FAST_GETTER_FUNCTION(property, bool);
938 else if (property->propType == QMetaType::QString)
939 fastgetter = FAST_GETTER_FUNCTION(property, QString);
940 else if (property->propType == QMetaType::UInt)
941 fastgetter = FAST_GETTER_FUNCTION(property, uint);
942 else if (property->propType == QMetaType::Float)
943 fastgetter = FAST_GETTER_FUNCTION(property, float);
944 else if (property->propType == QMetaType::Double)
945 fastgetter = FAST_GETTER_FUNCTION(property, double);
948 QString name = iter.key();
949 if (name == toString || name == destroy)
953 ft = v8::FunctionTemplate::New();
954 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
955 QV8QObjectWrapper::Setter,
956 QV8QObjectWrapper::Query,
958 QV8QObjectWrapper::Enumerator);
959 ft->InstanceTemplate()->SetHasExternalResource(true);
962 // We wrap the raw QDeclarativePropertyData pointer here. This is safe as the
963 // pointer will remain valid at least as long as the lifetime of any QObject's of
964 // this type and the property accessor checks if the object is 0 (deleted) before
965 // dereferencing the pointer.
966 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
967 v8::External::Wrap(property));
972 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
974 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
975 QV8QObjectWrapper::Setter,
976 QV8QObjectWrapper::Query,
978 QV8QObjectWrapper::Enumerator);
979 ft->InstanceTemplate()->SetHasExternalResource(true);
980 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
983 QDeclarativeCleanup::addToEngine(this->engine);
986 v8::Local<v8::Object> result = constructor->NewInstance();
987 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
988 result->SetExternalResource(r);
992 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
994 v8::Local<v8::Object> rv;
996 if (!ddata->propertyCache && engine->engine()) {
997 ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
998 if (ddata->propertyCache) ddata->propertyCache->addref();
1001 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
1002 rv = ddata->propertyCache->newQObject(object, engine);
1004 // XXX NewInstance() should be optimized
1005 rv = m_constructor->NewInstance();
1006 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
1007 rv->SetExternalResource(r);
1014 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
1015 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
1017 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
1018 persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that
1019 "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this
1021 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create
1022 an entry in the m_taintedObject hash where we store the handle and mark the object as
1023 "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
1024 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
1025 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has
1026 released the handle.
1028 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
1033 if (QObjectPrivate::get(object)->wasDeleted)
1034 return v8::Undefined();
1036 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
1039 return v8::Undefined();
1041 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
1042 // We own the v8object
1043 return v8::Local<v8::Object>::New(ddata->v8object);
1044 } else if (ddata->v8object.IsEmpty() &&
1045 (ddata->v8objectid == m_id || // We own the QObject
1046 ddata->v8objectid == 0 || // No one owns the QObject
1047 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
1049 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1050 ddata->v8object = qPersistentNew<v8::Object>(rv);
1051 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1052 ddata->v8objectid = m_id;
1056 // If this object is tainted, we have to check to see if it is in our
1057 // tainted object list
1058 TaintedHash::Iterator iter =
1059 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
1060 bool found = iter != m_taintedObjects.end();
1062 // If our tainted handle doesn't exist or has been collected, and there isn't
1063 // a handle in the ddata, we can assume ownership of the ddata->v8object
1064 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
1065 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1066 ddata->v8object = qPersistentNew<v8::Object>(rv);
1067 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
1068 ddata->v8objectid = m_id;
1072 m_taintedObjects.erase(iter);
1076 } else if (!found) {
1077 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
1078 iter = m_taintedObjects.insert(object, instance);
1079 ddata->hasTaintedV8Object = true;
1082 if ((*iter)->v8object.IsEmpty()) {
1083 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
1084 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
1085 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
1088 return v8::Local<v8::Object>::New((*iter)->v8object);
1092 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object)
1094 if (object->IsFunction())
1095 return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object));
1097 if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object))
1098 return qMakePair(resource->object.data(), resource->index);
1100 return qMakePair((QObject *)0, -1);
1103 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
1105 v8::ScriptOrigin origin = function->GetScriptOrigin();
1106 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
1108 // This is one of our special QObject method wrappers
1109 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1110 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1112 if (data->IsArray()) {
1113 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1114 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1117 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1120 return qMakePair((QObject *)0, -1);
1123 class QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
1126 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1127 ~QV8QObjectConnectionList();
1131 : needsDestroy(false) {}
1132 Connection(const Connection &other)
1133 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1134 Connection &operator=(const Connection &other) {
1135 thisObject = other.thisObject;
1136 function = other.function;
1137 needsDestroy = other.needsDestroy;
1141 v8::Persistent<v8::Object> thisObject;
1142 v8::Persistent<v8::Function> function;
1145 qPersistentDispose(thisObject);
1146 qPersistentDispose(function);
1152 struct ConnectionList : public QList<Connection> {
1153 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1154 int connectionsInUse;
1155 bool connectionsNeedClean;
1160 typedef QHash<int, ConnectionList> SlotHash;
1165 virtual void objectDestroyed(QObject *);
1166 virtual int qt_metacall(QMetaObject::Call, int, void **);
1169 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1170 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1174 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1176 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1177 QList<Connection> &connections = *iter;
1178 for (int ii = 0; ii < connections.count(); ++ii) {
1179 qPersistentDispose(connections[ii].thisObject);
1180 qPersistentDispose(connections[ii].function);
1186 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1188 engine->qobjectWrapper()->m_connections.remove(object);
1191 needsDestroy = true;
1196 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1198 if (method == QMetaObject::InvokeMetaMethod) {
1199 SlotHash::Iterator iter = slotHash.find(index);
1200 if (iter == slotHash.end())
1202 ConnectionList &connectionList = *iter;
1203 if (connectionList.isEmpty())
1208 connectionList.connectionsInUse++;
1210 QList<Connection> connections = connectionList;
1212 QVarLengthArray<int, 9> dummy;
1213 int *argsTypes = QDeclarativePropertyCache::methodParameterTypes(data(), index, dummy, 0);
1215 v8::HandleScope handle_scope;
1216 v8::Context::Scope scope(engine->context());
1218 int argCount = argsTypes?argsTypes[0]:0;
1219 QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount);
1221 for (int ii = 0; ii < argCount; ++ii) {
1222 int type = argsTypes[ii + 1];
1223 if (type == qMetaTypeId<QVariant>()) {
1224 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1226 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1230 for (int ii = 0; ii < connections.count(); ++ii) {
1231 Connection &connection = connections[ii];
1232 if (connection.needsDestroy)
1235 v8::TryCatch try_catch;
1236 if (connection.thisObject.IsEmpty()) {
1237 connection.function->Call(engine->global(), argCount, args.data());
1239 connection.function->Call(connection.thisObject, argCount, args.data());
1242 if (try_catch.HasCaught()) {
1243 QDeclarativeError error;
1244 error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName())));
1245 v8::Local<v8::Message> message = try_catch.Message();
1246 if (!message.IsEmpty())
1247 QDeclarativeExpressionPrivate::exceptionToError(message, error);
1248 QDeclarativeEnginePrivate::get(engine->engine())->warning(error);
1252 connectionList.connectionsInUse--;
1253 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1254 for (QList<Connection>::Iterator iter = connectionList.begin();
1255 iter != connectionList.end(); ) {
1256 if (iter->needsDestroy) {
1258 iter = connectionList.erase(iter);
1266 if (inUse == 0 && needsDestroy)
1273 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1275 if (args.Length() == 0)
1276 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1278 QV8Engine *engine = V8ENGINE();
1280 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1281 QObject *signalObject = signalInfo.first;
1282 int signalIndex = signalInfo.second;
1284 if (signalIndex == -1)
1285 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1288 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1290 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1291 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1293 v8::Local<v8::Value> functionValue;
1294 v8::Local<v8::Value> functionThisValue;
1296 if (args.Length() == 1) {
1297 functionValue = args[0];
1299 functionThisValue = args[0];
1300 functionValue = args[1];
1303 if (!functionValue->IsFunction())
1304 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1306 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1307 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1309 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1310 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1311 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1312 if (iter == connections.end())
1313 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1315 QV8QObjectConnectionList *connectionList = *iter;
1316 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1317 if (slotIter == connectionList->slotHash.end()) {
1318 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1319 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1322 QV8QObjectConnectionList::Connection connection;
1323 if (!functionThisValue.IsEmpty())
1324 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1325 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1327 slotIter->append(connection);
1329 return v8::Undefined();
1332 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1334 if (args.Length() == 0)
1335 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1337 QV8Engine *engine = V8ENGINE();
1339 QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This());
1340 QObject *signalObject = signalInfo.first;
1341 int signalIndex = signalInfo.second;
1343 if (signalIndex == -1)
1344 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1347 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1349 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1350 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1352 v8::Local<v8::Value> functionValue;
1353 v8::Local<v8::Value> functionThisValue;
1355 if (args.Length() == 1) {
1356 functionValue = args[0];
1358 functionThisValue = args[0];
1359 functionValue = args[1];
1362 if (!functionValue->IsFunction())
1363 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1365 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1366 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1368 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1369 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1370 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1371 if (iter == connectionsList.end())
1372 return v8::Undefined(); // Nothing to disconnect from
1374 QV8QObjectConnectionList *connectionList = *iter;
1375 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1376 if (slotIter == connectionList->slotHash.end())
1377 return v8::Undefined(); // Nothing to disconnect from
1379 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1381 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1382 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1384 if (functionData.second != -1) {
1385 // This is a QObject function wrapper
1386 for (int ii = 0; ii < connections.count(); ++ii) {
1387 QV8QObjectConnectionList::Connection &connection = connections[ii];
1389 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1390 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1392 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1393 if (connectedFunctionData == functionData) {
1395 if (connections.connectionsInUse) {
1396 connection.needsDestroy = true;
1397 connections.connectionsNeedClean = true;
1399 connection.dispose();
1400 connections.removeAt(ii);
1402 return v8::Undefined();
1408 // This is a normal JS function
1409 for (int ii = 0; ii < connections.count(); ++ii) {
1410 QV8QObjectConnectionList::Connection &connection = connections[ii];
1411 if (connection.function->StrictEquals(function) &&
1412 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1413 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1415 if (connections.connectionsInUse) {
1416 connection.needsDestroy = true;
1417 connections.connectionsNeedClean = true;
1419 connection.dispose();
1420 connections.removeAt(ii);
1422 return v8::Undefined();
1427 return v8::Undefined();
1431 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1433 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1435 Only searches for real properties of \a object (including methods), not attached properties etc.
1439 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1441 Set the \a property of \a object to \a value.
1443 Returns true if the property was "set" - even if this results in an exception being thrown -
1444 and false if the object has no such property.
1446 Only searches for real properties of \a object (including methods), not attached properties etc.
1452 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1453 int Length() const { return _length; }
1454 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1458 v8::Handle<v8::Object> *_args;
1462 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1463 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1467 QVarLengthArray<CallArgument, 9> args(argCount + 1);
1468 args[0].initAsType(returnType);
1470 for (int ii = 0; ii < argCount; ++ii)
1471 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1473 QVarLengthArray<void *, 9> argData(args.count());
1474 for (int ii = 0; ii < args.count(); ++ii)
1475 argData[ii] = args[ii].dataPtr();
1477 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1479 return args[0].toValue(engine);
1481 } else if (returnType != 0) {
1484 arg.initAsType(returnType);
1486 void *args[] = { arg.dataPtr() };
1488 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1490 return arg.toValue(engine);
1494 void *args[] = { 0 };
1495 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1496 return v8::Undefined();
1502 Returns the match score for converting \a actual to be of type \a conversionType. A
1503 zero score means "perfect match" whereas a higher score is worse.
1505 The conversion table is copied out of the QtScript callQtMethod() function.
1507 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType)
1509 if (actual->IsNumber()) {
1510 switch (conversionType) {
1511 case QMetaType::Double:
1513 case QMetaType::Float:
1515 case QMetaType::LongLong:
1516 case QMetaType::ULongLong:
1518 case QMetaType::Long:
1519 case QMetaType::ULong:
1521 case QMetaType::Int:
1522 case QMetaType::UInt:
1524 case QMetaType::Short:
1525 case QMetaType::UShort:
1528 case QMetaType::Char:
1529 case QMetaType::UChar:
1534 } else if (actual->IsString()) {
1535 switch (conversionType) {
1536 case QMetaType::QString:
1541 } else if (actual->IsBoolean()) {
1542 switch (conversionType) {
1543 case QMetaType::Bool:
1548 } else if (actual->IsDate()) {
1549 switch (conversionType) {
1550 case QMetaType::QDateTime:
1552 case QMetaType::QDate:
1554 case QMetaType::QTime:
1559 } else if (actual->IsRegExp()) {
1560 switch (conversionType) {
1561 case QMetaType::QRegExp:
1566 } else if (actual->IsArray()) {
1567 switch (conversionType) {
1568 case QMetaType::QStringList:
1569 case QMetaType::QVariantList:
1574 } else if (actual->IsNull()) {
1575 switch (conversionType) {
1576 case QMetaType::VoidStar:
1577 case QMetaType::QObjectStar:
1580 const char *typeName = QMetaType::typeName(conversionType);
1581 if (typeName && typeName[strlen(typeName) - 1] == '*')
1587 } else if (actual->IsObject()) {
1588 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1590 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1591 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1592 switch (conversionType) {
1593 case QMetaType::QObjectStar:
1598 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1599 if (conversionType == qMetaTypeId<QVariant>())
1601 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1614 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1620 int classInfoCount, classInfoData;
1621 int methodCount, methodData;
1624 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1627 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1629 QByteArray sig = m.signature();
1630 int paren = sig.indexOf('(');
1634 return sig.left(paren);
1638 Returns the next related method, if one, or 0.
1640 static const QDeclarativePropertyData * RelatedMethod(QObject *object,
1641 const QDeclarativePropertyData *current,
1642 QDeclarativePropertyData &dummy)
1644 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1645 if (!current->isOverload())
1648 Q_ASSERT(!current->overrideIndexIsProperty);
1651 return cache->method(current->overrideIndex);
1653 const QMetaObject *mo = object->metaObject();
1654 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1656 while (methodOffset > current->overrideIndex) {
1657 mo = mo->superClass();
1658 methodOffset -= QMetaObject_methods(mo);
1661 QMetaMethod method = mo->method(current->overrideIndex);
1664 // Look for overloaded methods
1665 QByteArray methodName = QMetaMethod_name(method);
1666 for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) {
1667 if (methodName == QMetaMethod_name(mo->method(ii))) {
1668 dummy.setFlags(dummy.getFlags() | QDeclarativePropertyData::IsOverload);
1669 dummy.overrideIndexIsProperty = 0;
1670 dummy.overrideIndex = ii;
1679 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyData &data,
1680 QV8Engine *engine, CallArgs &callArgs)
1682 if (data.hasArguments()) {
1685 QVarLengthArray<int, 9> dummy;
1686 QByteArray unknownTypeError;
1688 args = QDeclarativePropertyCache::methodParameterTypes(object, data.coreIndex, dummy,
1692 QString typeName = QString::fromLatin1(unknownTypeError);
1693 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName);
1694 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1695 return v8::Handle<v8::Value>();
1698 if (args[0] > callArgs.Length()) {
1699 QString error = QLatin1String("Insufficient arguments");
1700 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1701 return v8::Handle<v8::Value>();
1704 return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs);
1708 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1714 Resolve the overloaded method to call. The algorithm works conceptually like this:
1715 1. Resolve the set of overloads it is *possible* to call.
1716 Impossible overloads include those that have too many parameters or have parameters
1718 2. Filter the set of overloads to only contain those with the closest number of
1720 For example, if we are called with 3 parameters and there are 2 overloads that
1721 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1722 3. Find the best remaining overload based on its match score.
1723 If two or more overloads have the same match score, call the last one. The match
1724 score is constructed by adding the matchScore() result for each of the parameters.
1726 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyData &data,
1727 QV8Engine *engine, CallArgs &callArgs)
1729 int argumentCount = callArgs.Length();
1731 const QDeclarativePropertyData *best = 0;
1732 int bestParameterScore = INT_MAX;
1733 int bestMatchScore = INT_MAX;
1735 QDeclarativePropertyData dummy;
1736 const QDeclarativePropertyData *attempt = &data;
1739 QVarLengthArray<int, 9> dummy;
1740 int methodArgumentCount = 0;
1741 int *methodArgTypes = 0;
1742 if (attempt->hasArguments()) {
1743 typedef QDeclarativePropertyCache PC;
1744 int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0);
1745 if (!args) // Must be an unknown argument
1748 methodArgumentCount = args[0];
1749 methodArgTypes = args + 1;
1752 if (methodArgumentCount > argumentCount)
1753 continue; // We don't have sufficient arguments to call this method
1755 int methodParameterScore = argumentCount - methodArgumentCount;
1756 if (methodParameterScore > bestParameterScore)
1757 continue; // We already have a better option
1759 int methodMatchScore = 0;
1760 for (int ii = 0; ii < methodArgumentCount; ++ii)
1761 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]);
1763 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1765 bestParameterScore = methodParameterScore;
1766 bestMatchScore = methodMatchScore;
1769 if (bestParameterScore == 0 && bestMatchScore == 0)
1770 break; // We can't get better than that
1772 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1775 return CallPrecise(object, *best, engine, callArgs);
1777 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1778 const QDeclarativePropertyData *candidate = &data;
1780 error += QLatin1String("\n ") +
1781 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1782 candidate = RelatedMethod(object, candidate, dummy);
1785 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1786 return v8::Handle<v8::Value>();
1790 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1794 QString objectName = object->objectName();
1796 result += QString::fromUtf8(object->metaObject()->className());
1797 result += QLatin1String("(0x");
1798 result += QString::number((quintptr)object,16);
1800 if (!objectName.isEmpty()) {
1801 result += QLatin1String(", \"");
1802 result += objectName;
1803 result += QLatin1Char('\"');
1806 result += QLatin1Char(')');
1808 result = QLatin1String("null");
1811 return engine->toString(result);
1814 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1816 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1817 if (!ddata || ddata->indestructible) {
1818 const char *error = "Invalid attempt to destroy() an indestructible object";
1819 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1820 return v8::Undefined();
1825 delay = args->Get(0)->Uint32Value();
1828 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1830 object->deleteLater();
1832 return v8::Undefined();
1835 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1837 // object, index, qmlglobal, argCount, args
1838 Q_ASSERT(args.Length() == 5);
1839 Q_ASSERT(args[0]->IsObject());
1841 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1844 return v8::Undefined();
1846 int argCount = args[3]->Int32Value();
1847 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1849 // Special hack to return info about this closure.
1850 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1851 v8::Local<v8::Array> data = v8::Array::New(2);
1852 data->Set(0, args[0]);
1853 data->Set(1, args[1]);
1857 QObject *object = resource->object;
1858 int index = args[1]->Int32Value();
1861 return v8::Undefined();
1864 // Builtin functions
1865 if (index == QOBJECT_TOSTRING_INDEX) {
1866 return ToString(resource->engine, object, argCount, arguments);
1867 } else if (index == QOBJECT_DESTROY_INDEX) {
1868 return Destroy(resource->engine, object, argCount, arguments);
1870 return v8::Undefined();
1874 QDeclarativePropertyData method;
1876 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1877 if (ddata->propertyCache) {
1878 QDeclarativePropertyData *d = ddata->propertyCache->method(index);
1880 return v8::Undefined();
1885 if (method.coreIndex == -1) {
1886 method.load(object->metaObject()->method(index));
1888 if (method.coreIndex == -1)
1889 return v8::Undefined();
1892 if (method.isV8Function()) {
1893 v8::Handle<v8::Value> rv;
1894 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1896 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1897 resource->engine->contextWrapper()->context(qmlglobal),
1899 QDeclarativeV8Function *funcptr = &func;
1901 void *args[] = { 0, &funcptr };
1902 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1904 if (rv.IsEmpty()) return v8::Undefined();
1908 CallArgs callArgs(argCount, &arguments);
1909 if (!method.isOverload()) {
1910 return CallPrecise(object, method, resource->engine, callArgs);
1912 return CallOverloaded(object, method, resource->engine, callArgs);
1916 CallArgument::CallArgument()
1917 : type(QVariant::Invalid)
1921 CallArgument::~CallArgument()
1926 void CallArgument::cleanup()
1928 if (type == QMetaType::QString) {
1929 qstringPtr->~QString();
1930 } else if (type == -1 || type == QMetaType::QVariant) {
1931 qvariantPtr->~QVariant();
1932 } else if (type == qMetaTypeId<QJSValue>()) {
1933 qjsValuePtr->~QJSValue();
1934 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1935 qlistPtr->~QList<QObject *>();
1939 void *CallArgument::dataPtr()
1942 return qvariantPtr->data();
1944 return (void *)&allocData;
1947 void CallArgument::initAsType(int callType)
1949 if (type != 0) { cleanup(); type = 0; }
1950 if (callType == 0) return;
1952 if (callType == qMetaTypeId<QJSValue>()) {
1953 qjsValuePtr = new (&allocData) QJSValue();
1955 } else if (callType == QMetaType::Int ||
1956 callType == QMetaType::UInt ||
1957 callType == QMetaType::Bool ||
1958 callType == QMetaType::Double ||
1959 callType == QMetaType::Float) {
1961 } else if (callType == QMetaType::QObjectStar) {
1964 } else if (callType == QMetaType::QString) {
1965 qstringPtr = new (&allocData) QString();
1967 } else if (callType == QMetaType::QVariant) {
1969 qvariantPtr = new (&allocData) QVariant();
1970 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1972 qlistPtr = new (&allocData) QList<QObject *>();
1973 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1975 handlePtr = new (&allocData) QDeclarativeV8Handle;
1978 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
1982 void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1984 if (type != 0) { cleanup(); type = 0; }
1986 if (callType == qMetaTypeId<QJSValue>()) {
1987 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
1988 type = qMetaTypeId<QJSValue>();
1989 } else if (callType == QMetaType::Int) {
1990 intValue = quint32(value->Int32Value());
1992 } else if (callType == QMetaType::UInt) {
1993 intValue = quint32(value->Uint32Value());
1995 } else if (callType == QMetaType::Bool) {
1996 boolValue = value->BooleanValue();
1998 } else if (callType == QMetaType::Double) {
1999 doubleValue = double(value->NumberValue());
2001 } else if (callType == QMetaType::Float) {
2002 floatValue = float(value->NumberValue());
2004 } else if (callType == QMetaType::QString) {
2005 if (value->IsNull() || value->IsUndefined())
2006 qstringPtr = new (&allocData) QString();
2008 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
2010 } else if (callType == QMetaType::QObjectStar) {
2011 qobjectPtr = engine->toQObject(value);
2013 } else if (callType == qMetaTypeId<QVariant>()) {
2014 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
2016 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
2017 qlistPtr = new (&allocData) QList<QObject *>();
2018 if (value->IsArray()) {
2019 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
2020 uint32_t length = array->Length();
2021 for (uint32_t ii = 0; ii < length; ++ii)
2022 qlistPtr->append(engine->toQObject(array->Get(ii)));
2024 qlistPtr->append(engine->toQObject(value));
2027 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
2028 handlePtr = new (&allocData) QDeclarativeV8Handle(QDeclarativeV8Handle::fromHandle(value));
2031 qvariantPtr = new (&allocData) QVariant();
2034 QDeclarativeEnginePrivate *ep = engine->engine() ? QDeclarativeEnginePrivate::get(engine->engine()) : 0;
2035 QVariant v = engine->toVariant(value, -1);
2037 if (v.userType() == callType) {
2039 } else if (v.canConvert((QVariant::Type)callType)) {
2041 qvariantPtr->convert((QVariant::Type)callType);
2042 } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
2043 QObject *obj = ep->toQObject(v);
2046 const QMetaObject *objMo = obj->metaObject();
2047 while (objMo && objMo != mo) objMo = objMo->superClass();
2048 if (!objMo) obj = 0;
2051 *qvariantPtr = QVariant(callType, &obj);
2053 *qvariantPtr = QVariant(callType, (void *)0);
2058 v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine)
2060 if (type == qMetaTypeId<QJSValue>()) {
2061 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
2062 } else if (type == QMetaType::Int) {
2063 return v8::Integer::New(int(intValue));
2064 } else if (type == QMetaType::UInt) {
2065 return v8::Integer::NewFromUnsigned(intValue);
2066 } else if (type == QMetaType::Bool) {
2067 return v8::Boolean::New(boolValue);
2068 } else if (type == QMetaType::Double) {
2069 return v8::Number::New(doubleValue);
2070 } else if (type == QMetaType::Float) {
2071 return v8::Number::New(floatValue);
2072 } else if (type == QMetaType::QString) {
2073 return engine->toString(*qstringPtr);
2074 } else if (type == QMetaType::QObjectStar) {
2075 QObject *object = qobjectPtr;
2077 QDeclarativeData::get(object, true)->setImplicitDestructible();
2078 return engine->newQObject(object);
2079 } else if (type == qMetaTypeId<QList<QObject *> >()) {
2080 // XXX Can this be made more by using Array as a prototype and implementing
2081 // directly against QList<QObject*>?
2082 QList<QObject *> &list = *qlistPtr;
2083 v8::Local<v8::Array> array = v8::Array::New(list.count());
2084 for (int ii = 0; ii < list.count(); ++ii)
2085 array->Set(ii, engine->newQObject(list.at(ii)));
2087 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
2088 return handlePtr->toHandle();
2089 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2090 QVariant value = *qvariantPtr;
2091 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2092 if (QObject *object = engine->toQObject(rv))
2093 QDeclarativeData::get(object, true)->setImplicitDestructible();
2096 return v8::Undefined();