1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
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>
54 #include <QtDeclarative/qjsvalue.h>
55 #include <QtCore/qvarlengtharray.h>
56 #include <QtCore/qtimer.h>
57 #include <QtCore/qatomic.h>
61 Q_DECLARE_METATYPE(QJSValue);
62 Q_DECLARE_METATYPE(QDeclarativeV8Handle);
65 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
66 // The code in this file does not violate strict aliasing, but GCC thinks it does
67 // so turn off the warnings for us to have a clean build
68 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
72 #define QOBJECT_TOSTRING_INDEX -2
73 #define QOBJECT_DESTROY_INDEX -3
75 // XXX TODO: Need to review all calls to QDeclarativeEngine *engine() to confirm QObjects work
76 // correctly in a worker thread
78 class QV8QObjectResource : public QV8ObjectResource
80 V8_RESOURCE_TYPE(QObjectType);
83 QV8QObjectResource(QV8Engine *engine, QObject *object);
85 QDeclarativeGuard<QObject> object;
88 class QV8QObjectInstance : public QDeclarativeGuard<QObject>
91 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
92 : QDeclarativeGuard<QObject>(o), wrapper(w)
98 qPersistentDispose(v8object);
101 virtual void objectDestroyed(QObject *o)
104 wrapper->m_taintedObjects.remove(o);
108 v8::Persistent<v8::Object> v8object;
109 QV8QObjectWrapper *wrapper;
113 struct MetaCallArgument {
114 inline MetaCallArgument();
115 inline ~MetaCallArgument();
116 inline void *dataPtr();
118 inline void initAsType(int type);
119 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
120 inline v8::Handle<v8::Value> toValue(QV8Engine *);
123 MetaCallArgument(const MetaCallArgument &);
125 inline void cleanup();
134 char allocData[sizeof(QVariant)];
137 // Pointers to allocData
140 QVariant *qvariantPtr;
141 QList<QObject *> *qlistPtr;
142 QJSValue *qjsValuePtr;
143 QDeclarativeV8Handle *handlePtr;
150 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
151 : QV8ObjectResource(engine), object(object)
155 static QAtomicInt objectIdCounter(1);
157 QV8QObjectWrapper::QV8QObjectWrapper()
158 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
162 QV8QObjectWrapper::~QV8QObjectWrapper()
164 for (TaintedHash::Iterator iter = m_taintedObjects.begin();
165 iter != m_taintedObjects.end();
167 (*iter)->wrapper = 0;
169 m_taintedObjects.clear();
172 void QV8QObjectWrapper::destroy()
174 qDeleteAll(m_connections);
175 m_connections.clear();
177 qPersistentDispose(m_hiddenObject);
178 qPersistentDispose(m_destroySymbol);
179 qPersistentDispose(m_toStringSymbol);
180 qPersistentDispose(m_methodConstructor);
181 qPersistentDispose(m_constructor);
184 #define FAST_VALUE_GETTER(name, cpptype, defaultvalue, constructor) \
185 static v8::Handle<v8::Value> name ## ValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) \
187 v8::Handle<v8::Object> This = info.This(); \
188 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
190 if (resource->object.isNull()) return v8::Undefined(); \
192 QObject *object = resource->object; \
194 uint32_t data = info.Data()->Uint32Value(); \
195 int index = data & 0x7FFF; \
196 int notify = (data & 0x0FFF0000) >> 16; \
197 if (notify == 0x0FFF) notify = -1; \
199 QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
200 if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
201 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
202 ep->capturedProperties << CapturedProperty(object, index, notify); \
205 cpptype value = defaultvalue; \
206 void *args[] = { &value, 0 }; \
207 QMetaObject::metacall(object, QMetaObject::ReadProperty, index, args); \
209 return constructor(value); \
211 static v8::Handle<v8::Value> name ## ValueGetterDirect(v8::Local<v8::String>, const v8::AccessorInfo &info) \
213 v8::Handle<v8::Object> This = info.This(); \
214 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
216 if (resource->object.isNull()) return v8::Undefined(); \
218 QObject *object = resource->object; \
220 uint32_t data = info.Data()->Uint32Value(); \
221 int index = data & 0x7FFF; \
222 int notify = (data & 0x0FFF0000) >> 16; \
223 if (notify == 0x0FFF) notify = -1; \
225 QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
226 if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
227 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
228 ep->capturedProperties << CapturedProperty(object, index, notify); \
231 cpptype value = defaultvalue; \
232 void *args[] = { &value, 0 }; \
233 object->qt_metacall(QMetaObject::ReadProperty, index, args); \
235 return constructor(value); \
238 #define CREATE_FUNCTION \
239 "(function(method) { "\
240 "return (function(object, data, qmlglobal) { "\
241 "return (function() { "\
242 "return method(object, data, qmlglobal, arguments.length, arguments); "\
248 static quint32 toStringHash = -1;
249 static quint32 destroyHash = -1;
251 void QV8QObjectWrapper::init(QV8Engine *engine)
255 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
256 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
257 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
259 m_toStringString = QHashedV8String(m_toStringSymbol);
260 m_destroyString = QHashedV8String(m_destroySymbol);
262 toStringHash = m_toStringString.hash();
263 destroyHash = m_destroyString.hash();
266 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
267 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
268 ft->InstanceTemplate()->SetHasExternalResource(true);
269 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
272 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
273 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin);
274 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
275 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
276 v8::Handle<v8::Value> args[] = { invokeFn };
277 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
278 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
282 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
283 prototype->Set(v8::String::New("connect"), V8FUNCTION(Connect, engine));
284 prototype->Set(v8::String::New("disconnect"), V8FUNCTION(Disconnect, engine));
288 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
290 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
293 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
295 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
296 return r?r->object:0;
299 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
300 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
302 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
303 return static_cast<QV8QObjectResource *>(r)->object;
306 // Load value properties
307 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
308 const QDeclarativePropertyCache::Data &property)
310 Q_ASSERT(!property.isFunction());
312 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
313 if (property.propType == QMetaType:: metatype) { \
314 cpptype type = cpptype(); \
315 void *args[] = { &type, 0 }; \
316 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \
317 return constructor(type); \
320 if (property.isQObject()) {
322 void *args[] = { &rv, 0 };
323 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
324 return engine->newQObject(rv);
325 } else if (property.isQList()) {
326 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
327 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
328 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
329 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
330 else PROPERTY_LOAD(QString, QString, engine->toString)
331 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
332 else PROPERTY_LOAD(Float, float, v8::Number::New)
333 else PROPERTY_LOAD(Double, double, v8::Number::New)
334 else if(property.isV8Handle()) {
335 QDeclarativeV8Handle handle;
336 void *args[] = { &handle, 0 };
337 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
338 return handle.toHandle();
339 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)) {
340 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
341 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
343 return engine->newValueType(object, property.coreIndex, valueType);
346 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
347 return engine->fromVariant(var);
352 static v8::Handle<v8::Value> LoadPropertyDirect(QV8Engine *engine, QObject *object,
353 const QDeclarativePropertyCache::Data &property)
355 Q_ASSERT(!property.isFunction());
357 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
358 if (property.propType == QMetaType:: metatype) { \
359 cpptype type = cpptype(); \
360 void *args[] = { &type, 0 }; \
361 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); \
362 return constructor(type); \
365 if (property.isQObject()) {
367 void *args[] = { &rv, 0 };
368 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
369 return engine->newQObject(rv);
370 } else if (property.isQList()) {
371 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
372 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
373 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
374 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
375 else PROPERTY_LOAD(QString, QString, engine->toString)
376 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
377 else PROPERTY_LOAD(Float, float, v8::Number::New)
378 else PROPERTY_LOAD(Double, double, v8::Number::New)
379 else if(property.isV8Handle()) {
380 QDeclarativeV8Handle handle;
381 void *args[] = { &handle, 0 };
382 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
383 return handle.toHandle();
384 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)) {
385 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
386 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
388 return engine->newValueType(object, property.coreIndex, valueType);
391 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
392 return engine->fromVariant(var);
397 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
398 v8::Handle<v8::Value> *objectHandle,
399 const QHashedV8String &property,
400 QV8QObjectWrapper::RevisionMode revisionMode)
402 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
403 // will be a faster way of creating QObject method objects.
404 struct MethodClosure {
405 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
406 v8::Handle<v8::Value> *objectHandle,
408 v8::Handle<v8::Value> argv[] = {
409 objectHandle?*objectHandle:engine->newQObject(object),
410 v8::Integer::New(index)
412 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
414 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
415 v8::Handle<v8::Value> *objectHandle,
417 v8::Handle<v8::Value> argv[] = {
418 objectHandle?*objectHandle:engine->newQObject(object),
419 v8::Integer::New(index),
420 v8::Context::GetCallingQmlGlobal()
422 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
427 // Comparing the hash first actually makes a measurable difference here, at least on x86
428 quint32 hash = property.hash();
429 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
430 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
431 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
432 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
436 QDeclarativePropertyCache::Data local;
437 QDeclarativePropertyCache::Data *result = 0;
439 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
440 if (ddata && ddata->propertyCache)
441 result = ddata->propertyCache->property(property);
443 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
447 return v8::Handle<v8::Value>();
449 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
450 QDeclarativeData *ddata = QDeclarativeData::get(object);
451 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
452 return v8::Handle<v8::Value>();
455 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
457 if (result->isFunction()) {
458 if (result->isVMEFunction()) {
459 return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
460 } else if (result->isV8Function()) {
461 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
463 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
467 QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
468 if (ep && ep->captureProperties && !result->isConstant()) {
469 if (result->coreIndex == 0)
470 ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
472 ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
475 if (result->isDirect()) {
476 return LoadPropertyDirect(engine, object, *result);
478 return LoadProperty(engine, object, *result);
482 // Setter for writable properties. Shared between the interceptor and fast property accessor
483 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property,
484 v8::Handle<v8::Value> value)
486 QDeclarativeBinding *newBinding = 0;
488 if (value->IsFunction()) {
489 QDeclarativeContextData *context = engine->callingContext();
490 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
492 v8::Local<v8::StackTrace> trace =
493 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
494 v8::StackTrace::kScriptName));
495 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
496 int lineNumber = frame->GetLineNumber();
497 QString url = engine->toString(frame->GetScriptName());
499 QDeclarativePropertyCache::ValueTypeData valueTypeData;
500 newBinding = new QDeclarativeBinding(&function, object, context);
501 newBinding->setSourceLocation(url, lineNumber);
502 newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context));
503 newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
506 QDeclarativeAbstractBinding *oldBinding =
507 QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
509 oldBinding->destroy();
511 #define PROPERTY_STORE(cpptype, value) \
515 void *argv[] = { &o, 0, &status, &flags }; \
516 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
519 if (value->IsNull() && property->isQObject()) {
520 PROPERTY_STORE(QObject*, 0);
521 } else if (value->IsUndefined() && property->isResettable()) {
523 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
524 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
525 PROPERTY_STORE(QVariant, QVariant());
526 } else if (value->IsUndefined()) {
527 QString error = QLatin1String("Cannot assign [undefined] to ") +
528 QLatin1String(QMetaType::typeName(property->propType));
529 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
530 } else if (value->IsFunction()) {
531 // this is handled by the binding creation above
532 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
533 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
534 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
535 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
536 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
537 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
538 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
539 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
540 } else if (property->propType == QMetaType::QString && value->IsString()) {
541 PROPERTY_STORE(QString, engine->toString(value->ToString()));
544 if (property->isQList())
545 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
547 v = engine->toVariant(value, property->propType);
549 QDeclarativeContextData *context = engine->callingContext();
551 if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
552 const char *valueType = 0;
553 if (v.userType() == QVariant::Invalid) valueType = "null";
554 else valueType = QMetaType::typeName(v.userType());
556 QString error = QLatin1String("Cannot assign ") +
557 QLatin1String(valueType) +
558 QLatin1String(" to ") +
559 QLatin1String(QMetaType::typeName(property->propType));
560 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
565 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
566 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
568 if (engine->qobjectWrapper()->m_toStringString == property ||
569 engine->qobjectWrapper()->m_destroyString == property)
572 QDeclarativePropertyCache::Data local;
573 QDeclarativePropertyCache::Data *result = 0;
574 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
579 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
580 QDeclarativeData *ddata = QDeclarativeData::get(object);
581 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
585 if (!result->isWritable() && !result->isQList()) {
586 QString error = QLatin1String("Cannot assign to read-only property \"") +
587 engine->toString(property.string()) + QLatin1Char('\"');
588 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
592 StoreProperty(engine, object, result, value);
597 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
598 const v8::AccessorInfo &info)
600 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
602 if (resource->object.isNull())
603 return v8::Undefined();
605 QObject *object = resource->object;
607 QHashedV8String propertystring(property);
609 QV8Engine *v8engine = resource->engine;
610 v8::Handle<v8::Value> This = info.This();
611 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
612 QV8QObjectWrapper::IgnoreRevision);
613 if (!result.IsEmpty())
616 if (QV8Engine::startsWithUpper(property)) {
617 // Check for attached properties
618 QDeclarativeContextData *context = v8engine->callingContext();
619 QDeclarativeTypeNameCache::Data *data =
620 context && (context->imports)?context->imports->data(propertystring):0;
624 return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums);
625 } else if (data->typeNamespace) {
626 return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
631 return v8::Undefined();
634 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
635 v8::Local<v8::Value> value,
636 const v8::AccessorInfo &info)
638 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
640 if (resource->object.isNull())
643 QObject *object = resource->object;
645 QHashedV8String propertystring(property);
647 QV8Engine *v8engine = resource->engine;
648 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
651 QString error = QLatin1String("Cannot assign to non-existent property \"") +
652 v8engine->toString(property) + QLatin1Char('\"');
653 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
660 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
661 const v8::AccessorInfo &info)
663 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
665 if (resource->object.isNull())
666 return v8::Handle<v8::Integer>();
668 QV8Engine *engine = resource->engine;
669 QObject *object = resource->object;
671 QHashedV8String propertystring(property);
673 QDeclarativePropertyCache::Data local;
674 QDeclarativePropertyCache::Data *result = 0;
675 result = QDeclarativePropertyCache::property(engine->engine(), object, propertystring, local);
678 return v8::Handle<v8::Integer>();
679 else if (!result->isWritable() && !result->isQList())
680 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
682 return v8::Integer::New(v8::DontDelete);
685 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
687 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
689 if (resource->object.isNull())
690 return v8::Array::New();
692 QObject *object = resource->object;
696 QDeclarativeEnginePrivate *ep = resource->engine->engine()
697 ? QDeclarativeEnginePrivate::get(resource->engine->engine())
700 QDeclarativePropertyCache *cache = 0;
701 QDeclarativeData *ddata = QDeclarativeData::get(object);
703 cache = ddata->propertyCache;
706 cache = ep ? ep->cache(object) : 0;
708 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
710 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
711 const QMetaObject *mo = object->metaObject();
712 int pc = mo->propertyCount();
713 int po = mo->propertyOffset();
714 for (int i=po; i<pc; ++i)
715 result << QString::fromUtf8(mo->property(i).name());
718 result = cache->propertyNames();
721 v8::Local<v8::Array> rv = v8::Array::New(result.count());
723 for (int ii = 0; ii < result.count(); ++ii)
724 rv->Set(ii, resource->engine->toString(result.at(ii)));
729 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
730 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
731 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
732 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
733 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
734 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
735 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
737 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
738 const v8::AccessorInfo& info)
740 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
742 if (resource->object.isNull())
745 QObject *object = resource->object;
747 uint32_t data = info.Data()->Uint32Value();
748 int index = data & 0x7FFF; // So that we can use the same data for Setter and Getter
750 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
752 Q_ASSERT(ddata->propertyCache);
754 QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index);
757 Q_ASSERT(pdata->isWritable() || pdata->isQList());
759 StoreProperty(resource->engine, object, pdata, value);
762 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
763 const v8::AccessorInfo& info)
765 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
767 if (resource->object.isNull())
770 QV8Engine *v8engine = resource->engine;
772 QString error = QLatin1String("Cannot assign to read-only property \"") +
773 v8engine->toString(property) + QLatin1Char('\"');
774 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
777 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
779 Q_ASSERT(handle->IsObject());
781 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
785 QObject *object = resource->object;
787 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
789 ddata->v8object.Clear();
790 if (!object->parent() && !ddata->indestructible)
791 object->deleteLater();
795 qPersistentDispose(handle);
798 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
800 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
801 instance->v8object.Clear();
802 qPersistentDispose(handle);
805 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
809 Q_ASSERT(QDeclarativeData::get(object, false));
810 Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
813 if (constructor.IsEmpty()) {
814 v8::Local<v8::FunctionTemplate> ft;
816 QString toString = QLatin1String("toString");
817 QString destroy = QLatin1String("destroy");
819 // XXX TODO: Enables fast property accessors. These more than double the property access
820 // performance, but the cost of setting up this structure hasn't been measured so
821 // its not guarenteed that this is a win overall. We need to try and measure the cost.
822 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
823 Data *property = *iter;
824 if (property->isFunction() ||
825 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x0FFF ||
826 property->coreIndex == 0)
829 v8::AccessorGetter fastgetter = 0;
830 v8::AccessorSetter fastsetter = FastValueSetter;
831 if (!property->isWritable())
832 fastsetter = FastValueSetterReadOnly;
834 if (property->isQObject())
835 fastgetter = property->isDirect()?QObjectValueGetterDirect:QObjectValueGetter;
836 else if (property->propType == QMetaType::Int || property->isEnum())
837 fastgetter = property->isDirect()?IntValueGetterDirect:IntValueGetter;
838 else if (property->propType == QMetaType::Bool)
839 fastgetter = property->isDirect()?BoolValueGetterDirect:BoolValueGetter;
840 else if (property->propType == QMetaType::QString)
841 fastgetter = property->isDirect()?QStringValueGetterDirect:QStringValueGetter;
842 else if (property->propType == QMetaType::UInt)
843 fastgetter = property->isDirect()?UIntValueGetterDirect:UIntValueGetter;
844 else if (property->propType == QMetaType::Float)
845 fastgetter = property->isDirect()?FloatValueGetterDirect:FloatValueGetter;
846 else if (property->propType == QMetaType::Double)
847 fastgetter = property->isDirect()?DoubleValueGetterDirect:DoubleValueGetter;
850 int notifyIndex = property->notifyIndex;
851 if (property->isConstant()) notifyIndex = 0;
852 else if (notifyIndex == -1) notifyIndex = 0x0FFF;
853 uint32_t data = (notifyIndex & 0x0FFF) << 16 | property->coreIndex;
855 QString name = iter.key();
856 if (name == toString || name == destroy)
860 ft = v8::FunctionTemplate::New();
861 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
862 QV8QObjectWrapper::Setter,
863 QV8QObjectWrapper::Query,
865 QV8QObjectWrapper::Enumerator);
866 ft->InstanceTemplate()->SetHasExternalResource(true);
869 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
870 v8::Integer::NewFromUnsigned(data));
875 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
877 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
878 QV8QObjectWrapper::Setter,
879 QV8QObjectWrapper::Query,
881 QV8QObjectWrapper::Enumerator);
882 ft->InstanceTemplate()->SetHasExternalResource(true);
883 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
887 v8::Local<v8::Object> result = constructor->NewInstance();
888 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
889 result->SetExternalResource(r);
893 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
895 v8::Local<v8::Object> rv;
897 if (!ddata->propertyCache && engine->engine()) {
898 ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
899 if (ddata->propertyCache) ddata->propertyCache->addref();
902 if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) {
903 rv = ddata->propertyCache->newQObject(object, engine);
905 // XXX NewInstance() should be optimized
906 rv = m_constructor->NewInstance();
907 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
908 rv->SetExternalResource(r);
915 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
916 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
918 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
919 persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that
920 "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this
922 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create
923 an entry in the m_taintedObject hash where we store the handle and mark the object as
924 "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
925 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
926 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has
929 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
934 if (QObjectPrivate::get(object)->wasDeleted)
935 return v8::Undefined();
937 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
940 return v8::Undefined();
942 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
943 // We own the v8object
944 return v8::Local<v8::Object>::New(ddata->v8object);
945 } else if (ddata->v8object.IsEmpty() &&
946 (ddata->v8objectid == m_id || // We own the QObject
947 ddata->v8objectid == 0 || // No one owns the QObject
948 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
950 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
951 ddata->v8object = qPersistentNew<v8::Object>(rv);
952 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
953 ddata->v8objectid = m_id;
957 // If this object is tainted, we have to check to see if it is in our
958 // tainted object list
959 TaintedHash::Iterator iter =
960 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
961 bool found = iter != m_taintedObjects.end();
963 // If our tainted handle doesn't exist or has been collected, and there isn't
964 // a handle in the ddata, we can assume ownership of the ddata->v8object
965 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
966 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
967 ddata->v8object = qPersistentNew<v8::Object>(rv);
968 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
969 ddata->v8objectid = m_id;
973 m_taintedObjects.erase(iter);
978 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
979 iter = m_taintedObjects.insert(object, instance);
980 ddata->hasTaintedV8Object = true;
983 if ((*iter)->v8object.IsEmpty()) {
984 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
985 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
986 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
989 return v8::Local<v8::Object>::New((*iter)->v8object);
993 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
995 v8::ScriptOrigin origin = function->GetScriptOrigin();
996 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
998 // This is one of our special QObject method wrappers
999 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
1000 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
1002 if (data->IsArray()) {
1003 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
1004 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
1007 // In theory this can't fall through, but I suppose V8 might run out of memory or something
1010 return qMakePair((QObject *)0, -1);
1013 struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
1015 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
1016 ~QV8QObjectConnectionList();
1020 : needsDestroy(false) {}
1021 Connection(const Connection &other)
1022 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1023 Connection &operator=(const Connection &other) {
1024 thisObject = other.thisObject;
1025 function = other.function;
1026 needsDestroy = other.needsDestroy;
1030 v8::Persistent<v8::Object> thisObject;
1031 v8::Persistent<v8::Function> function;
1034 qPersistentDispose(thisObject);
1035 qPersistentDispose(function);
1041 struct ConnectionList : public QList<Connection> {
1042 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1043 int connectionsInUse;
1044 bool connectionsNeedClean;
1049 typedef QHash<int, ConnectionList> SlotHash;
1054 virtual void objectDestroyed(QObject *);
1055 virtual int qt_metacall(QMetaObject::Call, int, void **);
1058 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1059 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1063 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1065 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1066 QList<Connection> &connections = *iter;
1067 for (int ii = 0; ii < connections.count(); ++ii) {
1068 qPersistentDispose(connections[ii].thisObject);
1069 qPersistentDispose(connections[ii].function);
1075 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1077 engine->qobjectWrapper()->m_connections.remove(object);
1080 needsDestroy = true;
1085 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1087 if (method == QMetaObject::InvokeMetaMethod) {
1088 SlotHash::Iterator iter = slotHash.find(index);
1089 if (iter == slotHash.end())
1091 ConnectionList &connectionList = *iter;
1092 if (connectionList.isEmpty())
1097 connectionList.connectionsInUse++;
1099 QList<Connection> connections = connectionList;
1101 QMetaMethod method = data()->metaObject()->method(index);
1102 Q_ASSERT(method.methodType() == QMetaMethod::Signal);
1103 // XXX TODO: We should figure out a way to cache the parameter types to avoid resolving
1105 QList<QByteArray> params = method.parameterTypes();
1107 v8::HandleScope handle_scope;
1108 v8::Context::Scope scope(engine->context());
1110 QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
1111 int argCount = params.count();
1113 for (int ii = 0; ii < argCount; ++ii) {
1114 int type = QMetaType::type(params.at(ii).constData());
1115 if (type == qMetaTypeId<QVariant>()) {
1116 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1118 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1122 for (int ii = 0; ii < connections.count(); ++ii) {
1123 Connection &connection = connections[ii];
1124 if (connection.needsDestroy)
1126 if (connection.thisObject.IsEmpty()) {
1127 connection.function->Call(engine->global(), argCount, args.data());
1129 connection.function->Call(connection.thisObject, argCount, args.data());
1133 connectionList.connectionsInUse--;
1134 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1135 for (QList<Connection>::Iterator iter = connectionList.begin();
1136 iter != connectionList.end(); ) {
1137 if (iter->needsDestroy) {
1139 iter = connectionList.erase(iter);
1147 if (inUse == 0 && needsDestroy)
1154 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1156 if (args.Length() == 0)
1157 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1159 QV8Engine *engine = V8ENGINE();
1161 if (!args.This()->IsFunction())
1162 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1164 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1165 QObject *signalObject = signalInfo.first;
1166 int signalIndex = signalInfo.second;
1168 if (signalIndex == -1)
1169 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1172 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1174 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1175 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1177 v8::Local<v8::Value> functionValue;
1178 v8::Local<v8::Value> functionThisValue;
1180 if (args.Length() == 1) {
1181 functionValue = args[0];
1183 functionThisValue = args[0];
1184 functionValue = args[1];
1187 if (!functionValue->IsFunction())
1188 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1190 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1191 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1193 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1194 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1195 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1196 if (iter == connections.end())
1197 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1199 QV8QObjectConnectionList *connectionList = *iter;
1200 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1201 if (slotIter == connectionList->slotHash.end()) {
1202 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1203 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1206 QV8QObjectConnectionList::Connection connection;
1207 if (!functionThisValue.IsEmpty())
1208 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1209 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1211 slotIter->append(connection);
1213 return v8::Undefined();
1216 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1218 if (args.Length() == 0)
1219 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1221 QV8Engine *engine = V8ENGINE();
1223 if (!args.This()->IsFunction())
1224 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1226 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1227 QObject *signalObject = signalInfo.first;
1228 int signalIndex = signalInfo.second;
1230 if (signalIndex == -1)
1231 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1234 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1236 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1237 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1239 v8::Local<v8::Value> functionValue;
1240 v8::Local<v8::Value> functionThisValue;
1242 if (args.Length() == 1) {
1243 functionValue = args[0];
1245 functionThisValue = args[0];
1246 functionValue = args[1];
1249 if (!functionValue->IsFunction())
1250 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1252 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1253 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1255 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1256 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1257 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1258 if (iter == connectionsList.end())
1259 return v8::Undefined(); // Nothing to disconnect from
1261 QV8QObjectConnectionList *connectionList = *iter;
1262 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1263 if (slotIter == connectionList->slotHash.end())
1264 return v8::Undefined(); // Nothing to disconnect from
1266 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1268 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1269 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1271 if (functionData.second != -1) {
1272 // This is a QObject function wrapper
1273 for (int ii = 0; ii < connections.count(); ++ii) {
1274 QV8QObjectConnectionList::Connection &connection = connections[ii];
1276 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1277 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1279 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1280 if (connectedFunctionData == functionData) {
1282 if (connections.connectionsInUse) {
1283 connection.needsDestroy = true;
1285 connection.dispose();
1286 connections.removeAt(ii);
1288 return v8::Undefined();
1294 // This is a normal JS function
1295 for (int ii = 0; ii < connections.count(); ++ii) {
1296 QV8QObjectConnectionList::Connection &connection = connections[ii];
1297 if (connection.function->StrictEquals(function) &&
1298 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1299 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1301 if (connections.connectionsInUse) {
1302 connection.needsDestroy = true;
1304 connection.dispose();
1305 connections.removeAt(ii);
1307 return v8::Undefined();
1312 return v8::Undefined();
1316 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1318 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1320 Only searches for real properties of \a object (including methods), not attached properties etc.
1324 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1326 Set the \a property of \a object to \a value.
1328 Returns true if the property was "set" - even if this results in an exception being thrown -
1329 and false if the object has no such property.
1331 Only searches for real properties of \a object (including methods), not attached properties etc.
1337 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1338 int Length() const { return _length; }
1339 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1343 v8::Handle<v8::Object> *_args;
1347 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1348 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1352 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1353 args[0].initAsType(returnType);
1355 for (int ii = 0; ii < argCount; ++ii)
1356 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1358 QVarLengthArray<void *, 9> argData(args.count());
1359 for (int ii = 0; ii < args.count(); ++ii)
1360 argData[ii] = args[ii].dataPtr();
1362 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1364 return args[0].toValue(engine);
1366 } else if (returnType != 0) {
1368 MetaCallArgument arg;
1369 arg.initAsType(returnType);
1371 void *args[] = { arg.dataPtr() };
1373 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1375 return arg.toValue(engine);
1379 void *args[] = { 0 };
1380 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1381 return v8::Undefined();
1386 static int EnumType(const QMetaObject *meta, const QString &strname)
1388 QByteArray str = strname.toUtf8();
1391 int scopeIdx = str.lastIndexOf("::");
1392 if (scopeIdx != -1) {
1393 scope = str.left(scopeIdx);
1394 name = str.mid(scopeIdx + 2);
1398 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1399 QMetaEnum m = meta->enumerator(i);
1400 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1401 return QVariant::Int;
1403 return QVariant::Invalid;
1407 Returns the match score for converting \a actual to be of type \a conversionType. A
1408 zero score means "perfect match" whereas a higher score is worse.
1410 The conversion table is copied out of the QtScript callQtMethod() function.
1412 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
1413 const QByteArray &conversionTypeName)
1415 if (actual->IsNumber()) {
1416 switch (conversionType) {
1417 case QMetaType::Double:
1419 case QMetaType::Float:
1421 case QMetaType::LongLong:
1422 case QMetaType::ULongLong:
1424 case QMetaType::Long:
1425 case QMetaType::ULong:
1427 case QMetaType::Int:
1428 case QMetaType::UInt:
1430 case QMetaType::Short:
1431 case QMetaType::UShort:
1434 case QMetaType::Char:
1435 case QMetaType::UChar:
1440 } else if (actual->IsString()) {
1441 switch (conversionType) {
1442 case QMetaType::QString:
1447 } else if (actual->IsBoolean()) {
1448 switch (conversionType) {
1449 case QMetaType::Bool:
1454 } else if (actual->IsDate()) {
1455 switch (conversionType) {
1456 case QMetaType::QDateTime:
1458 case QMetaType::QDate:
1460 case QMetaType::QTime:
1465 } else if (actual->IsRegExp()) {
1466 switch (conversionType) {
1467 case QMetaType::QRegExp:
1472 } else if (actual->IsArray()) {
1473 switch (conversionType) {
1474 case QMetaType::QStringList:
1475 case QMetaType::QVariantList:
1480 } else if (actual->IsNull()) {
1481 switch (conversionType) {
1482 case QMetaType::VoidStar:
1483 case QMetaType::QObjectStar:
1486 if (!conversionTypeName.endsWith('*'))
1491 } else if (actual->IsObject()) {
1492 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1494 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1495 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1496 switch (conversionType) {
1497 case QMetaType::QObjectStar:
1502 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1503 if (conversionType == qMetaTypeId<QVariant>())
1505 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1518 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1524 int classInfoCount, classInfoData;
1525 int methodCount, methodData;
1528 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1531 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1533 QByteArray sig = m.signature();
1534 int paren = sig.indexOf('(');
1538 return sig.left(paren);
1542 Returns the next related method, if one, or 0.
1544 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object,
1545 const QDeclarativePropertyCache::Data *current,
1546 QDeclarativePropertyCache::Data &dummy)
1548 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1549 if (current->relatedIndex == -1)
1553 return cache->method(current->relatedIndex);
1555 const QMetaObject *mo = object->metaObject();
1556 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1558 while (methodOffset > current->relatedIndex) {
1559 mo = mo->superClass();
1560 methodOffset -= QMetaObject_methods(mo);
1563 QMetaMethod method = mo->method(current->relatedIndex);
1566 // Look for overloaded methods
1567 QByteArray methodName = QMetaMethod_name(method);
1568 for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1569 if (methodName == QMetaMethod_name(mo->method(ii))) {
1570 dummy.relatedIndex = ii;
1579 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
1580 QV8Engine *engine, CallArgs &callArgs)
1582 if (data.hasArguments()) {
1584 QMetaMethod m = object->metaObject()->method(data.coreIndex);
1585 QList<QByteArray> argTypeNames = m.parameterTypes();
1586 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1589 for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1590 argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1591 if (argTypes[ii] == QVariant::Invalid)
1592 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1593 if (argTypes[ii] == QVariant::Invalid) {
1594 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1595 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1596 return v8::Handle<v8::Value>();
1600 if (argTypes.count() > callArgs.Length()) {
1601 QString error = QLatin1String("Insufficient arguments");
1602 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1603 return v8::Handle<v8::Value>();
1606 return CallMethod(object, data.coreIndex, data.propType, argTypes.count(),
1607 argTypes.data(), engine, callArgs);
1611 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1617 Resolve the overloaded method to call. The algorithm works conceptually like this:
1618 1. Resolve the set of overloads it is *possible* to call.
1619 Impossible overloads include those that have too many parameters or have parameters
1621 2. Filter the set of overloads to only contain those with the closest number of
1623 For example, if we are called with 3 parameters and there are 2 overloads that
1624 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1625 3. Find the best remaining overload based on its match score.
1626 If two or more overloads have the same match score, call the last one. The match
1627 score is constructed by adding the matchScore() result for each of the parameters.
1629 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data,
1630 QV8Engine *engine, CallArgs &callArgs)
1632 int argumentCount = callArgs.Length();
1634 const QDeclarativePropertyCache::Data *best = 0;
1635 int bestParameterScore = INT_MAX;
1636 int bestMatchScore = INT_MAX;
1638 QDeclarativePropertyCache::Data dummy;
1639 const QDeclarativePropertyCache::Data *attempt = &data;
1642 QList<QByteArray> methodArgTypeNames;
1644 if (attempt->hasArguments())
1645 methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1647 int methodArgumentCount = methodArgTypeNames.count();
1649 if (methodArgumentCount > argumentCount)
1650 continue; // We don't have sufficient arguments to call this method
1652 int methodParameterScore = argumentCount - methodArgumentCount;
1653 if (methodParameterScore > bestParameterScore)
1654 continue; // We already have a better option
1656 int methodMatchScore = 0;
1657 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1659 bool unknownArgument = false;
1660 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1661 methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1662 if (methodArgTypes[ii] == QVariant::Invalid)
1663 methodArgTypes[ii] = EnumType(object->metaObject(),
1664 QString::fromLatin1(methodArgTypeNames.at(ii)));
1665 if (methodArgTypes[ii] == QVariant::Invalid) {
1666 unknownArgument = true;
1669 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1671 if (unknownArgument)
1672 continue; // We don't understand all the parameters
1674 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1676 bestParameterScore = methodParameterScore;
1677 bestMatchScore = methodMatchScore;
1680 if (bestParameterScore == 0 && bestMatchScore == 0)
1681 break; // We can't get better than that
1683 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1686 return CallPrecise(object, *best, engine, callArgs);
1688 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1689 const QDeclarativePropertyCache::Data *candidate = &data;
1691 error += QLatin1String("\n ") +
1692 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1693 candidate = RelatedMethod(object, candidate, dummy);
1696 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1697 return v8::Handle<v8::Value>();
1701 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1705 QString objectName = object->objectName();
1707 result += QString::fromUtf8(object->metaObject()->className());
1708 result += QLatin1String("(0x");
1709 result += QString::number((quintptr)object,16);
1711 if (!objectName.isEmpty()) {
1712 result += QLatin1String(", \"");
1713 result += objectName;
1714 result += QLatin1Char('\"');
1717 result += QLatin1Char(')');
1719 result = QLatin1String("null");
1722 return engine->toString(result);
1725 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1727 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1728 if (!ddata || ddata->indestructible) {
1729 const char *error = "Invalid attempt to destroy() an indestructible object";
1730 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1731 return v8::Undefined();
1736 delay = args->Get(0)->Uint32Value();
1739 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1741 object->deleteLater();
1743 return v8::Undefined();
1746 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1748 // object, index, qmlglobal, argCount, args
1749 Q_ASSERT(args.Length() == 5);
1750 Q_ASSERT(args[0]->IsObject());
1752 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1755 return v8::Undefined();
1757 int argCount = args[3]->Int32Value();
1758 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1760 // Special hack to return info about this closure.
1761 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1762 v8::Local<v8::Array> data = v8::Array::New(2);
1763 data->Set(0, args[0]);
1764 data->Set(1, args[1]);
1768 QObject *object = resource->object;
1769 int index = args[1]->Int32Value();
1772 return v8::Undefined();
1775 // Builtin functions
1776 if (index == QOBJECT_TOSTRING_INDEX) {
1777 return ToString(resource->engine, object, argCount, arguments);
1778 } else if (index == QOBJECT_DESTROY_INDEX) {
1779 return Destroy(resource->engine, object, argCount, arguments);
1781 return v8::Undefined();
1785 QDeclarativePropertyCache::Data method;
1787 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1788 if (ddata->propertyCache) {
1789 QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1791 return v8::Undefined();
1796 if (method.coreIndex == -1) {
1797 QMetaMethod mm = object->metaObject()->method(index);
1798 method.load(object->metaObject()->method(index));
1800 if (method.coreIndex == -1)
1801 return v8::Undefined();
1804 if (method.isV8Function()) {
1805 v8::Handle<v8::Value> rv;
1806 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1808 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1809 resource->engine->contextWrapper()->context(qmlglobal),
1811 QDeclarativeV8Function *funcptr = &func;
1813 void *args[] = { 0, &funcptr };
1814 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1816 if (rv.IsEmpty()) return v8::Undefined();
1820 CallArgs callArgs(argCount, &arguments);
1821 if (method.relatedIndex == -1) {
1822 return CallPrecise(object, method, resource->engine, callArgs);
1824 return CallOverloaded(object, method, resource->engine, callArgs);
1828 MetaCallArgument::MetaCallArgument()
1829 : type(QVariant::Invalid)
1833 MetaCallArgument::~MetaCallArgument()
1838 void MetaCallArgument::cleanup()
1840 if (type == QMetaType::QString) {
1841 qstringPtr->~QString();
1842 } else if (type == -1 || type == QMetaType::QVariant) {
1843 qvariantPtr->~QVariant();
1844 } else if (type == qMetaTypeId<QJSValue>()) {
1845 qjsValuePtr->~QJSValue();
1846 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1847 qlistPtr->~QList<QObject *>();
1851 void *MetaCallArgument::dataPtr()
1854 return qvariantPtr->data();
1856 return (void *)&allocData;
1859 void MetaCallArgument::initAsType(int callType)
1861 if (type != 0) { cleanup(); type = 0; }
1862 if (callType == 0) return;
1864 if (callType == qMetaTypeId<QJSValue>()) {
1865 qjsValuePtr = new (&allocData) QJSValue();
1867 } else if (callType == QMetaType::Int ||
1868 callType == QMetaType::UInt ||
1869 callType == QMetaType::Bool ||
1870 callType == QMetaType::Double ||
1871 callType == QMetaType::Float) {
1873 } else if (callType == QMetaType::QObjectStar) {
1876 } else if (callType == QMetaType::QString) {
1877 qstringPtr = new (&allocData) QString();
1879 } else if (callType == QMetaType::QVariant) {
1881 qvariantPtr = new (&allocData) QVariant();
1882 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1884 qlistPtr = new (&allocData) QList<QObject *>();
1885 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1887 handlePtr = new (&allocData) QDeclarativeV8Handle;
1890 qvariantPtr = new (&allocData) QVariant(callType, (void *)0);
1894 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1896 if (type != 0) { cleanup(); type = 0; }
1898 if (callType == qMetaTypeId<QJSValue>()) {
1899 qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value)));
1900 type = qMetaTypeId<QJSValue>();
1901 } else if (callType == QMetaType::Int) {
1902 intValue = quint32(value->Int32Value());
1904 } else if (callType == QMetaType::UInt) {
1905 intValue = quint32(value->Uint32Value());
1907 } else if (callType == QMetaType::Bool) {
1908 boolValue = value->BooleanValue();
1910 } else if (callType == QMetaType::Double) {
1911 doubleValue = double(value->NumberValue());
1913 } else if (callType == QMetaType::Float) {
1914 floatValue = float(value->NumberValue());
1916 } else if (callType == QMetaType::QString) {
1917 if (value->IsNull() || value->IsUndefined())
1918 qstringPtr = new (&allocData) QString();
1920 qstringPtr = new (&allocData) QString(engine->toString(value->ToString()));
1922 } else if (callType == QMetaType::QObjectStar) {
1923 qobjectPtr = engine->toQObject(value);
1925 } else if (callType == qMetaTypeId<QVariant>()) {
1926 qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1));
1928 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1929 qlistPtr = new (&allocData) QList<QObject *>();
1930 if (value->IsArray()) {
1931 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1932 uint32_t length = array->Length();
1933 for (uint32_t ii = 0; ii < length; ++ii)
1934 qlistPtr->append(engine->toQObject(array->Get(ii)));
1936 qlistPtr->append(engine->toQObject(value));
1939 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1940 handlePtr = new (&allocData) QDeclarativeV8Handle(QDeclarativeV8Handle::fromHandle(value));
1943 qvariantPtr = new (&allocData) QVariant();
1946 QDeclarativeEnginePrivate *ep = engine->engine() ? QDeclarativeEnginePrivate::get(engine->engine()) : 0;
1947 QVariant v = engine->toVariant(value, -1);
1949 if (v.userType() == callType) {
1951 } else if (v.canConvert((QVariant::Type)callType)) {
1953 qvariantPtr->convert((QVariant::Type)callType);
1954 } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) {
1955 QObject *obj = ep->toQObject(v);
1958 const QMetaObject *objMo = obj->metaObject();
1959 while (objMo && objMo != mo) objMo = objMo->superClass();
1960 if (!objMo) obj = 0;
1963 *qvariantPtr = QVariant(callType, &obj);
1965 *qvariantPtr = QVariant(callType, (void *)0);
1970 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1972 if (type == qMetaTypeId<QJSValue>()) {
1973 return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine);
1974 } else if (type == QMetaType::Int) {
1975 return v8::Integer::New(int(intValue));
1976 } else if (type == QMetaType::UInt) {
1977 return v8::Integer::NewFromUnsigned(intValue);
1978 } else if (type == QMetaType::Bool) {
1979 return v8::Boolean::New(boolValue);
1980 } else if (type == QMetaType::Double) {
1981 return v8::Number::New(doubleValue);
1982 } else if (type == QMetaType::Float) {
1983 return v8::Number::New(floatValue);
1984 } else if (type == QMetaType::QString) {
1985 return engine->toString(*qstringPtr);
1986 } else if (type == QMetaType::QObjectStar) {
1987 QObject *object = qobjectPtr;
1989 QDeclarativeData::get(object, true)->setImplicitDestructible();
1990 return engine->newQObject(object);
1991 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1992 // XXX Can this be made more by using Array as a prototype and implementing
1993 // directly against QList<QObject*>?
1994 QList<QObject *> &list = *qlistPtr;
1995 v8::Local<v8::Array> array = v8::Array::New(list.count());
1996 for (int ii = 0; ii < list.count(); ++ii)
1997 array->Set(ii, engine->newQObject(list.at(ii)));
1999 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
2000 return handlePtr->toHandle();
2001 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
2002 QVariant value = *qvariantPtr;
2003 v8::Handle<v8::Value> rv = engine->fromVariant(value);
2004 if (QObject *object = engine->toQObject(rv))
2005 QDeclarativeData::get(object, true)->setImplicitDestructible();
2008 return v8::Undefined();