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 Need to check all calls to QDeclarativeEngine *engine() to confirm this class works
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_cast<QV8QObjectResource>(This); \
171 if (!resource || 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 & 0x7FFF0000) >> 16; \
178 if (notify == 0x7FFF) 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); \
193 #define CREATE_FUNCTION \
194 "(function(method) { "\
195 "return (function(object, data, qmlglobal) { "\
196 "return (function() { "\
197 "return method(object, data, qmlglobal, arguments.length, arguments); "\
202 void QV8QObjectWrapper::init(QV8Engine *engine)
206 m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString"));
207 m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy"));
208 m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New());
211 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
212 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
213 ft->InstanceTemplate()->SetHasExternalResource(true);
214 m_constructor = qPersistentNew<v8::Function>(ft->GetFunction());
217 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
218 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin);
219 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
220 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
221 v8::Handle<v8::Value> args[] = { invokeFn };
222 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
223 m_methodConstructor = qPersistentNew<v8::Function>(createFn);
227 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
228 prototype->Set(v8::String::New("connect"), V8FUNCTION(Connect, engine));
229 prototype->Set(v8::String::New("disconnect"), V8FUNCTION(Disconnect, engine));
233 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
235 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
238 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
240 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
241 return r?r->object:0;
244 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
245 QObject *QV8QObjectWrapper::QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
247 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
248 return static_cast<QV8QObjectResource *>(r)->object;
251 // Load value properties
252 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
253 const QDeclarativePropertyCache::Data &property)
255 Q_ASSERT(!property.isFunction());
257 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
258 if (property.propType == QMetaType:: metatype) { \
259 cpptype type = cpptype(); \
260 void *args[] = { &type, 0 }; \
261 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \
262 return constructor(type); \
265 if (property.isQObject()) {
267 void *args[] = { &rv, 0 };
268 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
269 return engine->newQObject(rv);
270 } else if (property.isQList()) {
271 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
272 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
273 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Number::New)
274 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
275 else PROPERTY_LOAD(QString, QString, engine->toString)
276 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
277 else PROPERTY_LOAD(Float, float, v8::Number::New)
278 else PROPERTY_LOAD(Double, double, v8::Number::New)
279 else if(property.isV8Handle()) {
280 QDeclarativeV8Handle handle;
281 void *args[] = { &handle, 0 };
282 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
283 return reinterpret_cast<v8::Handle<v8::Value> &>(handle);
284 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)) {
285 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
286 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
288 return engine->newValueType(object, property.coreIndex, valueType);
291 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
292 return engine->fromVariant(var);
295 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
296 v8::Handle<v8::Value> *objectHandle,
297 v8::Handle<v8::String> property,
298 QV8QObjectWrapper::RevisionMode revisionMode)
300 // XXX aakenned This can't possibly be the best solution!!!
301 struct MethodClosure {
302 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
303 v8::Handle<v8::Value> *objectHandle,
305 v8::Handle<v8::Value> argv[] = {
306 objectHandle?*objectHandle:engine->newQObject(object),
307 v8::Integer::New(index)
309 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
311 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
312 v8::Handle<v8::Value> *objectHandle,
314 v8::Handle<v8::Value> argv[] = {
315 objectHandle?*objectHandle:engine->newQObject(object),
316 v8::Integer::New(index),
317 v8::Context::GetCallingQmlGlobal()
319 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
323 if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property)) {
324 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
325 } else if (engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property)) {
326 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
329 QDeclarativePropertyCache::Data local;
330 QDeclarativePropertyCache::Data *result = 0;
331 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
334 return v8::Handle<v8::Value>();
336 QDeclarativeEnginePrivate *ep = engine->engine()?QDeclarativeEnginePrivate::get(engine->engine()):0;
338 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
339 QDeclarativeData *ddata = QDeclarativeData::get(object);
340 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
341 return v8::Handle<v8::Value>();
344 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
346 if (result->isFunction()) {
347 if (result->flags & QDeclarativePropertyCache::Data::IsVMEFunction) {
348 return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
349 } else if (result->flags & QDeclarativePropertyCache::Data::IsV8Function) {
350 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
352 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
356 if (ep && ep->captureProperties && !result->isConstant()) {
357 if (result->coreIndex == 0)
358 ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
360 ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
363 return LoadProperty(engine, object, *result);
366 // Setter for writable properties. Shared between the interceptor and fast property accessor
367 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property,
368 v8::Handle<v8::Value> value)
370 QDeclarativeBinding *newBinding = 0;
372 if (value->IsFunction()) {
373 QDeclarativeContextData *context = engine->callingContext();
374 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
376 v8::Local<v8::StackTrace> trace =
377 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
378 v8::StackTrace::kScriptName));
379 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
380 int lineNumber = frame->GetLineNumber();
381 QString url = engine->toString(frame->GetScriptName());
383 QDeclarativePropertyCache::ValueTypeData valueTypeData;
384 newBinding = new QDeclarativeBinding(&function, object, context);
385 newBinding->setSourceLocation(url, lineNumber);
386 newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context));
387 newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
390 QDeclarativeAbstractBinding *oldBinding =
391 QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
393 oldBinding->destroy();
395 #define PROPERTY_STORE(cpptype, value) \
399 void *argv[] = { &o, 0, &status, &flags }; \
400 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
403 if (value->IsNull() && property->isQObject()) {
404 PROPERTY_STORE(QObject*, 0);
405 } else if (value->IsUndefined() && property->isResettable()) {
407 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
408 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
409 PROPERTY_STORE(QVariant, QVariant());
410 } else if (value->IsUndefined()) {
411 QString error = QLatin1String("Cannot assign [undefined] to ") +
412 QLatin1String(QMetaType::typeName(property->propType));
413 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
414 } else if (value->IsFunction()) {
415 // this is handled by the binding creation above
416 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
417 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
418 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
419 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
420 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
421 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
422 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
423 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
424 } else if (property->propType == QMetaType::QString && value->IsString()) {
425 PROPERTY_STORE(QString, engine->toString(value->ToString()));
428 if (property->isQList())
429 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
431 v = engine->toVariant(value, property->propType);
433 QDeclarativeContextData *context = engine->callingContext();
435 if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
436 const char *valueType = 0;
437 if (v.userType() == QVariant::Invalid) valueType = "null";
438 else valueType = QMetaType::typeName(v.userType());
440 QString error = QLatin1String("Cannot assign ") +
441 QLatin1String(valueType) +
442 QLatin1String(" to ") +
443 QLatin1String(QMetaType::typeName(property->propType));
444 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
449 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, v8::Handle<v8::String> property,
450 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
452 if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property) ||
453 engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property))
456 QDeclarativePropertyCache::Data local;
457 QDeclarativePropertyCache::Data *result = 0;
458 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
463 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
464 QDeclarativeData *ddata = QDeclarativeData::get(object);
465 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
469 if (!result->isWritable() && !result->isQList()) {
470 QString error = QLatin1String("Cannot assign to read-only property \"") +
471 engine->toString(property) + QLatin1Char('\"');
472 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
476 StoreProperty(engine, object, result, value);
481 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
482 const v8::AccessorInfo &info)
484 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
485 v8::Handle<v8::Value> This = info.This();
487 if (!resource || resource->object.isNull()) return v8::Undefined();
489 QObject *object = resource->object;
491 QV8Engine *v8engine = resource->engine;
492 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, property, QV8QObjectWrapper::IgnoreRevision);
493 if (!result.IsEmpty())
496 if (QV8Engine::startsWithUpper(property)) {
497 // Check for attached properties
498 QDeclarativeContextData *context = v8engine->callingContext();
499 QDeclarativeTypeNameCache::Data *data = context && (context->imports)?context->imports->data(property):0;
503 return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums);
504 } else if (data->typeNamespace) {
505 return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
509 return v8::Undefined();
512 return v8::Undefined();
516 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
517 v8::Local<v8::Value> value,
518 const v8::AccessorInfo &info)
520 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
522 if (!resource || resource->object.isNull())
525 QObject *object = resource->object;
527 QV8Engine *v8engine = resource->engine;
528 bool result = SetProperty(v8engine, object, property, value, QV8QObjectWrapper::IgnoreRevision);
531 QString error = QLatin1String("Cannot assign to non-existent property \"") +
532 v8engine->toString(property) + QLatin1Char('\"');
533 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
540 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
541 const v8::AccessorInfo &info)
543 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
545 if (!resource || resource->object.isNull())
546 return v8::Handle<v8::Integer>();
548 QV8Engine *engine = resource->engine;
549 QObject *object = resource->object;
551 QDeclarativePropertyCache::Data local;
552 QDeclarativePropertyCache::Data *result = 0;
553 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
556 return v8::Handle<v8::Integer>();
557 else if (!result->isWritable() && !result->isQList())
558 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
560 return v8::Integer::New(v8::DontDelete);
563 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
565 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
567 if (!resource || resource->object.isNull())
568 return v8::Array::New();
570 QObject *object = resource->object;
574 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine());
576 QDeclarativePropertyCache *cache = 0;
577 QDeclarativeData *ddata = QDeclarativeData::get(object);
579 cache = ddata->propertyCache;
582 cache = ep->cache(object);
584 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
586 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
587 // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal.
588 // XXX This is a workaround for QTBUG-9420.
589 const QMetaObject *mo = object->metaObject();
590 int pc = mo->propertyCount();
591 int po = mo->propertyOffset();
592 for (int i=po; i<pc; ++i)
593 result << QString::fromUtf8(mo->property(i).name());
596 result = cache->propertyNames();
599 v8::Local<v8::Array> rv = v8::Array::New(result.count());
601 for (int ii = 0; ii < result.count(); ++ii)
602 rv->Set(ii, resource->engine->toString(result.at(ii)));
607 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
608 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
609 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
610 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
611 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
612 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
613 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
615 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
616 const v8::AccessorInfo& info)
618 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
620 if (!resource || resource->object.isNull())
623 QObject *object = resource->object;
625 uint32_t data = info.Data()->Uint32Value();
626 int index = data & 0x7FFF; // So that we can use the same data for Setter and Getter
628 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
630 Q_ASSERT(ddata->propertyCache);
632 QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index);
635 Q_ASSERT(pdata->isWritable() || pdata->isQList());
637 StoreProperty(resource->engine, object, pdata, value);
640 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
642 Q_ASSERT(handle->IsObject());
644 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(handle->ToObject());
648 QObject *object = resource->object;
650 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
652 ddata->v8object.Clear();
653 if (!object->parent() && !ddata->indestructible)
654 object->deleteLater();
658 qPersistentDispose(handle);
661 static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data)
663 QV8QObjectInstance *instance = (QV8QObjectInstance *)data;
664 instance->v8object.Clear();
665 qPersistentDispose(handle);
668 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
672 Q_ASSERT(QDeclarativeData::get(object, false));
673 Q_ASSERT(QDeclarativeData::get(object, false)->propertyCache == this);
676 if (constructor.IsEmpty()) {
677 v8::Local<v8::FunctionTemplate> ft;
679 QString toString = QLatin1String("toString");
680 QString destroy = QLatin1String("destroy");
682 // XXX Enables fast property accessors. These more than double the property access
683 // performance, but the cost of setting up this structure hasn't been measured so
684 // its not guarenteed that this is a win overall
685 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
686 Data *property = *iter;
687 if (property->isFunction() || !property->isWritable() ||
688 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x7FFF ||
689 property->coreIndex == 0)
692 v8::AccessorGetter fastgetter = 0;
695 if (property->isQObject())
696 fastgetter = QObjectValueGetter;
697 else if (property->propType == QMetaType::Int || property->isEnum())
698 fastgetter = IntValueGetter;
699 else if (property->propType == QMetaType::Bool)
700 fastgetter = BoolValueGetter;
701 else if (property->propType == QMetaType::QString)
702 fastgetter = QStringValueGetter;
703 else if (property->propType == QMetaType::UInt)
704 fastgetter = UIntValueGetter;
705 else if (property->propType == QMetaType::Float)
706 fastgetter = FloatValueGetter;
707 else if (property->propType == QMetaType::Double)
708 fastgetter = DoubleValueGetter;
711 int notifyIndex = property->notifyIndex;
712 if (property->isConstant()) notifyIndex = 0;
713 else if (notifyIndex == -1) notifyIndex = 0x7FFF;
714 uint32_t data = (property->notifyIndex & 0x7FFF) << 16 | property->coreIndex;
716 QString name = iter.key();
717 if (name == toString || name == destroy)
721 ft = v8::FunctionTemplate::New();
722 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
723 QV8QObjectWrapper::Setter,
724 QV8QObjectWrapper::Query,
726 QV8QObjectWrapper::Enumerator);
727 ft->InstanceTemplate()->SetHasExternalResource(true);
730 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, FastValueSetter,
731 v8::Integer::NewFromUnsigned(data));
736 constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor);
738 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
739 QV8QObjectWrapper::Setter,
740 QV8QObjectWrapper::Query,
742 QV8QObjectWrapper::Enumerator);
743 ft->InstanceTemplate()->SetHasExternalResource(true);
744 constructor = qPersistentNew<v8::Function>(ft->GetFunction());
748 v8::Local<v8::Object> result = constructor->NewInstance();
749 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
750 result->SetExternalResource(r);
754 v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QDeclarativeData *ddata, QV8Engine *engine)
756 v8::Local<v8::Object> rv;
758 if (ddata->propertyCache) {
759 rv = ddata->propertyCache->newQObject(object, engine);
761 // XXX aakenned - NewInstance() is slow for our case
762 rv = m_constructor->NewInstance();
763 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
764 rv->SetExternalResource(r);
771 As V8 doesn't support an equality callback, for QObject's we have to return exactly the same
772 V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two
774 1. If there is no current outstanding V8 handle to the QObject, we create one and store a
775 persistent handle in QDeclarativeData::v8object. We mark the QV8QObjectWrapper that
776 "owns" this handle by setting the QDeclarativeData::v8objectid to the id of this
778 2. If another QV8QObjectWrapper has create the handle in QDeclarativeData::v8object we create
779 an entry in the m_taintedObject hash where we store the handle and mark the object as
780 "tainted" in the QDeclarativeData::hasTaintedV8Object flag.
781 We have to mark the object as tainted to ensure that we search our m_taintedObject hash even
782 in the case that the original QV8QObjectWrapper owner of QDeclarativeData::v8object has
785 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
790 if (QObjectPrivate::get(object)->wasDeleted)
791 return v8::Undefined();
793 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
796 return v8::Undefined();
798 if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) {
799 // We own the v8object
800 return v8::Local<v8::Object>::New(ddata->v8object);
801 } else if (ddata->v8object.IsEmpty() &&
802 (ddata->v8objectid == m_id || // We own the QObject
803 ddata->v8objectid == 0 || // No one owns the QObject
804 !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted
806 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
807 ddata->v8object = qPersistentNew<v8::Object>(rv);
808 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
809 ddata->v8objectid = m_id;
813 // If this object is tainted, we have to check to see if it is in our
814 // tainted object list
815 TaintedHash::Iterator iter =
816 ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end();
817 bool found = iter != m_taintedObjects.end();
819 // If our tainted handle doesn't exist or has been collected, and there isn't
820 // a handle in the ddata, we can assume ownership of the ddata->v8object
821 if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) {
822 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
823 ddata->v8object = qPersistentNew<v8::Object>(rv);
824 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
825 ddata->v8objectid = m_id;
829 m_taintedObjects.erase(iter);
834 QV8QObjectInstance *instance = new QV8QObjectInstance(object, this);
835 iter = m_taintedObjects.insert(object, instance);
836 ddata->hasTaintedV8Object = true;
839 if ((*iter)->v8object.IsEmpty()) {
840 v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine);
841 (*iter)->v8object = qPersistentNew<v8::Object>(rv);
842 (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback);
845 return v8::Local<v8::Object>::New((*iter)->v8object);
849 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
851 v8::ScriptOrigin origin = function->GetScriptOrigin();
852 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
854 // This is one of our special QObject method wrappers
855 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
856 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
858 if (data->IsArray()) {
859 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
860 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
863 // In theory this can't fall through, but I suppose V8 might run out of memory or something
866 return qMakePair((QObject *)0, -1);
869 struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
871 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
872 ~QV8QObjectConnectionList();
875 v8::Persistent<v8::Object> thisObject;
876 v8::Persistent<v8::Function> function;
881 typedef QHash<int, QList<Connection> > SlotHash;
884 virtual void objectDestroyed(QObject *);
885 virtual int qt_metacall(QMetaObject::Call, int, void **);
888 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
889 : QDeclarativeGuard<QObject>(object), engine(engine)
893 QV8QObjectConnectionList::~QV8QObjectConnectionList()
895 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
896 QList<Connection> &connections = *iter;
897 for (int ii = 0; ii < connections.count(); ++ii) {
898 qPersistentDispose(connections[ii].thisObject);
899 qPersistentDispose(connections[ii].function);
905 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
907 engine->qobjectWrapper()->m_connections.remove(object);
911 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
913 if (method == QMetaObject::InvokeMetaMethod) {
914 SlotHash::Iterator iter = slotHash.find(index);
915 if (iter == slotHash.end())
917 QList<Connection> &connections = *iter;
918 if (connections.isEmpty())
922 QMetaMethod method = data()->metaObject()->method(index);
923 Q_ASSERT(method.methodType() == QMetaMethod::Signal);
924 QList<QByteArray> params = method.parameterTypes();
926 v8::HandleScope handle_scope;
927 v8::Context::Scope scope(engine->context());
929 QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
930 int argCount = params.count();
932 for (int ii = 0; ii < argCount; ++ii) {
933 int type = QMetaType::type(params.at(ii).constData());
934 if (type == qMetaTypeId<QVariant>()) {
935 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
937 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
941 // XXX what if this list changes or this object is deleted during the calls?
942 for (int ii = 0; ii < connections.count(); ++ii) {
943 Connection &connection = connections[ii];
944 if (connection.thisObject.IsEmpty()) {
945 connection.function->Call(engine->global(), argCount, args.data());
947 connection.function->Call(connection.thisObject, argCount, args.data());
955 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
957 if (args.Length() == 0)
958 V8THROW_ERROR("Function.prototype.connect: no arguments given");
960 QV8Engine *engine = V8ENGINE();
962 if (!args.This()->IsFunction())
963 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
965 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
966 QObject *signalObject = signalInfo.first;
967 int signalIndex = signalInfo.second;
969 if (signalIndex == -1)
970 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
973 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
975 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
976 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
978 v8::Local<v8::Value> functionValue;
979 v8::Local<v8::Value> functionThisValue;
981 if (args.Length() == 1) {
982 functionValue = args[0];
984 functionThisValue = args[0];
985 functionValue = args[1];
988 if (!functionValue->IsFunction())
989 V8THROW_ERROR("Function.prototype.connect: target is not a function");
991 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
992 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
994 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
995 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
996 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
997 if (iter == connections.end())
998 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
1000 QV8QObjectConnectionList *connectionList = *iter;
1001 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1002 if (slotIter == connectionList->slotHash.end()) {
1003 slotIter = connectionList->slotHash.insert(signalIndex, QList<QV8QObjectConnectionList::Connection>());
1004 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
1007 QV8QObjectConnectionList::Connection connection;
1008 if (!functionThisValue.IsEmpty())
1009 connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject());
1010 connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue));
1012 slotIter->append(connection);
1014 return v8::Undefined();
1017 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
1019 if (args.Length() == 0)
1020 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
1022 QV8Engine *engine = V8ENGINE();
1024 if (!args.This()->IsFunction())
1025 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1027 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
1028 QObject *signalObject = signalInfo.first;
1029 int signalIndex = signalInfo.second;
1031 if (signalIndex == -1)
1032 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1035 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
1037 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
1038 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
1040 v8::Local<v8::Value> functionValue;
1041 v8::Local<v8::Value> functionThisValue;
1043 if (args.Length() == 1) {
1044 functionValue = args[0];
1046 functionThisValue = args[0];
1047 functionValue = args[1];
1050 if (!functionValue->IsFunction())
1051 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
1053 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
1054 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
1056 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
1057 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
1058 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
1059 if (iter == connectionsList.end())
1060 return v8::Undefined(); // Nothing to disconnect from
1062 QV8QObjectConnectionList *connectionList = *iter;
1063 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
1064 if (slotIter == connectionList->slotHash.end())
1065 return v8::Undefined(); // Nothing to disconnect from
1067 QList<QV8QObjectConnectionList::Connection> &connections = *slotIter;
1069 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
1070 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
1072 if (functionData.second != -1) {
1073 // This is a QObject function wrapper
1074 for (int ii = 0; ii < connections.count(); ++ii) {
1075 QV8QObjectConnectionList::Connection &connection = connections[ii];
1077 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1078 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1080 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
1081 if (connectedFunctionData == functionData) {
1083 qPersistentDispose(connection.thisObject);
1084 qPersistentDispose(connection.function);
1085 connections.removeAt(ii);
1086 return v8::Undefined();
1092 // This is a normal JS function
1093 for (int ii = 0; ii < connections.count(); ++ii) {
1094 QV8QObjectConnectionList::Connection &connection = connections[ii];
1095 if (connection.function->StrictEquals(function) &&
1096 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1097 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1099 qPersistentDispose(connection.thisObject);
1100 qPersistentDispose(connection.function);
1101 connections.removeAt(ii);
1102 return v8::Undefined();
1107 return v8::Undefined();
1111 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1113 Only searches for real properties of \a object (including methods), not attached properties etc.
1115 v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, v8::Handle<v8::String> property,
1116 QV8QObjectWrapper::RevisionMode revisionMode)
1118 return GetProperty(m_engine, object, 0, property, revisionMode);
1122 Set the \a property of \a object to \a value.
1124 Returns true if the property was "set" - even if this results in an exception being thrown -
1125 and false if the object has no such property.
1127 Only searches for real properties of \a object (including methods), not attached properties etc.
1129 bool QV8QObjectWrapper::setProperty(QObject *object, v8::Handle<v8::String> property,
1130 v8::Handle<v8::Value> value, RevisionMode revisionMode)
1132 return SetProperty(m_engine, object, property, value, revisionMode);
1138 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1139 int Length() const { return _length; }
1140 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1144 v8::Handle<v8::Object> *_args;
1148 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1149 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1153 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1154 args[0].initAsType(returnType);
1156 for (int ii = 0; ii < argCount; ++ii)
1157 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1159 QVarLengthArray<void *, 9> argData(args.count());
1160 for (int ii = 0; ii < args.count(); ++ii)
1161 argData[ii] = args[ii].dataPtr();
1163 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1165 return args[0].toValue(engine);
1167 } else if (returnType != 0) {
1169 MetaCallArgument arg;
1170 arg.initAsType(returnType);
1172 void *args[] = { arg.dataPtr() };
1174 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1176 return arg.toValue(engine);
1180 void *args[] = { 0 };
1181 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1182 return v8::Undefined();
1187 static int EnumType(const QMetaObject *meta, const QString &strname)
1189 QByteArray str = strname.toUtf8();
1192 int scopeIdx = str.lastIndexOf("::");
1193 if (scopeIdx != -1) {
1194 scope = str.left(scopeIdx);
1195 name = str.mid(scopeIdx + 2);
1199 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1200 QMetaEnum m = meta->enumerator(i);
1201 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1202 return QVariant::Int;
1204 return QVariant::Invalid;
1208 Returns the match score for converting \a actual to be of type \a conversionType. A
1209 zero score means "perfect match" whereas a higher score is worse.
1211 The conversion table is copied out of the QtScript callQtMethod() function.
1213 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
1214 const QByteArray &conversionTypeName)
1216 if (actual->IsNumber()) {
1217 switch (conversionType) {
1218 case QMetaType::Double:
1220 case QMetaType::Float:
1222 case QMetaType::LongLong:
1223 case QMetaType::ULongLong:
1225 case QMetaType::Long:
1226 case QMetaType::ULong:
1228 case QMetaType::Int:
1229 case QMetaType::UInt:
1231 case QMetaType::Short:
1232 case QMetaType::UShort:
1235 case QMetaType::Char:
1236 case QMetaType::UChar:
1241 } else if (actual->IsString()) {
1242 switch (conversionType) {
1243 case QMetaType::QString:
1248 } else if (actual->IsBoolean()) {
1249 switch (conversionType) {
1250 case QMetaType::Bool:
1255 } else if (actual->IsDate()) {
1256 switch (conversionType) {
1257 case QMetaType::QDateTime:
1259 case QMetaType::QDate:
1261 case QMetaType::QTime:
1266 } else if (actual->IsRegExp()) {
1267 switch (conversionType) {
1268 case QMetaType::QRegExp:
1273 } else if (actual->IsArray()) {
1274 switch (conversionType) {
1275 case QMetaType::QStringList:
1276 case QMetaType::QVariantList:
1281 } else if (actual->IsNull()) {
1282 switch (conversionType) {
1283 case QMetaType::VoidStar:
1284 case QMetaType::QObjectStar:
1287 if (!conversionTypeName.endsWith('*'))
1292 } else if (actual->IsObject()) {
1293 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1295 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1296 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1297 switch (conversionType) {
1298 case QMetaType::QObjectStar:
1303 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1304 if (conversionType == qMetaTypeId<QVariant>())
1306 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1319 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1325 int classInfoCount, classInfoData;
1326 int methodCount, methodData;
1329 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1332 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1334 QByteArray sig = m.signature();
1335 int paren = sig.indexOf('(');
1339 return sig.left(paren);
1343 Returns the next related method, if one, or 0.
1345 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object,
1346 const QDeclarativePropertyCache::Data *current,
1347 QDeclarativePropertyCache::Data &dummy)
1349 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1350 if (current->relatedIndex == -1)
1354 return cache->method(current->relatedIndex);
1356 const QMetaObject *mo = object->metaObject();
1357 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1359 while (methodOffset > current->relatedIndex) {
1360 mo = mo->superClass();
1361 methodOffset -= QMetaObject_methods(mo);
1364 QMetaMethod method = mo->method(current->relatedIndex);
1367 // Look for overloaded methods
1368 QByteArray methodName = QMetaMethod_name(method);
1369 for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1370 if (methodName == QMetaMethod_name(mo->method(ii))) {
1371 dummy.relatedIndex = ii;
1380 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
1381 QV8Engine *engine, CallArgs &callArgs)
1383 if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
1385 QMetaMethod m = object->metaObject()->method(data.coreIndex);
1386 QList<QByteArray> argTypeNames = m.parameterTypes();
1387 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1390 for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1391 argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1392 if (argTypes[ii] == QVariant::Invalid)
1393 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1394 if (argTypes[ii] == QVariant::Invalid) {
1395 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1396 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1397 return v8::Handle<v8::Value>();
1401 if (argTypes.count() > callArgs.Length()) {
1402 QString error = QLatin1String("Insufficient arguments");
1403 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1404 return v8::Handle<v8::Value>();
1407 return CallMethod(object, data.coreIndex, data.propType, argTypes.count(),
1408 argTypes.data(), engine, callArgs);
1412 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1418 Resolve the overloaded method to call. The algorithm works conceptually like this:
1419 1. Resolve the set of overloads it is *possible* to call.
1420 Impossible overloads include those that have too many parameters or have parameters
1422 2. Filter the set of overloads to only contain those with the closest number of
1424 For example, if we are called with 3 parameters and there are 2 overloads that
1425 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1426 3. Find the best remaining overload based on its match score.
1427 If two or more overloads have the same match score, call the last one. The match
1428 score is constructed by adding the matchScore() result for each of the parameters.
1430 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data,
1431 QV8Engine *engine, CallArgs &callArgs)
1433 int argumentCount = callArgs.Length();
1435 const QDeclarativePropertyCache::Data *best = 0;
1436 int bestParameterScore = INT_MAX;
1437 int bestMatchScore = INT_MAX;
1439 QDeclarativePropertyCache::Data dummy;
1440 const QDeclarativePropertyCache::Data *attempt = &data;
1443 QList<QByteArray> methodArgTypeNames;
1445 if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1446 methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1448 int methodArgumentCount = methodArgTypeNames.count();
1450 if (methodArgumentCount > argumentCount)
1451 continue; // We don't have sufficient arguments to call this method
1453 int methodParameterScore = argumentCount - methodArgumentCount;
1454 if (methodParameterScore > bestParameterScore)
1455 continue; // We already have a better option
1457 int methodMatchScore = 0;
1458 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1460 bool unknownArgument = false;
1461 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1462 methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1463 if (methodArgTypes[ii] == QVariant::Invalid)
1464 methodArgTypes[ii] = EnumType(object->metaObject(),
1465 QString::fromLatin1(methodArgTypeNames.at(ii)));
1466 if (methodArgTypes[ii] == QVariant::Invalid) {
1467 unknownArgument = true;
1470 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1472 if (unknownArgument)
1473 continue; // We don't understand all the parameters
1475 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1477 bestParameterScore = methodParameterScore;
1478 bestMatchScore = methodMatchScore;
1481 if (bestParameterScore == 0 && bestMatchScore == 0)
1482 break; // We can't get better than that
1484 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1487 return CallPrecise(object, *best, engine, callArgs);
1489 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1490 const QDeclarativePropertyCache::Data *candidate = &data;
1492 error += QLatin1String("\n ") +
1493 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1494 candidate = RelatedMethod(object, candidate, dummy);
1497 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1498 return v8::Handle<v8::Value>();
1502 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1506 QString objectName = object->objectName();
1508 result += QString::fromUtf8(object->metaObject()->className());
1509 result += QLatin1String("(0x");
1510 result += QString::number((quintptr)object,16);
1512 if (!objectName.isEmpty()) {
1513 result += QLatin1String(", \"");
1514 result += objectName;
1515 result += QLatin1Char('\"');
1518 result += QLatin1Char(')');
1520 result = QLatin1String("null");
1523 return engine->toString(result);
1526 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1528 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1529 if (!ddata || ddata->indestructible) {
1530 const char *error = "Invalid attempt to destroy() an indestructible object";
1531 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1532 return v8::Undefined();
1537 delay = args->Get(0)->Uint32Value();
1540 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1542 object->deleteLater();
1544 return v8::Undefined();
1547 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1549 // object, index, qmlglobal, argCount, args
1550 Q_ASSERT(args.Length() == 5);
1551 Q_ASSERT(args[0]->IsObject());
1553 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1556 return v8::Undefined();
1558 int argCount = args[3]->Int32Value();
1559 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1561 // Special hack to return info about this closure.
1562 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1563 v8::Local<v8::Array> data = v8::Array::New(2);
1564 data->Set(0, args[0]);
1565 data->Set(1, args[1]);
1569 QObject *object = resource->object;
1570 int index = args[1]->Int32Value();
1573 return v8::Undefined();
1576 // Builtin functions
1577 if (index == QOBJECT_TOSTRING_INDEX) {
1578 return ToString(resource->engine, object, argCount, arguments);
1579 } else if (index == QOBJECT_DESTROY_INDEX) {
1580 return Destroy(resource->engine, object, argCount, arguments);
1582 return v8::Undefined();
1586 QDeclarativePropertyCache::Data method;
1588 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1589 if (ddata->propertyCache) {
1590 QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1592 return v8::Undefined();
1597 if (method.coreIndex == -1) {
1598 QMetaMethod mm = object->metaObject()->method(index);
1599 method.load(object->metaObject()->method(index));
1601 if (method.coreIndex == -1)
1602 return v8::Undefined();
1605 if (method.flags & QDeclarativePropertyCache::Data::IsV8Function) {
1606 v8::Handle<v8::Value> rv;
1607 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1609 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1610 resource->engine->contextWrapper()->context(qmlglobal),
1612 QDeclarativeV8Function *funcptr = &func;
1614 void *args[] = { 0, &funcptr };
1615 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1617 if (rv.IsEmpty()) return v8::Undefined();
1621 CallArgs callArgs(argCount, &arguments);
1622 if (method.relatedIndex == -1) {
1623 return CallPrecise(object, method, resource->engine, callArgs);
1625 return CallOverloaded(object, method, resource->engine, callArgs);
1629 MetaCallArgument::MetaCallArgument()
1630 : type(QVariant::Invalid), isObjectType(false)
1634 MetaCallArgument::~MetaCallArgument()
1639 void MetaCallArgument::cleanup()
1641 if (type == QMetaType::QString) {
1642 ((QString *)&data)->~QString();
1643 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1644 ((QVariant *)&data)->~QVariant();
1645 } else if (type == qMetaTypeId<QScriptValue>()) {
1646 ((QScriptValue *)&data)->~QScriptValue();
1647 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1648 ((QList<QObject *> *)&data)->~QList<QObject *>();
1652 void *MetaCallArgument::dataPtr()
1655 return ((QVariant *)data)->data();
1657 return (void *)&data;
1660 void MetaCallArgument::initAsType(int callType)
1662 if (type != 0) { cleanup(); type = 0; }
1663 if (callType == 0) return;
1665 if (callType == qMetaTypeId<QScriptValue>()) {
1666 new (&data) QScriptValue();
1668 } else if (callType == QMetaType::Int ||
1669 callType == QMetaType::UInt ||
1670 callType == QMetaType::Bool ||
1671 callType == QMetaType::Double ||
1672 callType == QMetaType::Float) {
1674 } else if (callType == QMetaType::QObjectStar) {
1675 *((QObject **)&data) = 0;
1677 } else if (callType == QMetaType::QString) {
1678 new (&data) QString();
1680 } else if (callType == qMetaTypeId<QVariant>()) {
1682 new (&data) QVariant();
1683 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1685 new (&data) QList<QObject *>();
1686 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1688 new (&data) v8::Handle<v8::Value>();
1691 new (&data) QVariant(callType, (void *)0);
1695 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1697 if (type != 0) { cleanup(); type = 0; }
1699 if (callType == qMetaTypeId<QScriptValue>()) {
1700 new (&data) QScriptValue();
1701 type = qMetaTypeId<QScriptValue>();
1702 } else if (callType == QMetaType::Int) {
1703 *((int *)&data) = int(value->Int32Value());
1705 } else if (callType == QMetaType::UInt) {
1706 *((uint *)&data) = uint(value->Uint32Value());
1708 } else if (callType == QMetaType::Bool) {
1709 *((bool *)&data) = value->BooleanValue();
1711 } else if (callType == QMetaType::Double) {
1712 *((double *)&data) = double(value->NumberValue());
1714 } else if (callType == QMetaType::Float) {
1715 *((float *)&data) = float(value->NumberValue());
1717 } else if (callType == QMetaType::QString) {
1718 if (value->IsNull() || value->IsUndefined())
1719 new (&data) QString();
1721 new (&data) QString(engine->toString(value->ToString()));
1723 } else if (callType == QMetaType::QObjectStar) {
1724 *((QObject **)&data) = engine->toQObject(value);
1726 } else if (callType == qMetaTypeId<QVariant>()) {
1727 new (&data) QVariant(engine->toVariant(value, -1));
1729 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1730 QList<QObject *> *list = new (&data) QList<QObject *>();
1731 if (value->IsArray()) {
1732 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1733 uint32_t length = array->Length();
1734 for (uint32_t ii = 0; ii < length; ++ii)
1735 list->append(engine->toQObject(array->Get(ii)));
1737 list->append(engine->toQObject(value));
1740 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1741 new (&data) v8::Handle<v8::Value>(value);
1744 new (&data) QVariant();
1747 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
1748 QVariant v = engine->toVariant(value, -1);
1750 if (v.userType() == callType) {
1751 *((QVariant *)&data) = v;
1752 } else if (v.canConvert((QVariant::Type)callType)) {
1753 *((QVariant *)&data) = v;
1754 ((QVariant *)&data)->convert((QVariant::Type)callType);
1755 } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) {
1756 QObject *obj = ep->toQObject(v);
1759 const QMetaObject *objMo = obj->metaObject();
1760 while (objMo && objMo != mo) objMo = objMo->superClass();
1761 if (!objMo) obj = 0;
1764 *((QVariant *)&data) = QVariant(callType, &obj);
1766 *((QVariant *)&data) = QVariant(callType, (void *)0);
1771 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1773 if (type == qMetaTypeId<QScriptValue>()) {
1774 return v8::Undefined();
1775 } else if (type == QMetaType::Int) {
1776 return v8::Integer::New(*((int *)&data));
1777 } else if (type == QMetaType::UInt) {
1778 return v8::Integer::NewFromUnsigned(*((uint *)&data));
1779 } else if (type == QMetaType::Bool) {
1780 return v8::Boolean::New(*((bool *)&data));
1781 } else if (type == QMetaType::Double) {
1782 return v8::Number::New(*((double *)&data));
1783 } else if (type == QMetaType::Float) {
1784 return v8::Number::New(*((float *)&data));
1785 } else if (type == QMetaType::QString) {
1786 return engine->toString(*((QString *)&data));
1787 } else if (type == QMetaType::QObjectStar) {
1788 QObject *object = *((QObject **)&data);
1790 QDeclarativeData::get(object, true)->setImplicitDestructible();
1791 return engine->newQObject(object);
1792 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1793 // XXX aakenned Can this be more optimal? Just use Array as a prototype and
1794 // implement directly against QList<QObject*>?
1795 QList<QObject *> &list = *(QList<QObject *>*)&data;
1796 v8::Local<v8::Array> array = v8::Array::New(list.count());
1797 for (int ii = 0; ii < list.count(); ++ii)
1798 array->Set(ii, engine->newQObject(list.at(ii)));
1800 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
1801 return *(v8::Handle<v8::Value>*)&data;
1802 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1803 QVariant value = *((QVariant *)&data);
1804 v8::Handle<v8::Value> rv = engine->fromVariant(value);
1805 if (QObject *object = engine->toQObject(rv))
1806 QDeclarativeData::get(object, true)->setImplicitDestructible();
1809 return v8::Undefined();