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>
52 #include <QtScript/qscriptvalue.h>
53 #include <QtCore/qvarlengtharray.h>
54 #include <QtCore/qtimer.h>
55 #include <QtCore/qatomic.h>
59 Q_DECLARE_METATYPE(QScriptValue);
60 Q_DECLARE_METATYPE(QDeclarativeV8Handle);
63 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
64 // The code in this file does not violate strict aliasing, but GCC thinks it does
65 // so turn off the warnings for us to have a clean build
66 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
70 #define QOBJECT_TOSTRING_INDEX -2
71 #define QOBJECT_DESTROY_INDEX -3
73 // XXX TODO: Need to review all calls to QDeclarativeEngine *engine() to confirm QObjects work
74 // correctly in a worker thread
76 class QV8QObjectResource : public QV8ObjectResource
78 V8_RESOURCE_TYPE(QObjectType);
81 QV8QObjectResource(QV8Engine *engine, QObject *object);
83 QDeclarativeGuard<QObject> object;
86 class QV8QObjectInstance : public QDeclarativeGuard<QObject>
89 QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w)
90 : QDeclarativeGuard<QObject>(o), wrapper(w)
96 qPersistentDispose(v8object);
99 virtual void objectDestroyed(QObject *o)
102 wrapper->m_taintedObjects.remove(o);
106 v8::Persistent<v8::Object> v8object;
107 QV8QObjectWrapper *wrapper;
111 struct MetaCallArgument {
112 inline MetaCallArgument();
113 inline ~MetaCallArgument();
114 inline void *dataPtr();
116 inline void initAsType(int type);
117 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
118 inline v8::Handle<v8::Value> toValue(QV8Engine *);
121 MetaCallArgument(const MetaCallArgument &);
123 inline void cleanup();
125 char data[4 * sizeof(void *)];
131 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
132 : QV8ObjectResource(engine), object(object)
136 static QAtomicInt objectIdCounter(1);
138 QV8QObjectWrapper::QV8QObjectWrapper()
139 : m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1))
143 QV8QObjectWrapper::~QV8QObjectWrapper()
145 for (TaintedHash::Iterator iter = m_taintedObjects.begin();
146 iter != m_taintedObjects.end();
148 (*iter)->wrapper = 0;
150 m_taintedObjects.clear();
153 void QV8QObjectWrapper::destroy()
155 qDeleteAll(m_connections);
156 m_connections.clear();
158 qPersistentDispose(m_hiddenObject);
159 qPersistentDispose(m_destroySymbol);
160 qPersistentDispose(m_toStringSymbol);
161 qPersistentDispose(m_methodConstructor);
162 qPersistentDispose(m_constructor);
165 #define FAST_VALUE_GETTER(name, cpptype, defaultvalue, constructor) \
166 static v8::Handle<v8::Value> name ## ValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) \
168 v8::Handle<v8::Object> This = info.This(); \
169 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
171 if (resource->object.isNull()) return v8::Undefined(); \
173 QObject *object = resource->object; \
175 uint32_t data = info.Data()->Uint32Value(); \
176 int index = data & 0x7FFF; \
177 int notify = (data & 0x0FFF0000) >> 16; \
178 if (notify == 0x0FFF) notify = -1; \
180 QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
181 if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
182 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
183 ep->capturedProperties << CapturedProperty(object, index, notify); \
186 cpptype value = defaultvalue; \
187 void *args[] = { &value, 0 }; \
188 QMetaObject::metacall(object, QMetaObject::ReadProperty, index, args); \
190 return constructor(value); \
192 static v8::Handle<v8::Value> name ## ValueGetterDirect(v8::Local<v8::String>, const v8::AccessorInfo &info) \
194 v8::Handle<v8::Object> This = info.This(); \
195 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); \
197 if (resource->object.isNull()) return v8::Undefined(); \
199 QObject *object = resource->object; \
201 uint32_t data = info.Data()->Uint32Value(); \
202 int index = data & 0x7FFF; \
203 int notify = (data & 0x0FFF0000) >> 16; \
204 if (notify == 0x0FFF) notify = -1; \
206 QDeclarativeEnginePrivate *ep = resource->engine->engine()?QDeclarativeEnginePrivate::get(resource->engine->engine()):0; \
207 if (ep && notify /* 0 means constant */ && ep->captureProperties) { \
208 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
209 ep->capturedProperties << CapturedProperty(object, index, notify); \
212 cpptype value = defaultvalue; \
213 void *args[] = { &value, 0 }; \
214 object->qt_metacall(QMetaObject::ReadProperty, index, args); \
216 return constructor(value); \
219 #define CREATE_FUNCTION \
220 "(function(method) { "\
221 "return (function(object, data, qmlglobal) { "\
222 "return (function() { "\
223 "return method(object, data, qmlglobal, arguments.length, arguments); "\
229 static quint32 toStringHash = -1;
230 static quint32 destroyHash = -1;
232 void QV8QObjectWrapper::init(QV8Engine *engine)
236 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
237 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
238 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
240 m_toStringString = QHashedV8String(m_toStringSymbol);
241 m_destroyString = QHashedV8String(m_destroySymbol);
243 toStringHash = m_toStringString.hash();
244 destroyHash = m_destroyString.hash();
247 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
248 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
249 ft->InstanceTemplate()->SetHasExternalResource(true);
250 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
253 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
254 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin);
255 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
256 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
257 v8::Handle<v8::Value> args[] = { invokeFn };
258 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
259 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
263 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
264 prototype->Set(v8::String::New("connect"), V8FUNCTION(Connect, engine));
265 prototype->Set(v8::String::New("disconnect"), V8FUNCTION(Disconnect, engine));
269 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
271 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
274 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
276 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
277 return r?r->object:0;
280 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
281 QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
283 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
284 return static_cast<QV8QObjectResource *>(r)->object;
287 // Load value properties
288 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
289 const QDeclarativePropertyCache::Data &property)
291 Q_ASSERT(!property.isFunction());
293 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
294 if (property.propType == QMetaType:: metatype) { \
295 cpptype type = cpptype(); \
296 void *args[] = { &type, 0 }; \
297 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \
298 return constructor(type); \
301 if (property.isQObject()) {
303 void *args[] = { &rv, 0 };
304 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
305 return engine->newQObject(rv);
306 } else if (property.isQList()) {
307 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
308 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
309 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
310 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
311 else PROPERTY_LOAD(QString, QString, engine->toString)
312 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
313 else PROPERTY_LOAD(Float, float, v8::Number::New)
314 else PROPERTY_LOAD(Double, double, v8::Number::New)
315 else if(property.isV8Handle()) {
316 QDeclarativeV8Handle handle;
317 void *args[] = { &handle, 0 };
318 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
319 return reinterpret_cast<v8::Handle<v8::Value> &>(handle);
320 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)) {
321 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
322 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
324 return engine->newValueType(object, property.coreIndex, valueType);
327 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
328 return engine->fromVariant(var);
333 static v8::Handle<v8::Value> LoadPropertyDirect(QV8Engine *engine, QObject *object,
334 const QDeclarativePropertyCache::Data &property)
336 Q_ASSERT(!property.isFunction());
338 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
339 if (property.propType == QMetaType:: metatype) { \
340 cpptype type = cpptype(); \
341 void *args[] = { &type, 0 }; \
342 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); \
343 return constructor(type); \
346 if (property.isQObject()) {
348 void *args[] = { &rv, 0 };
349 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
350 return engine->newQObject(rv);
351 } else if (property.isQList()) {
352 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
353 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
354 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Integer::New)
355 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
356 else PROPERTY_LOAD(QString, QString, engine->toString)
357 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
358 else PROPERTY_LOAD(Float, float, v8::Number::New)
359 else PROPERTY_LOAD(Double, double, v8::Number::New)
360 else if(property.isV8Handle()) {
361 QDeclarativeV8Handle handle;
362 void *args[] = { &handle, 0 };
363 object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args);
364 return reinterpret_cast<v8::Handle<v8::Value> &>(handle);
365 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)) {
366 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
367 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
369 return engine->newValueType(object, property.coreIndex, valueType);
372 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
373 return engine->fromVariant(var);
378 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
379 v8::Handle<v8::Value> *objectHandle,
380 const QHashedV8String &property,
381 QV8QObjectWrapper::RevisionMode revisionMode)
383 // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these
384 // will be a faster way of creating QObject method objects.
385 struct MethodClosure {
386 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
387 v8::Handle<v8::Value> *objectHandle,
389 v8::Handle<v8::Value> argv[] = {
390 objectHandle?*objectHandle:engine->newQObject(object),
391 v8::Integer::New(index)
393 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
395 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
396 v8::Handle<v8::Value> *objectHandle,
398 v8::Handle<v8::Value> argv[] = {
399 objectHandle?*objectHandle:engine->newQObject(object),
400 v8::Integer::New(index),
401 v8::Context::GetCallingQmlGlobal()
403 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
408 // Comparing the hash first actually makes a measurable difference here, at least on x86
409 quint32 hash = property.hash();
410 if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) {
411 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
412 } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) {
413 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
417 QDeclarativePropertyCache::Data local;
418 QDeclarativePropertyCache::Data *result = 0;
420 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
421 if (ddata && ddata->propertyCache)
422 result = ddata->propertyCache->property(property);
424 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
428 return v8::Handle<v8::Value>();
430 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
431 QDeclarativeData *ddata = QDeclarativeData::get(object);
432 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
433 return v8::Handle<v8::Value>();
436 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
438 if (result->isFunction()) {
439 if (result->isVMEFunction()) {
440 return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
441 } else if (result->isV8Function()) {
442 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
444 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
448 QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
449 if (ep && ep->captureProperties && !result->isConstant()) {
450 if (result->coreIndex == 0)
451 ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
453 ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
456 if (result->isDirect()) {
457 return LoadPropertyDirect(engine, object, *result);
459 return LoadProperty(engine, object, *result);
463 // Setter for writable properties. Shared between the interceptor and fast property accessor
464 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property,
465 v8::Handle<v8::Value> value)
467 QDeclarativeBinding *newBinding = 0;
469 if (value->IsFunction()) {
470 QDeclarativeContextData *context = engine->callingContext();
471 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
473 v8::Local<v8::StackTrace> trace =
474 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
475 v8::StackTrace::kScriptName));
476 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
477 int lineNumber = frame->GetLineNumber();
478 QString url = engine->toString(frame->GetScriptName());
480 QDeclarativePropertyCache::ValueTypeData valueTypeData;
481 newBinding = new QDeclarativeBinding(&function, object, context);
482 newBinding->setSourceLocation(url, lineNumber);
483 newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context));
484 newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
487 QDeclarativeAbstractBinding *oldBinding =
488 QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
490 oldBinding->destroy();
492 #define PROPERTY_STORE(cpptype, value) \
496 void *argv[] = { &o, 0, &status, &flags }; \
497 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
500 if (value->IsNull() && property->isQObject()) {
501 PROPERTY_STORE(QObject*, 0);
502 } else if (value->IsUndefined() && property->isResettable()) {
504 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
505 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
506 PROPERTY_STORE(QVariant, QVariant());
507 } else if (value->IsUndefined()) {
508 QString error = QLatin1String("Cannot assign [undefined] to ") +
509 QLatin1String(QMetaType::typeName(property->propType));
510 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
511 } else if (value->IsFunction()) {
512 // this is handled by the binding creation above
513 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
514 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
515 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
516 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
517 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
518 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
519 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
520 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
521 } else if (property->propType == QMetaType::QString && value->IsString()) {
522 PROPERTY_STORE(QString, engine->toString(value->ToString()));
525 if (property->isQList())
526 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
528 v = engine->toVariant(value, property->propType);
530 QDeclarativeContextData *context = engine->callingContext();
532 if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
533 const char *valueType = 0;
534 if (v.userType() == QVariant::Invalid) valueType = "null";
535 else valueType = QMetaType::typeName(v.userType());
537 QString error = QLatin1String("Cannot assign ") +
538 QLatin1String(valueType) +
539 QLatin1String(" to ") +
540 QLatin1String(QMetaType::typeName(property->propType));
541 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
546 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property,
547 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
549 if (engine->qobjectWrapper()->m_toStringString == property ||
550 engine->qobjectWrapper()->m_destroyString == property)
553 QDeclarativePropertyCache::Data local;
554 QDeclarativePropertyCache::Data *result = 0;
555 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
560 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
561 QDeclarativeData *ddata = QDeclarativeData::get(object);
562 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
566 if (!result->isWritable() && !result->isQList()) {
567 QString error = QLatin1String("Cannot assign to read-only property \"") +
568 engine->toString(property.string()) + QLatin1Char('\"');
569 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
573 StoreProperty(engine, object, result, value);
578 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
579 const v8::AccessorInfo &info)
581 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
583 if (resource->object.isNull())
584 return v8::Undefined();
586 QObject *object = resource->object;
588 QHashedV8String propertystring(property);
590 QV8Engine *v8engine = resource->engine;
591 v8::Handle<v8::Value> This = info.This();
592 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring,
593 QV8QObjectWrapper::IgnoreRevision);
594 if (!result.IsEmpty())
597 if (QV8Engine::startsWithUpper(property)) {
598 // Check for attached properties
599 QDeclarativeContextData *context = v8engine->callingContext();
600 QDeclarativeTypeNameCache::Data *data =
601 context && (context->imports)?context->imports->data(propertystring):0;
605 return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums);
606 } else if (data->typeNamespace) {
607 return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
612 return v8::Undefined();
615 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
616 v8::Local<v8::Value> value,
617 const v8::AccessorInfo &info)
619 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
621 if (resource->object.isNull())
624 QObject *object = resource->object;
626 QHashedV8String propertystring(property);
628 QV8Engine *v8engine = resource->engine;
629 bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision);
632 QString error = QLatin1String("Cannot assign to non-existent property \"") +
633 v8engine->toString(property) + QLatin1Char('\"');
634 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
641 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
642 const v8::AccessorInfo &info)
644 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
646 if (resource->object.isNull())
647 return v8::Handle<v8::Integer>();
649 QV8Engine *engine = resource->engine;
650 QObject *object = resource->object;
652 QHashedV8String propertystring(property);
654 QDeclarativePropertyCache::Data local;
655 QDeclarativePropertyCache::Data *result = 0;
656 result = QDeclarativePropertyCache::property(engine->engine(), object, propertystring, local);
659 return v8::Handle<v8::Integer>();
660 else if (!result->isWritable() && !result->isQList())
661 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
663 return v8::Integer::New(v8::DontDelete);
666 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
668 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
670 if (resource->object.isNull())
671 return v8::Array::New();
673 QObject *object = resource->object;
677 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine());
679 QDeclarativePropertyCache *cache = 0;
680 QDeclarativeData *ddata = QDeclarativeData::get(object);
682 cache = ddata->propertyCache;
685 cache = ep->cache(object);
687 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
689 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
690 const QMetaObject *mo = object->metaObject();
691 int pc = mo->propertyCount();
692 int po = mo->propertyOffset();
693 for (int i=po; i<pc; ++i)
694 result << QString::fromUtf8(mo->property(i).name());
697 result = cache->propertyNames();
700 v8::Local<v8::Array> rv = v8::Array::New(result.count());
702 for (int ii = 0; ii < result.count(); ++ii)
703 rv->Set(ii, resource->engine->toString(result.at(ii)));
708 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
709 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
710 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
711 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
712 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
713 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
714 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
716 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
717 const v8::AccessorInfo& info)
719 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
721 if (resource->object.isNull())
724 QObject *object = resource->object;
726 uint32_t data = info.Data()->Uint32Value();
727 int index = data & 0x7FFF; // So that we can use the same data for Setter and Getter
729 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
731 Q_ASSERT(ddata->propertyCache);
733 QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index);
736 Q_ASSERT(pdata->isWritable() || pdata->isQList());
738 StoreProperty(resource->engine, object, pdata, value);
741 static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>,
742 const v8::AccessorInfo& info)
744 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This());
746 if (resource->object.isNull())
749 QV8Engine *v8engine = resource->engine;
751 QString error = QLatin1String("Cannot assign to read-only property \"") +
752 v8engine->toString(property) + QLatin1Char('\"');
753 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
756 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
758 Q_ASSERT(handle->IsObject());
760 QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject());
764 QObject *object = resource->object;
766 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
768 ddata->v8object.Clear();
769 if (!object->parent() && !ddata->indestructible)
770 object->deleteLater();
774 qPersistentDispose(handle);
777 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
779 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
780 instance->v8object.Clear();
781 qPersistentDispose(handle);
784 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
788 Q_ASSERT(QDeclarativeData::get(object, false));
789 Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
792 if (constructor.IsEmpty()) {
793 v8::Local<v8::FunctionTemplate> ft;
795 QString toString = QLatin1String("toString");
796 QString destroy = QLatin1String("destroy");
798 // XXX TODO: Enables fast property accessors. These more than double the property access
799 // performance, but the cost of setting up this structure hasn't been measured so
800 // its not guarenteed that this is a win overall. We need to try and measure the cost.
801 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
802 Data *property = *iter;
803 if (property->isFunction() ||
804 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x0FFF ||
805 property->coreIndex == 0)
808 v8::AccessorGetter fastgetter = 0;
809 v8::AccessorSetter fastsetter = FastValueSetter;
810 if (!property->isWritable())
811 fastsetter = FastValueSetterReadOnly;
813 if (property->isQObject())
814 fastgetter = property->isDirect()?QObjectValueGetterDirect:QObjectValueGetter;
815 else if (property->propType == QMetaType::Int || property->isEnum())
816 fastgetter = property->isDirect()?IntValueGetterDirect:IntValueGetter;
817 else if (property->propType == QMetaType::Bool)
818 fastgetter = property->isDirect()?BoolValueGetterDirect:BoolValueGetter;
819 else if (property->propType == QMetaType::QString)
820 fastgetter = property->isDirect()?QStringValueGetterDirect:QStringValueGetter;
821 else if (property->propType == QMetaType::UInt)
822 fastgetter = property->isDirect()?UIntValueGetterDirect:UIntValueGetter;
823 else if (property->propType == QMetaType::Float)
824 fastgetter = property->isDirect()?FloatValueGetterDirect:FloatValueGetter;
825 else if (property->propType == QMetaType::Double)
826 fastgetter = property->isDirect()?DoubleValueGetterDirect:DoubleValueGetter;
829 int notifyIndex = property->notifyIndex;
830 if (property->isConstant()) notifyIndex = 0;
831 else if (notifyIndex == -1) notifyIndex = 0x0FFF;
832 uint32_t data = (notifyIndex & 0x0FFF) << 16 | property->coreIndex;
834 QString name = iter.key();
835 if (name == toString || name == destroy)
839 ft = v8::FunctionTemplate::New();
840 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
841 QV8QObjectWrapper::Setter,
842 QV8QObjectWrapper::Query,
844 QV8QObjectWrapper::Enumerator);
845 ft->InstanceTemplate()->SetHasExternalResource(true);
848 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter,
849 v8::Integer::NewFromUnsigned(data));
854 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
856 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
857 QV8QObjectWrapper::Setter,
858 QV8QObjectWrapper::Query,
860 QV8QObjectWrapper::Enumerator);
861 ft->InstanceTemplate()->SetHasExternalResource(true);
862 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
866 v8::Local<v8::Object> result = constructor->NewInstance();
867 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
868 result->SetExternalResource(r);
872 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
874 v8::Local<v8::Object> rv;
876 if (!ddata->propertyCache && engine->engine()) {
877 ddata->propertyCache = QDeclarativeEnginePrivate::get(engine->engine())->cache(object);
878 if (ddata->propertyCache) ddata->propertyCache->addref();
881 if (ddata->propertyCache) {
882 rv = ddata->propertyCache->newQObject(object, engine);
884 // XXX NewInstance() should be optimized
885 rv = m_constructor->NewInstance();
886 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
887 rv->SetExternalResource(r);
894 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
895 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
897 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
898 persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that
899 "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this
901 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create
902 an entry in the m_taintedObject hash where we store the handle and mark the object as
903 "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
904 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
905 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has
908 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
913 if (QObjectPrivate::get(object)->wasDeleted)
914 return v8::Undefined();
916 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
919 return v8::Undefined();
921 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
922 // We own the v8object
923 return v8::Local<v8::Object>::New(ddata->v8object);
924 } else if (ddata->v8object.IsEmpty() &&
925 (ddata->v8objectid == m_id || // We own the QObject
926 ddata->v8objectid == 0 || // No one owns the QObject
927 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
929 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
930 ddata->v8object = qPersistentNew<v8::Object>(rv);
931 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
932 ddata->v8objectid = m_id;
936 // If this object is tainted, we have to check to see if it is in our
937 // tainted object list
938 TaintedHash::Iterator iter =
939 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
940 bool found = iter != m_taintedObjects.end();
942 // If our tainted handle doesn't exist or has been collected, and there isn't
943 // a handle in the ddata, we can assume ownership of the ddata->v8object
944 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
945 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
946 ddata->v8object = qPersistentNew<v8::Object>(rv);
947 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
948 ddata->v8objectid = m_id;
952 m_taintedObjects.erase(iter);
957 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
958 iter = m_taintedObjects.insert(object, instance);
959 ddata->hasTaintedV8Object = true;
962 if ((*iter)->v8object.IsEmpty()) {
963 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
964 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
965 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
968 return v8::Local<v8::Object>::New((*iter)->v8object);
972 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
974 v8::ScriptOrigin origin = function->GetScriptOrigin();
975 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
977 // This is one of our special QObject method wrappers
978 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
979 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
981 if (data->IsArray()) {
982 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
983 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
986 // In theory this can't fall through, but I suppose V8 might run out of memory or something
989 return qMakePair((QObject *)0, -1);
992 struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
994 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
995 ~QV8QObjectConnectionList();
999 : needsDestroy(false) {}
1000 Connection(const Connection &other)
1001 : thisObject(other.thisObject), function(other.function), needsDestroy(false) {}
1002 Connection &operator=(const Connection &other) {
1003 thisObject = other.thisObject;
1004 function = other.function;
1005 needsDestroy = other.needsDestroy;
1009 v8::Persistent<v8::Object> thisObject;
1010 v8::Persistent<v8::Function> function;
1013 qPersistentDispose(thisObject);
1014 qPersistentDispose(function);
1020 struct ConnectionList : public QList<Connection> {
1021 ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {}
1022 int connectionsInUse;
1023 bool connectionsNeedClean;
1028 typedef QHash<int, ConnectionList> SlotHash;
1033 virtual void objectDestroyed(QObject *);
1034 virtual int qt_metacall(QMetaObject::Call, int, void **);
1037 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
1038 : QDeclarativeGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0)
1042 QV8QObjectConnectionList::~QV8QObjectConnectionList()
1044 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
1045 QList<Connection> &connections = *iter;
1046 for (int ii = 0; ii < connections.count(); ++ii) {
1047 qPersistentDispose(connections[ii].thisObject);
1048 qPersistentDispose(connections[ii].function);
1054 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
1056 engine->qobjectWrapper()->m_connections.remove(object);
1059 needsDestroy = true;
1064 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
1066 if (method == QMetaObject::InvokeMetaMethod) {
1067 SlotHash::Iterator iter = slotHash.find(index);
1068 if (iter == slotHash.end())
1070 ConnectionList &connectionList = *iter;
1071 if (connectionList.isEmpty())
1076 connectionList.connectionsInUse++;
1078 QList<Connection> connections = connectionList;
1080 QMetaMethod method = data()->metaObject()->method(index);
1081 Q_ASSERT(method.methodType() == QMetaMethod::Signal);
1082 // XXX TODO: We should figure out a way to cache the parameter types to avoid resolving
1084 QList<QByteArray> params = method.parameterTypes();
1086 v8::HandleScope handle_scope;
1087 v8::Context::Scope scope(engine->context());
1089 QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
1090 int argCount = params.count();
1092 for (int ii = 0; ii < argCount; ++ii) {
1093 int type = QMetaType::type(params.at(ii).constData());
1094 if (type == qMetaTypeId<QVariant>()) {
1095 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
1097 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
1101 for (int ii = 0; ii < connections.count(); ++ii) {
1102 Connection &connection = connections[ii];
1103 if (connection.needsDestroy)
1105 if (connection.thisObject.IsEmpty()) {
1106 connection.function->Call(engine->global(), argCount, args.data());
1108 connection.function->Call(connection.thisObject, argCount, args.data());
1112 connectionList.connectionsInUse--;
1113 if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) {
1114 for (QList<Connection>::Iterator iter = connectionList.begin();
1115 iter != connectionList.end(); ) {
1116 if (iter->needsDestroy) {
1118 iter = connectionList.erase(iter);
1126 if (inUse == 0 && needsDestroy)
1133 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
1135 if (args.Length() == 0)
1136 V8THROW_ERROR("Function.prototype.connect: no arguments given");
1138 QV8Engine *engine = V8ENGINE();
1140 if (!args.This()->IsFunction())
1141 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1143 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1144 QObject *signalObject = signalInfo.first;
1145 int signalIndex = signalInfo.second;
1147 if (signalIndex == -1)
1148 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1151 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
1153 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1154 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
1156 v8::Local<v8::Value> functionValue;
1157 v8::Local<v8::Value> functionThisValue;
1159 if (args.Length() == 1) {
1160 functionValue = args[0];
1162 functionThisValue = args[0];
1163 functionValue = args[1];
1166 if (!functionValue->IsFunction())
1167 V8THROW_ERROR("Function.prototype.connect: target is not a function");
1169 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1170 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
1172 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1173 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
1174 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
1175 if (iter == connections.end())
1176 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1178 QV8QObjectConnectionList *connectionList = *iter;
1179 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1180 if (slotIter == connectionList->slotHash.end()) {
1181 slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList());
1182 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1185 QV8QObjectConnectionList::Connection connection;
1186 if (!functionThisValue.IsEmpty())
1187 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1188 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1190 slotIter->append(connection);
1192 return v8::Undefined();
1195 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1197 if (args.Length() == 0)
1198 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1200 QV8Engine *engine = V8ENGINE();
1202 if (!args.This()->IsFunction())
1203 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1205 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1206 QObject *signalObject = signalInfo.first;
1207 int signalIndex = signalInfo.second;
1209 if (signalIndex == -1)
1210 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1213 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1215 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1216 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1218 v8::Local<v8::Value> functionValue;
1219 v8::Local<v8::Value> functionThisValue;
1221 if (args.Length() == 1) {
1222 functionValue = args[0];
1224 functionThisValue = args[0];
1225 functionValue = args[1];
1228 if (!functionValue->IsFunction())
1229 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1231 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1232 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1234 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1235 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1236 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1237 if (iter == connectionsList.end())
1238 return v8::Undefined(); // Nothing to disconnect from
1240 QV8QObjectConnectionList *connectionList = *iter;
1241 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1242 if (slotIter == connectionList->slotHash.end())
1243 return v8::Undefined(); // Nothing to disconnect from
1245 QV8QObjectConnectionList::ConnectionList &connections = *slotIter;
1247 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1248 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1250 if (functionData.second != -1) {
1251 // This is a QObject function wrapper
1252 for (int ii = 0; ii < connections.count(); ++ii) {
1253 QV8QObjectConnectionList::Connection &connection = connections[ii];
1255 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1256 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1258 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1259 if (connectedFunctionData == functionData) {
1261 if (connections.connectionsInUse) {
1262 connection.needsDestroy = true;
1264 connection.dispose();
1265 connections.removeAt(ii);
1267 return v8::Undefined();
1273 // This is a normal JS function
1274 for (int ii = 0; ii < connections.count(); ++ii) {
1275 QV8QObjectConnectionList::Connection &connection = connections[ii];
1276 if (connection.function->StrictEquals(function) &&
1277 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1278 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1280 if (connections.connectionsInUse) {
1281 connection.needsDestroy = true;
1283 connection.dispose();
1284 connections.removeAt(ii);
1286 return v8::Undefined();
1291 return v8::Undefined();
1295 \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode)
1297 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1299 Only searches for real properties of \a object (including methods), not attached properties etc.
1303 \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode)
1305 Set the \a property of \a object to \a value.
1307 Returns true if the property was "set" - even if this results in an exception being thrown -
1308 and false if the object has no such property.
1310 Only searches for real properties of \a object (including methods), not attached properties etc.
1316 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1317 int Length() const { return _length; }
1318 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1322 v8::Handle<v8::Object> *_args;
1326 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1327 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1331 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1332 args[0].initAsType(returnType);
1334 for (int ii = 0; ii < argCount; ++ii)
1335 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1337 QVarLengthArray<void *, 9> argData(args.count());
1338 for (int ii = 0; ii < args.count(); ++ii)
1339 argData[ii] = args[ii].dataPtr();
1341 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1343 return args[0].toValue(engine);
1345 } else if (returnType != 0) {
1347 MetaCallArgument arg;
1348 arg.initAsType(returnType);
1350 void *args[] = { arg.dataPtr() };
1352 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1354 return arg.toValue(engine);
1358 void *args[] = { 0 };
1359 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1360 return v8::Undefined();
1365 static int EnumType(const QMetaObject *meta, const QString &strname)
1367 QByteArray str = strname.toUtf8();
1370 int scopeIdx = str.lastIndexOf("::");
1371 if (scopeIdx != -1) {
1372 scope = str.left(scopeIdx);
1373 name = str.mid(scopeIdx + 2);
1377 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1378 QMetaEnum m = meta->enumerator(i);
1379 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1380 return QVariant::Int;
1382 return QVariant::Invalid;
1386 Returns the match score for converting \a actual to be of type \a conversionType. A
1387 zero score means "perfect match" whereas a higher score is worse.
1389 The conversion table is copied out of the QtScript callQtMethod() function.
1391 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
1392 const QByteArray &conversionTypeName)
1394 if (actual->IsNumber()) {
1395 switch (conversionType) {
1396 case QMetaType::Double:
1398 case QMetaType::Float:
1400 case QMetaType::LongLong:
1401 case QMetaType::ULongLong:
1403 case QMetaType::Long:
1404 case QMetaType::ULong:
1406 case QMetaType::Int:
1407 case QMetaType::UInt:
1409 case QMetaType::Short:
1410 case QMetaType::UShort:
1413 case QMetaType::Char:
1414 case QMetaType::UChar:
1419 } else if (actual->IsString()) {
1420 switch (conversionType) {
1421 case QMetaType::QString:
1426 } else if (actual->IsBoolean()) {
1427 switch (conversionType) {
1428 case QMetaType::Bool:
1433 } else if (actual->IsDate()) {
1434 switch (conversionType) {
1435 case QMetaType::QDateTime:
1437 case QMetaType::QDate:
1439 case QMetaType::QTime:
1444 } else if (actual->IsRegExp()) {
1445 switch (conversionType) {
1446 case QMetaType::QRegExp:
1451 } else if (actual->IsArray()) {
1452 switch (conversionType) {
1453 case QMetaType::QStringList:
1454 case QMetaType::QVariantList:
1459 } else if (actual->IsNull()) {
1460 switch (conversionType) {
1461 case QMetaType::VoidStar:
1462 case QMetaType::QObjectStar:
1465 if (!conversionTypeName.endsWith('*'))
1470 } else if (actual->IsObject()) {
1471 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1473 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1474 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1475 switch (conversionType) {
1476 case QMetaType::QObjectStar:
1481 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1482 if (conversionType == qMetaTypeId<QVariant>())
1484 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1497 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1503 int classInfoCount, classInfoData;
1504 int methodCount, methodData;
1507 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1510 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1512 QByteArray sig = m.signature();
1513 int paren = sig.indexOf('(');
1517 return sig.left(paren);
1521 Returns the next related method, if one, or 0.
1523 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object,
1524 const QDeclarativePropertyCache::Data *current,
1525 QDeclarativePropertyCache::Data &dummy)
1527 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1528 if (current->relatedIndex == -1)
1532 return cache->method(current->relatedIndex);
1534 const QMetaObject *mo = object->metaObject();
1535 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1537 while (methodOffset > current->relatedIndex) {
1538 mo = mo->superClass();
1539 methodOffset -= QMetaObject_methods(mo);
1542 QMetaMethod method = mo->method(current->relatedIndex);
1545 // Look for overloaded methods
1546 QByteArray methodName = QMetaMethod_name(method);
1547 for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1548 if (methodName == QMetaMethod_name(mo->method(ii))) {
1549 dummy.relatedIndex = ii;
1558 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
1559 QV8Engine *engine, CallArgs &callArgs)
1561 if (data.hasArguments()) {
1563 QMetaMethod m = object->metaObject()->method(data.coreIndex);
1564 QList<QByteArray> argTypeNames = m.parameterTypes();
1565 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1568 for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1569 argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1570 if (argTypes[ii] == QVariant::Invalid)
1571 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1572 if (argTypes[ii] == QVariant::Invalid) {
1573 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1574 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1575 return v8::Handle<v8::Value>();
1579 if (argTypes.count() > callArgs.Length()) {
1580 QString error = QLatin1String("Insufficient arguments");
1581 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1582 return v8::Handle<v8::Value>();
1585 return CallMethod(object, data.coreIndex, data.propType, argTypes.count(),
1586 argTypes.data(), engine, callArgs);
1590 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1596 Resolve the overloaded method to call. The algorithm works conceptually like this:
1597 1. Resolve the set of overloads it is *possible* to call.
1598 Impossible overloads include those that have too many parameters or have parameters
1600 2. Filter the set of overloads to only contain those with the closest number of
1602 For example, if we are called with 3 parameters and there are 2 overloads that
1603 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1604 3. Find the best remaining overload based on its match score.
1605 If two or more overloads have the same match score, call the last one. The match
1606 score is constructed by adding the matchScore() result for each of the parameters.
1608 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data,
1609 QV8Engine *engine, CallArgs &callArgs)
1611 int argumentCount = callArgs.Length();
1613 const QDeclarativePropertyCache::Data *best = 0;
1614 int bestParameterScore = INT_MAX;
1615 int bestMatchScore = INT_MAX;
1617 QDeclarativePropertyCache::Data dummy;
1618 const QDeclarativePropertyCache::Data *attempt = &data;
1621 QList<QByteArray> methodArgTypeNames;
1623 if (attempt->hasArguments())
1624 methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1626 int methodArgumentCount = methodArgTypeNames.count();
1628 if (methodArgumentCount > argumentCount)
1629 continue; // We don't have sufficient arguments to call this method
1631 int methodParameterScore = argumentCount - methodArgumentCount;
1632 if (methodParameterScore > bestParameterScore)
1633 continue; // We already have a better option
1635 int methodMatchScore = 0;
1636 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1638 bool unknownArgument = false;
1639 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1640 methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1641 if (methodArgTypes[ii] == QVariant::Invalid)
1642 methodArgTypes[ii] = EnumType(object->metaObject(),
1643 QString::fromLatin1(methodArgTypeNames.at(ii)));
1644 if (methodArgTypes[ii] == QVariant::Invalid) {
1645 unknownArgument = true;
1648 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1650 if (unknownArgument)
1651 continue; // We don't understand all the parameters
1653 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1655 bestParameterScore = methodParameterScore;
1656 bestMatchScore = methodMatchScore;
1659 if (bestParameterScore == 0 && bestMatchScore == 0)
1660 break; // We can't get better than that
1662 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1665 return CallPrecise(object, *best, engine, callArgs);
1667 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1668 const QDeclarativePropertyCache::Data *candidate = &data;
1670 error += QLatin1String("\n ") +
1671 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1672 candidate = RelatedMethod(object, candidate, dummy);
1675 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1676 return v8::Handle<v8::Value>();
1680 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1684 QString objectName = object->objectName();
1686 result += QString::fromUtf8(object->metaObject()->className());
1687 result += QLatin1String("(0x");
1688 result += QString::number((quintptr)object,16);
1690 if (!objectName.isEmpty()) {
1691 result += QLatin1String(", \"");
1692 result += objectName;
1693 result += QLatin1Char('\"');
1696 result += QLatin1Char(')');
1698 result = QLatin1String("null");
1701 return engine->toString(result);
1704 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1706 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1707 if (!ddata || ddata->indestructible) {
1708 const char *error = "Invalid attempt to destroy() an indestructible object";
1709 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1710 return v8::Undefined();
1715 delay = args->Get(0)->Uint32Value();
1718 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1720 object->deleteLater();
1722 return v8::Undefined();
1725 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1727 // object, index, qmlglobal, argCount, args
1728 Q_ASSERT(args.Length() == 5);
1729 Q_ASSERT(args[0]->IsObject());
1731 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1734 return v8::Undefined();
1736 int argCount = args[3]->Int32Value();
1737 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1739 // Special hack to return info about this closure.
1740 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1741 v8::Local<v8::Array> data = v8::Array::New(2);
1742 data->Set(0, args[0]);
1743 data->Set(1, args[1]);
1747 QObject *object = resource->object;
1748 int index = args[1]->Int32Value();
1751 return v8::Undefined();
1754 // Builtin functions
1755 if (index == QOBJECT_TOSTRING_INDEX) {
1756 return ToString(resource->engine, object, argCount, arguments);
1757 } else if (index == QOBJECT_DESTROY_INDEX) {
1758 return Destroy(resource->engine, object, argCount, arguments);
1760 return v8::Undefined();
1764 QDeclarativePropertyCache::Data method;
1766 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1767 if (ddata->propertyCache) {
1768 QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1770 return v8::Undefined();
1775 if (method.coreIndex == -1) {
1776 QMetaMethod mm = object->metaObject()->method(index);
1777 method.load(object->metaObject()->method(index));
1779 if (method.coreIndex == -1)
1780 return v8::Undefined();
1783 if (method.isV8Function()) {
1784 v8::Handle<v8::Value> rv;
1785 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1787 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1788 resource->engine->contextWrapper()->context(qmlglobal),
1790 QDeclarativeV8Function *funcptr = &func;
1792 void *args[] = { 0, &funcptr };
1793 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1795 if (rv.IsEmpty()) return v8::Undefined();
1799 CallArgs callArgs(argCount, &arguments);
1800 if (method.relatedIndex == -1) {
1801 return CallPrecise(object, method, resource->engine, callArgs);
1803 return CallOverloaded(object, method, resource->engine, callArgs);
1807 MetaCallArgument::MetaCallArgument()
1808 : type(QVariant::Invalid), isObjectType(false)
1812 MetaCallArgument::~MetaCallArgument()
1817 void MetaCallArgument::cleanup()
1819 if (type == QMetaType::QString) {
1820 ((QString *)&data)->~QString();
1821 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1822 ((QVariant *)&data)->~QVariant();
1823 } else if (type == qMetaTypeId<QScriptValue>()) {
1824 ((QScriptValue *)&data)->~QScriptValue();
1825 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1826 ((QList<QObject *> *)&data)->~QList<QObject *>();
1830 void *MetaCallArgument::dataPtr()
1833 return ((QVariant *)data)->data();
1835 return (void *)&data;
1838 void MetaCallArgument::initAsType(int callType)
1840 if (type != 0) { cleanup(); type = 0; }
1841 if (callType == 0) return;
1843 if (callType == qMetaTypeId<QScriptValue>()) {
1844 new (&data) QScriptValue();
1846 } else if (callType == QMetaType::Int ||
1847 callType == QMetaType::UInt ||
1848 callType == QMetaType::Bool ||
1849 callType == QMetaType::Double ||
1850 callType == QMetaType::Float) {
1852 } else if (callType == QMetaType::QObjectStar) {
1853 *((QObject **)&data) = 0;
1855 } else if (callType == QMetaType::QString) {
1856 new (&data) QString();
1858 } else if (callType == qMetaTypeId<QVariant>()) {
1860 new (&data) QVariant();
1861 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1863 new (&data) QList<QObject *>();
1864 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1866 new (&data) v8::Handle<v8::Value>();
1869 new (&data) QVariant(callType, (void *)0);
1873 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1875 if (type != 0) { cleanup(); type = 0; }
1877 if (callType == qMetaTypeId<QScriptValue>()) {
1878 new (&data) QScriptValue();
1879 type = qMetaTypeId<QScriptValue>();
1880 } else if (callType == QMetaType::Int) {
1881 *((int *)&data) = int(value->Int32Value());
1883 } else if (callType == QMetaType::UInt) {
1884 *((uint *)&data) = uint(value->Uint32Value());
1886 } else if (callType == QMetaType::Bool) {
1887 *((bool *)&data) = value->BooleanValue();
1889 } else if (callType == QMetaType::Double) {
1890 *((double *)&data) = double(value->NumberValue());
1892 } else if (callType == QMetaType::Float) {
1893 *((float *)&data) = float(value->NumberValue());
1895 } else if (callType == QMetaType::QString) {
1896 if (value->IsNull() || value->IsUndefined())
1897 new (&data) QString();
1899 new (&data) QString(engine->toString(value->ToString()));
1901 } else if (callType == QMetaType::QObjectStar) {
1902 *((QObject **)&data) = engine->toQObject(value);
1904 } else if (callType == qMetaTypeId<QVariant>()) {
1905 new (&data) QVariant(engine->toVariant(value, -1));
1907 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1908 QList<QObject *> *list = new (&data) QList<QObject *>();
1909 if (value->IsArray()) {
1910 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1911 uint32_t length = array->Length();
1912 for (uint32_t ii = 0; ii < length; ++ii)
1913 list->append(engine->toQObject(array->Get(ii)));
1915 list->append(engine->toQObject(value));
1918 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1919 new (&data) v8::Handle<v8::Value>(value);
1922 new (&data) QVariant();
1925 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
1926 QVariant v = engine->toVariant(value, -1);
1928 if (v.userType() == callType) {
1929 *((QVariant *)&data) = v;
1930 } else if (v.canConvert((QVariant::Type)callType)) {
1931 *((QVariant *)&data) = v;
1932 ((QVariant *)&data)->convert((QVariant::Type)callType);
1933 } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) {
1934 QObject *obj = ep->toQObject(v);
1937 const QMetaObject *objMo = obj->metaObject();
1938 while (objMo && objMo != mo) objMo = objMo->superClass();
1939 if (!objMo) obj = 0;
1942 *((QVariant *)&data) = QVariant(callType, &obj);
1944 *((QVariant *)&data) = QVariant(callType, (void *)0);
1949 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1951 if (type == qMetaTypeId<QScriptValue>()) {
1952 return v8::Undefined();
1953 } else if (type == QMetaType::Int) {
1954 return v8::Integer::New(*((int *)&data));
1955 } else if (type == QMetaType::UInt) {
1956 return v8::Integer::NewFromUnsigned(*((uint *)&data));
1957 } else if (type == QMetaType::Bool) {
1958 return v8::Boolean::New(*((bool *)&data));
1959 } else if (type == QMetaType::Double) {
1960 return v8::Number::New(*((double *)&data));
1961 } else if (type == QMetaType::Float) {
1962 return v8::Number::New(*((float *)&data));
1963 } else if (type == QMetaType::QString) {
1964 return engine->toString(*((QString *)&data));
1965 } else if (type == QMetaType::QObjectStar) {
1966 QObject *object = *((QObject **)&data);
1968 QDeclarativeData::get(object, true)->setImplicitDestructible();
1969 return engine->newQObject(object);
1970 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1971 // XXX Can this be made more by using Array as a prototype and implementing
1972 // directly against QList<QObject*>?
1973 QList<QObject *> &list = *(QList<QObject *>*)&data;
1974 v8::Local<v8::Array> array = v8::Array::New(list.count());
1975 for (int ii = 0; ii < list.count(); ++ii)
1976 array->Set(ii, engine->newQObject(list.at(ii)));
1978 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
1979 return *(v8::Handle<v8::Value>*)&data;
1980 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1981 QVariant value = *((QVariant *)&data);
1982 v8::Handle<v8::Value> rv = engine->fromVariant(value);
1983 if (QObject *object = engine->toQObject(rv))
1984 QDeclarativeData::get(object, true)->setImplicitDestructible();
1987 return v8::Undefined();