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>
58 Q_DECLARE_METATYPE(QScriptValue);
59 Q_DECLARE_METATYPE(QDeclarativeV8Handle);
62 # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405
63 // The code in this file does not violate strict aliasing, but GCC thinks it does
64 // so turn off the warnings for us to have a clean build
65 # pragma GCC diagnostic ignored "-Wstrict-aliasing"
69 #define QOBJECT_TOSTRING_INDEX -2
70 #define QOBJECT_DESTROY_INDEX -3
72 class QV8QObjectResource : public QV8ObjectResource
74 V8_RESOURCE_TYPE(QObjectType);
77 QV8QObjectResource(QV8Engine *engine, QObject *object);
79 QDeclarativeGuard<QObject> object;
83 struct MetaCallArgument {
84 inline MetaCallArgument();
85 inline ~MetaCallArgument();
86 inline void *dataPtr();
88 inline void initAsType(int type);
89 inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>);
90 inline v8::Handle<v8::Value> toValue(QV8Engine *);
93 MetaCallArgument(const MetaCallArgument &);
95 inline void cleanup();
97 char data[4 * sizeof(void *)];
103 QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object)
104 : QV8ObjectResource(engine), object(object)
108 QV8QObjectWrapper::QV8QObjectWrapper()
113 QV8QObjectWrapper::~QV8QObjectWrapper()
117 void QV8QObjectWrapper::destroy()
119 qDeleteAll(m_connections);
120 m_connections.clear();
122 m_hiddenObject.Dispose();
123 m_destroySymbol.Dispose();
124 m_toStringSymbol.Dispose();
125 m_methodConstructor.Dispose();
126 m_constructor.Dispose();
129 #define FAST_VALUE_GETTER(name, cpptype, defaultvalue, constructor) \
130 static v8::Handle<v8::Value> name ## ValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) \
132 v8::Handle<v8::Object> This = info.This(); \
133 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(This); \
135 if (!resource || resource->object.isNull()) return v8::Undefined(); \
137 QObject *object = resource->object; \
139 uint32_t data = info.Data()->Uint32Value(); \
140 int index = data & 0x7FFF; \
141 int notify = (data & 0x7FFF0000) >> 16; \
142 if (notify == 0x7FFF) notify = -1; \
144 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine()); \
145 if (notify /* 0 means constant */ && ep->captureProperties) { \
146 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty; \
147 ep->capturedProperties << CapturedProperty(object, index, notify); \
150 cpptype value = defaultvalue; \
151 void *args[] = { &value, 0 }; \
152 QMetaObject::metacall(object, QMetaObject::ReadProperty, index, args); \
154 return constructor(value); \
157 #define CREATE_FUNCTION \
158 "(function(method) { "\
159 "return (function(object, data, qmlglobal) { "\
160 "return (function() { "\
161 "return method(object, data, qmlglobal, arguments.length, arguments); "\
166 void QV8QObjectWrapper::init(QV8Engine *engine)
170 m_toStringSymbol = v8::Persistent<v8::String>::New(v8::String::NewSymbol("toString"));
171 m_destroySymbol = v8::Persistent<v8::String>::New(v8::String::NewSymbol("destroy"));
172 m_hiddenObject = v8::Persistent<v8::Object>::New(v8::Object::New());
175 v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
176 ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator);
177 ft->InstanceTemplate()->SetHasExternalResource(true);
178 m_constructor = v8::Persistent<v8::Function>::New(ft->GetFunction());
181 v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions
182 v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION), &origin);
183 v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run());
184 v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction();
185 v8::Handle<v8::Value> args[] = { invokeFn };
186 v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args));
187 m_methodConstructor = v8::Persistent<v8::Function>::New(createFn);
191 v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject();
192 prototype->Set(v8::String::New("connect"), V8FUNCTION(Connect, engine));
193 prototype->Set(v8::String::New("disconnect"), V8FUNCTION(Disconnect, engine));
197 bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj)
199 return v8_resource_cast<QV8QObjectResource>(obj) != 0;
202 QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj)
204 QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj);
205 return r?r->object:0;
208 // r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType)
209 QObject *QV8QObjectWrapper::QV8QObjectWrapper::toQObject(QV8ObjectResource *r)
211 Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType);
212 return static_cast<QV8QObjectResource *>(r)->object;
215 // Load value properties
216 static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object,
217 const QDeclarativePropertyCache::Data &property)
219 Q_ASSERT(!property.isFunction());
221 #define PROPERTY_LOAD(metatype, cpptype, constructor) \
222 if (property.propType == QMetaType:: metatype) { \
223 cpptype type = cpptype(); \
224 void *args[] = { &type, 0 }; \
225 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); \
226 return constructor(type); \
229 if (property.isQObject()) {
231 void *args[] = { &rv, 0 };
232 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
233 return engine->newQObject(rv);
234 } else if (property.isQList()) {
235 return engine->listWrapper()->newList(object, property.coreIndex, property.propType);
236 } else PROPERTY_LOAD(QReal, qreal, v8::Number::New)
237 else PROPERTY_LOAD(Int || property.isEnum(), int, v8::Number::New)
238 else PROPERTY_LOAD(Bool, bool, v8::Boolean::New)
239 else PROPERTY_LOAD(QString, QString, engine->toString)
240 else PROPERTY_LOAD(UInt, uint, v8::Integer::NewFromUnsigned)
241 else PROPERTY_LOAD(Float, float, v8::Number::New)
242 else PROPERTY_LOAD(Double, double, v8::Number::New)
243 else if(property.isV8Handle()) {
244 QDeclarativeV8Handle handle;
245 void *args[] = { &handle, 0 };
246 QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args);
247 return reinterpret_cast<v8::Handle<v8::Value> &>(handle);
248 } else if (QDeclarativeValueTypeFactory::isValueType((uint)property.propType)) {
249 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
250 QDeclarativeValueType *valueType = ep->valueTypes[property.propType];
252 return engine->newValueType(object, property.coreIndex, valueType);
255 QVariant var = object->metaObject()->property(property.coreIndex).read(object);
256 return engine->fromVariant(var);
259 v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object,
260 v8::Handle<v8::Value> *objectHandle,
261 v8::Handle<v8::String> property,
262 QV8QObjectWrapper::RevisionMode revisionMode)
264 // XXX aakenned This can't possibly be the best solution!!!
265 struct MethodClosure {
266 static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object,
267 v8::Handle<v8::Value> *objectHandle,
269 v8::Handle<v8::Value> argv[] = {
270 objectHandle?*objectHandle:engine->newQObject(object),
271 v8::Integer::New(index)
273 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv);
275 static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object,
276 v8::Handle<v8::Value> *objectHandle,
278 v8::Handle<v8::Value> argv[] = {
279 objectHandle?*objectHandle:engine->newQObject(object),
280 v8::Integer::New(index),
281 v8::Context::GetCallingQmlGlobal()
283 return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv);
287 if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property)) {
288 return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX);
289 } else if (engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property)) {
290 return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX);
293 QDeclarativePropertyCache::Data local;
294 QDeclarativePropertyCache::Data *result = 0;
295 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
298 return v8::Handle<v8::Value>();
300 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
302 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
303 QDeclarativeData *ddata = QDeclarativeData::get(object);
304 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
305 return v8::Handle<v8::Value>();
308 typedef QDeclarativeEnginePrivate::CapturedProperty CapturedProperty;
310 if (result->isFunction()) {
311 if (result->flags & QDeclarativePropertyCache::Data::IsVMEFunction) {
312 return ((QDeclarativeVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex);
313 } else if (result->flags & QDeclarativePropertyCache::Data::IsV8Function) {
314 return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex);
316 return MethodClosure::create(engine, object, objectHandle, result->coreIndex);
320 if (ep->captureProperties && !result->isConstant()) {
321 if (result->coreIndex == 0)
322 ep->capturedProperties << CapturedProperty(QDeclarativeData::get(object, true)->objectNameNotifier());
324 ep->capturedProperties << CapturedProperty(object, result->coreIndex, result->notifyIndex);
327 return LoadProperty(engine, object, *result);
330 // Setter for writable properties. Shared between the interceptor and fast property accessor
331 static inline void StoreProperty(QV8Engine *engine, QObject *object, QDeclarativePropertyCache::Data *property,
332 v8::Handle<v8::Value> value)
334 QDeclarativeBinding *newBinding = 0;
336 if (value->IsFunction()) {
337 QDeclarativeContextData *context = engine->callingContext();
338 v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value);
340 v8::Local<v8::StackTrace> trace =
341 v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber |
342 v8::StackTrace::kScriptName));
343 v8::Local<v8::StackFrame> frame = trace->GetFrame(0);
344 int lineNumber = frame->GetLineNumber();
345 QString url = engine->toString(frame->GetScriptName());
347 QDeclarativePropertyCache::ValueTypeData valueTypeData;
348 newBinding = new QDeclarativeBinding(&function, object, context);
349 newBinding->setSourceLocation(url, lineNumber);
350 newBinding->setTarget(QDeclarativePropertyPrivate::restore(*property, valueTypeData, object, context));
351 newBinding->setEvaluateFlags(newBinding->evaluateFlags() | QDeclarativeBinding::RequiresThisObject);
354 QDeclarativeAbstractBinding *oldBinding =
355 QDeclarativePropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding);
357 oldBinding->destroy();
359 #define PROPERTY_STORE(cpptype, value) \
363 void *argv[] = { &o, 0, &status, &flags }; \
364 QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv);
367 if (value->IsNull() && property->isQObject()) {
368 PROPERTY_STORE(QObject*, 0);
369 } else if (value->IsUndefined() && property->isResettable()) {
371 QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a);
372 } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) {
373 PROPERTY_STORE(QVariant, QVariant());
374 } else if (value->IsUndefined()) {
375 QString error = QLatin1String("Cannot assign [undefined] to ") +
376 QLatin1String(QMetaType::typeName(property->propType));
377 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
378 } else if (value->IsFunction()) {
379 // this is handled by the binding creation above
380 } else if (property->propType == QMetaType::Int && value->IsNumber()) {
381 PROPERTY_STORE(int, qRound(value->ToNumber()->Value()));
382 } else if (property->propType == QMetaType::QReal && value->IsNumber()) {
383 PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value()));
384 } else if (property->propType == QMetaType::Float && value->IsNumber()) {
385 PROPERTY_STORE(float, float(value->ToNumber()->Value()));
386 } else if (property->propType == QMetaType::Double && value->IsNumber()) {
387 PROPERTY_STORE(double, double(value->ToNumber()->Value()));
388 } else if (property->propType == QMetaType::QString && value->IsString()) {
389 PROPERTY_STORE(QString, engine->toString(value->ToString()));
392 if (property->isQList())
393 v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >());
395 v = engine->toVariant(value, property->propType);
397 QDeclarativeContextData *context = engine->callingContext();
399 if (!QDeclarativePropertyPrivate::write(object, *property, v, context)) {
400 const char *valueType = 0;
401 if (v.userType() == QVariant::Invalid) valueType = "null";
402 else valueType = QMetaType::typeName(v.userType());
404 QString error = QLatin1String("Cannot assign ") +
405 QLatin1String(valueType) +
406 QLatin1String(" to ") +
407 QLatin1String(QMetaType::typeName(property->propType));
408 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
413 bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, v8::Handle<v8::String> property,
414 v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode)
416 if (engine->qobjectWrapper()->m_toStringSymbol->StrictEquals(property) ||
417 engine->qobjectWrapper()->m_destroySymbol->StrictEquals(property))
420 QDeclarativePropertyCache::Data local;
421 QDeclarativePropertyCache::Data *result = 0;
422 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
427 if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) {
428 QDeclarativeData *ddata = QDeclarativeData::get(object);
429 if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result))
433 if (!result->isWritable() && !result->isQList()) {
434 QString error = QLatin1String("Cannot assign to read-only property \"") +
435 engine->toString(property) + QLatin1Char('\"');
436 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
440 StoreProperty(engine, object, result, value);
445 v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property,
446 const v8::AccessorInfo &info)
448 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
449 v8::Handle<v8::Value> This = info.This();
451 if (!resource || resource->object.isNull()) return v8::Undefined();
453 QObject *object = resource->object;
455 QV8Engine *v8engine = resource->engine;
456 v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, property, QV8QObjectWrapper::IgnoreRevision);
457 if (!result.IsEmpty())
460 if (QV8Engine::startsWithUpper(property)) {
461 // Check for attached properties
462 QDeclarativeContextData *context = v8engine->callingContext();
463 QDeclarativeTypeNameCache::Data *data = context && (context->imports)?context->imports->data(property):0;
467 return v8engine->typeWrapper()->newObject(object, data->type, QV8TypeWrapper::ExcludeEnums);
468 } else if (data->typeNamespace) {
469 return v8engine->typeWrapper()->newObject(object, data->typeNamespace, QV8TypeWrapper::ExcludeEnums);
473 return v8::Undefined();
476 return v8::Undefined();
480 v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property,
481 v8::Local<v8::Value> value,
482 const v8::AccessorInfo &info)
484 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
486 if (!resource || resource->object.isNull())
489 QObject *object = resource->object;
491 QV8Engine *v8engine = resource->engine;
492 bool result = SetProperty(v8engine, object, property, value, QV8QObjectWrapper::IgnoreRevision);
495 QString error = QLatin1String("Cannot assign to non-existent property \"") +
496 v8engine->toString(property) + QLatin1Char('\"');
497 v8::ThrowException(v8::Exception::Error(v8engine->toString(error)));
504 v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property,
505 const v8::AccessorInfo &info)
507 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
509 if (!resource || resource->object.isNull())
510 return v8::Handle<v8::Integer>();
512 QV8Engine *engine = resource->engine;
513 QObject *object = resource->object;
515 QDeclarativePropertyCache::Data local;
516 QDeclarativePropertyCache::Data *result = 0;
517 result = QDeclarativePropertyCache::property(engine->engine(), object, property, local);
520 return v8::Handle<v8::Integer>();
521 else if (!result->isWritable() && !result->isQList())
522 return v8::Integer::New(v8::ReadOnly | v8::DontDelete);
524 return v8::Integer::New(v8::DontDelete);
527 v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info)
529 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
531 if (!resource || resource->object.isNull())
532 return v8::Array::New();
534 QObject *object = resource->object;
538 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine());
540 QDeclarativePropertyCache *cache = 0;
541 QDeclarativeData *ddata = QDeclarativeData::get(object);
543 cache = ddata->propertyCache;
546 cache = ep->cache(object);
548 if (ddata) { cache->addref(); ddata->propertyCache = cache; }
550 // Not cachable - fall back to QMetaObject (eg. dynamic meta object)
551 // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal.
552 // XXX This is a workaround for QTBUG-9420.
553 const QMetaObject *mo = object->metaObject();
554 int pc = mo->propertyCount();
555 int po = mo->propertyOffset();
556 for (int i=po; i<pc; ++i)
557 result << QString::fromUtf8(mo->property(i).name());
560 result = cache->propertyNames();
563 v8::Local<v8::Array> rv = v8::Array::New(result.count());
565 for (int ii = 0; ii < result.count(); ++ii)
566 rv->Set(ii, resource->engine->toString(result.at(ii)));
571 FAST_VALUE_GETTER(QObject, QObject*, 0, resource->engine->newQObject);
572 FAST_VALUE_GETTER(Int, int, 0, v8::Integer::New);
573 FAST_VALUE_GETTER(Bool, bool, false, v8::Boolean::New);
574 FAST_VALUE_GETTER(QString, QString, QString(), resource->engine->toString);
575 FAST_VALUE_GETTER(UInt, uint, 0, v8::Integer::NewFromUnsigned);
576 FAST_VALUE_GETTER(Float, float, 0, v8::Number::New);
577 FAST_VALUE_GETTER(Double, double, 0, v8::Number::New);
579 static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value,
580 const v8::AccessorInfo& info)
582 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(info.This());
584 if (!resource || resource->object.isNull())
587 QObject *object = resource->object;
589 uint32_t data = info.Data()->Uint32Value();
590 int index = data & 0x7FFF; // So that we can use the same data for Setter and Getter
592 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
594 Q_ASSERT(ddata->propertyCache);
596 QDeclarativePropertyCache::Data *pdata = ddata->propertyCache->property(index);
599 Q_ASSERT(pdata->isWritable() || pdata->isQList());
601 StoreProperty(resource->engine, object, pdata, value);
604 static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *)
606 Q_ASSERT(handle->IsObject());
608 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(handle->ToObject());
612 QObject *object = resource->object;
614 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
616 ddata->v8object.Clear();
617 if (!object->parent() && !ddata->indestructible)
618 object->deleteLater();
622 // XXX do we want to use the objectDataRefCount to support multiple concurrent engines?
627 v8::Local<v8::Object> QDeclarativePropertyCache::newQObject(QObject *object, QV8Engine *engine)
631 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
633 Q_ASSERT(ddata && ddata->propertyCache && ddata->propertyCache == this);
634 Q_ASSERT(ddata->v8object.IsEmpty());
637 if (constructor.IsEmpty()) {
638 v8::Local<v8::FunctionTemplate> ft;
640 QString toString = QLatin1String("toString");
641 QString destroy = QLatin1String("destroy");
643 // XXX Enables fast property accessors. These more than double the property access
644 // performance, but the cost of setting up this structure hasn't been measured so
645 // its not guarenteed that this is a win overall
646 for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) {
647 Data *property = *iter;
648 if (property->isFunction() || !property->isWritable() ||
649 property->coreIndex >= 0x7FFF || property->notifyIndex >= 0x7FFF ||
650 property->coreIndex == 0)
653 v8::AccessorGetter fastgetter = 0;
656 if (property->isQObject())
657 fastgetter = QObjectValueGetter;
658 else if (property->propType == QMetaType::Int || property->isEnum())
659 fastgetter = IntValueGetter;
660 else if (property->propType == QMetaType::Bool)
661 fastgetter = BoolValueGetter;
662 else if (property->propType == QMetaType::QString)
663 fastgetter = QStringValueGetter;
664 else if (property->propType == QMetaType::UInt)
665 fastgetter = UIntValueGetter;
666 else if (property->propType == QMetaType::Float)
667 fastgetter = FloatValueGetter;
668 else if (property->propType == QMetaType::Double)
669 fastgetter = DoubleValueGetter;
672 int notifyIndex = property->notifyIndex;
673 if (property->isConstant()) notifyIndex = 0;
674 else if (notifyIndex == -1) notifyIndex = 0x7FFF;
675 uint32_t data = (property->notifyIndex & 0x7FFF) << 16 | property->coreIndex;
677 QString name = iter.key();
678 if (name == toString || name == destroy)
682 ft = v8::FunctionTemplate::New();
683 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
684 QV8QObjectWrapper::Setter,
685 QV8QObjectWrapper::Query,
687 QV8QObjectWrapper::Enumerator);
688 ft->InstanceTemplate()->SetHasExternalResource(true);
691 ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, FastValueSetter,
692 v8::Integer::NewFromUnsigned(data));
697 constructor = v8::Persistent<v8::Function>::New(engine->qobjectWrapper()->m_constructor);
699 ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter,
700 QV8QObjectWrapper::Setter,
701 QV8QObjectWrapper::Query,
703 QV8QObjectWrapper::Enumerator);
704 ft->InstanceTemplate()->SetHasExternalResource(true);
705 constructor = v8::Persistent<v8::Function>::New(ft->GetFunction());
709 v8::Local<v8::Object> result = constructor->NewInstance();
710 QV8QObjectResource *r = new QV8QObjectResource(engine, object);
711 result->SetExternalResource(r);
713 ddata->v8object = v8::Persistent<v8::Object>::New(result);
714 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
718 v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object)
720 // XXX aakenned QDeclarativeObjectScriptClass::newQObject() does a lot more
725 if (QObjectPrivate::get(object)->wasDeleted)
726 return v8::Undefined();
728 QDeclarativeData *ddata = QDeclarativeData::get(object, true);
731 return v8::Undefined();
733 if (ddata->v8object.IsEmpty()) {
735 if (ddata->propertyCache) {
736 return ddata->propertyCache->newQObject(object, m_engine);
739 // XXX aakenned - NewInstance() is slow for our case
740 v8::Local<v8::Object> rv = m_constructor->NewInstance();
741 QV8QObjectResource *r = new QV8QObjectResource(m_engine, object);
742 rv->SetExternalResource(r);
743 ddata->v8object = v8::Persistent<v8::Object>::New(rv);
746 // XXX do we have to check that the v8object isn't "owned" by another engine?
748 ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback);
749 return v8::Local<v8::Object>::New(ddata->v8object);
752 QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function)
754 v8::ScriptOrigin origin = function->GetScriptOrigin();
755 if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) {
757 // This is one of our special QObject method wrappers
758 v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject };
759 v8::Local<v8::Value> data = function->Call(engine->global(), 1, args);
761 if (data->IsArray()) {
762 v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data);
763 return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value());
766 // In theory this can't fall through, but I suppose V8 might run out of memory or something
769 return qMakePair((QObject *)0, -1);
772 struct QV8QObjectConnectionList : public QObject, public QDeclarativeGuard<QObject>
774 QV8QObjectConnectionList(QObject *object, QV8Engine *engine);
775 ~QV8QObjectConnectionList();
778 v8::Persistent<v8::Object> thisObject;
779 v8::Persistent<v8::Function> function;
784 typedef QHash<int, QList<Connection> > SlotHash;
787 virtual void objectDestroyed(QObject *);
788 virtual int qt_metacall(QMetaObject::Call, int, void **);
791 QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine)
792 : QDeclarativeGuard<QObject>(object), engine(engine)
796 QV8QObjectConnectionList::~QV8QObjectConnectionList()
798 for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) {
799 QList<Connection> &connections = *iter;
800 for (int ii = 0; ii < connections.count(); ++ii) {
801 connections[ii].thisObject.Dispose();
802 connections[ii].function.Dispose();
808 void QV8QObjectConnectionList::objectDestroyed(QObject *object)
810 engine->qobjectWrapper()->m_connections.remove(object);
814 int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs)
816 if (method == QMetaObject::InvokeMetaMethod) {
817 SlotHash::Iterator iter = slotHash.find(index);
818 if (iter == slotHash.end())
820 QList<Connection> &connections = *iter;
821 if (connections.isEmpty())
825 QMetaMethod method = data()->metaObject()->method(index);
826 Q_ASSERT(method.methodType() == QMetaMethod::Signal);
827 QList<QByteArray> params = method.parameterTypes();
829 v8::HandleScope handle_scope;
830 v8::Context::Scope scope(engine->context());
832 QVarLengthArray<v8::Handle<v8::Value> > args(params.count());
833 int argCount = params.count();
835 for (int ii = 0; ii < argCount; ++ii) {
836 int type = QMetaType::type(params.at(ii).constData());
837 if (type == qMetaTypeId<QVariant>()) {
838 args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1]));
840 args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1]));
844 // XXX what if this list changes or this object is deleted during the calls?
845 for (int ii = 0; ii < connections.count(); ++ii) {
846 Connection &connection = connections[ii];
847 if (connection.thisObject.IsEmpty()) {
848 connection.function->Call(engine->global(), argCount, args.data());
850 connection.function->Call(connection.thisObject, argCount, args.data());
858 v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args)
860 if (args.Length() == 0)
861 V8THROW_ERROR("Function.prototype.connect: no arguments given");
863 QV8Engine *engine = V8ENGINE();
865 if (!args.This()->IsFunction())
866 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
868 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
869 QObject *signalObject = signalInfo.first;
870 int signalIndex = signalInfo.second;
872 if (signalIndex == -1)
873 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
876 V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject");
878 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
879 V8THROW_ERROR("Function.prototype.connect: this object is not a signal");
881 v8::Local<v8::Value> functionValue;
882 v8::Local<v8::Value> functionThisValue;
884 if (args.Length() == 1) {
885 functionValue = args[0];
887 functionThisValue = args[0];
888 functionValue = args[1];
891 if (!functionValue->IsFunction())
892 V8THROW_ERROR("Function.prototype.connect: target is not a function");
894 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
895 V8THROW_ERROR("Function.prototype.connect: target this is not an object");
897 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
898 QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections;
899 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject);
900 if (iter == connections.end())
901 iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine));
903 QV8QObjectConnectionList *connectionList = *iter;
904 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
905 if (slotIter == connectionList->slotHash.end()) {
906 slotIter = connectionList->slotHash.insert(signalIndex, QList<QV8QObjectConnectionList::Connection>());
907 QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex);
910 QV8QObjectConnectionList::Connection connection;
911 if (!functionThisValue.IsEmpty())
912 connection.thisObject = v8::Persistent<v8::Object>::New(functionThisValue->ToObject());
913 connection.function = v8::Persistent<v8::Function>::New(v8::Handle<v8::Function>::Cast(functionValue));
915 slotIter->append(connection);
917 return v8::Undefined();
920 v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args)
922 if (args.Length() == 0)
923 V8THROW_ERROR("Function.prototype.disconnect: no arguments given");
925 QV8Engine *engine = V8ENGINE();
927 if (!args.This()->IsFunction())
928 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
930 QPair<QObject *, int> signalInfo = ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(args.This()));
931 QObject *signalObject = signalInfo.first;
932 int signalIndex = signalInfo.second;
934 if (signalIndex == -1)
935 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
938 V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject");
940 if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal)
941 V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal");
943 v8::Local<v8::Value> functionValue;
944 v8::Local<v8::Value> functionThisValue;
946 if (args.Length() == 1) {
947 functionValue = args[0];
949 functionThisValue = args[0];
950 functionValue = args[1];
953 if (!functionValue->IsFunction())
954 V8THROW_ERROR("Function.prototype.disconnect: target is not a function");
956 if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject())
957 V8THROW_ERROR("Function.prototype.disconnect: target this is not an object");
959 QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper();
960 QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections;
961 QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject);
962 if (iter == connectionsList.end())
963 return v8::Undefined(); // Nothing to disconnect from
965 QV8QObjectConnectionList *connectionList = *iter;
966 QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex);
967 if (slotIter == connectionList->slotHash.end())
968 return v8::Undefined(); // Nothing to disconnect from
970 QList<QV8QObjectConnectionList::Connection> &connections = *slotIter;
972 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue);
973 QPair<QObject *, int> functionData = ExtractQtMethod(engine, function);
975 if (functionData.second != -1) {
976 // This is a QObject function wrapper
977 for (int ii = 0; ii < connections.count(); ++ii) {
978 QV8QObjectConnectionList::Connection &connection = connections[ii];
980 if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
981 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
983 QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function);
984 if (connectedFunctionData == functionData) {
986 connection.thisObject.Dispose();
987 connection.function.Dispose();
988 connections.removeAt(ii);
989 return v8::Undefined();
995 // This is a normal JS function
996 for (int ii = 0; ii < connections.count(); ++ii) {
997 QV8QObjectConnectionList::Connection &connection = connections[ii];
998 if (connection.function->StrictEquals(function) &&
999 connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() &&
1000 (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) {
1002 connection.thisObject.Dispose();
1003 connection.function.Dispose();
1004 connections.removeAt(ii);
1005 return v8::Undefined();
1010 return v8::Undefined();
1014 Get the \a property of \a object. Returns an empty handle if the property doesn't exist.
1016 Only searches for real properties of \a object (including methods), not attached properties etc.
1018 v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, v8::Handle<v8::String> property,
1019 QV8QObjectWrapper::RevisionMode revisionMode)
1021 return GetProperty(m_engine, object, 0, property, revisionMode);
1025 Set the \a property of \a object to \a value.
1027 Returns true if the property was "set" - even if this results in an exception being thrown -
1028 and false if the object has no such property.
1030 Only searches for real properties of \a object (including methods), not attached properties etc.
1032 bool QV8QObjectWrapper::setProperty(QObject *object, v8::Handle<v8::String> property,
1033 v8::Handle<v8::Value> value, RevisionMode revisionMode)
1035 return SetProperty(m_engine, object, property, value, revisionMode);
1041 CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {}
1042 int Length() const { return _length; }
1043 v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); }
1047 v8::Handle<v8::Object> *_args;
1051 static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount,
1052 int *argTypes, QV8Engine *engine, CallArgs &callArgs)
1056 QVarLengthArray<MetaCallArgument, 9> args(argCount + 1);
1057 args[0].initAsType(returnType);
1059 for (int ii = 0; ii < argCount; ++ii)
1060 args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]);
1062 QVarLengthArray<void *, 9> argData(args.count());
1063 for (int ii = 0; ii < args.count(); ++ii)
1064 argData[ii] = args[ii].dataPtr();
1066 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data());
1068 return args[0].toValue(engine);
1070 } else if (returnType != 0) {
1072 MetaCallArgument arg;
1073 arg.initAsType(returnType);
1075 void *args[] = { arg.dataPtr() };
1077 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1079 return arg.toValue(engine);
1083 void *args[] = { 0 };
1084 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args);
1085 return v8::Undefined();
1090 static int EnumType(const QMetaObject *meta, const QString &strname)
1092 QByteArray str = strname.toUtf8();
1095 int scopeIdx = str.lastIndexOf("::");
1096 if (scopeIdx != -1) {
1097 scope = str.left(scopeIdx);
1098 name = str.mid(scopeIdx + 2);
1102 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
1103 QMetaEnum m = meta->enumerator(i);
1104 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
1105 return QVariant::Int;
1107 return QVariant::Invalid;
1111 Returns the match score for converting \a actual to be of type \a conversionType. A
1112 zero score means "perfect match" whereas a higher score is worse.
1114 The conversion table is copied out of the QtScript callQtMethod() function.
1116 static int MatchScore(v8::Handle<v8::Value> actual, int conversionType,
1117 const QByteArray &conversionTypeName)
1119 if (actual->IsNumber()) {
1120 switch (conversionType) {
1121 case QMetaType::Double:
1123 case QMetaType::Float:
1125 case QMetaType::LongLong:
1126 case QMetaType::ULongLong:
1128 case QMetaType::Long:
1129 case QMetaType::ULong:
1131 case QMetaType::Int:
1132 case QMetaType::UInt:
1134 case QMetaType::Short:
1135 case QMetaType::UShort:
1138 case QMetaType::Char:
1139 case QMetaType::UChar:
1144 } else if (actual->IsString()) {
1145 switch (conversionType) {
1146 case QMetaType::QString:
1151 } else if (actual->IsBoolean()) {
1152 switch (conversionType) {
1153 case QMetaType::Bool:
1158 } else if (actual->IsDate()) {
1159 switch (conversionType) {
1160 case QMetaType::QDateTime:
1162 case QMetaType::QDate:
1164 case QMetaType::QTime:
1169 } else if (actual->IsRegExp()) {
1170 switch (conversionType) {
1171 case QMetaType::QRegExp:
1176 } else if (actual->IsArray()) {
1177 switch (conversionType) {
1178 case QMetaType::QStringList:
1179 case QMetaType::QVariantList:
1184 } else if (actual->IsNull()) {
1185 switch (conversionType) {
1186 case QMetaType::VoidStar:
1187 case QMetaType::QObjectStar:
1190 if (!conversionTypeName.endsWith('*'))
1195 } else if (actual->IsObject()) {
1196 v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual);
1198 QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource());
1199 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
1200 switch (conversionType) {
1201 case QMetaType::QObjectStar:
1206 } else if (r && r->resourceType() == QV8ObjectResource::VariantType) {
1207 if (conversionType == qMetaTypeId<QVariant>())
1209 else if (r->engine->toVariant(actual, -1).userType() == conversionType)
1222 static inline int QMetaObject_methods(const QMetaObject *metaObject)
1228 int classInfoCount, classInfoData;
1229 int methodCount, methodData;
1232 return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount;
1235 static QByteArray QMetaMethod_name(const QMetaMethod &m)
1237 QByteArray sig = m.signature();
1238 int paren = sig.indexOf('(');
1242 return sig.left(paren);
1246 Returns the next related method, if one, or 0.
1248 static const QDeclarativePropertyCache::Data * RelatedMethod(QObject *object,
1249 const QDeclarativePropertyCache::Data *current,
1250 QDeclarativePropertyCache::Data &dummy)
1252 QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache;
1253 if (current->relatedIndex == -1)
1257 return cache->method(current->relatedIndex);
1259 const QMetaObject *mo = object->metaObject();
1260 int methodOffset = mo->methodCount() - QMetaObject_methods(mo);
1262 while (methodOffset > current->relatedIndex) {
1263 mo = mo->superClass();
1264 methodOffset -= QMetaObject_methods(mo);
1267 QMetaMethod method = mo->method(current->relatedIndex);
1270 // Look for overloaded methods
1271 QByteArray methodName = QMetaMethod_name(method);
1272 for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) {
1273 if (methodName == QMetaMethod_name(mo->method(ii))) {
1274 dummy.relatedIndex = ii;
1283 static v8::Handle<v8::Value> CallPrecise(QObject *object, const QDeclarativePropertyCache::Data &data,
1284 QV8Engine *engine, CallArgs &callArgs)
1286 if (data.flags & QDeclarativePropertyCache::Data::HasArguments) {
1288 QMetaMethod m = object->metaObject()->method(data.coreIndex);
1289 QList<QByteArray> argTypeNames = m.parameterTypes();
1290 QVarLengthArray<int, 9> argTypes(argTypeNames.count());
1293 for (int ii = 0; ii < argTypeNames.count(); ++ii) {
1294 argTypes[ii] = QMetaType::type(argTypeNames.at(ii));
1295 if (argTypes[ii] == QVariant::Invalid)
1296 argTypes[ii] = EnumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii)));
1297 if (argTypes[ii] == QVariant::Invalid) {
1298 QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii)));
1299 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1300 return v8::Handle<v8::Value>();
1304 if (argTypes.count() > callArgs.Length()) {
1305 QString error = QLatin1String("Insufficient arguments");
1306 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1307 return v8::Handle<v8::Value>();
1310 return CallMethod(object, data.coreIndex, data.propType, argTypes.count(),
1311 argTypes.data(), engine, callArgs);
1315 return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs);
1321 Resolve the overloaded method to call. The algorithm works conceptually like this:
1322 1. Resolve the set of overloads it is *possible* to call.
1323 Impossible overloads include those that have too many parameters or have parameters
1325 2. Filter the set of overloads to only contain those with the closest number of
1327 For example, if we are called with 3 parameters and there are 2 overloads that
1328 take 2 parameters and one that takes 3, eliminate the 2 parameter overloads.
1329 3. Find the best remaining overload based on its match score.
1330 If two or more overloads have the same match score, call the last one. The match
1331 score is constructed by adding the matchScore() result for each of the parameters.
1333 static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QDeclarativePropertyCache::Data &data,
1334 QV8Engine *engine, CallArgs &callArgs)
1336 int argumentCount = callArgs.Length();
1338 const QDeclarativePropertyCache::Data *best = 0;
1339 int bestParameterScore = INT_MAX;
1340 int bestMatchScore = INT_MAX;
1342 QDeclarativePropertyCache::Data dummy;
1343 const QDeclarativePropertyCache::Data *attempt = &data;
1346 QList<QByteArray> methodArgTypeNames;
1348 if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments)
1349 methodArgTypeNames = object->metaObject()->method(attempt->coreIndex).parameterTypes();
1351 int methodArgumentCount = methodArgTypeNames.count();
1353 if (methodArgumentCount > argumentCount)
1354 continue; // We don't have sufficient arguments to call this method
1356 int methodParameterScore = argumentCount - methodArgumentCount;
1357 if (methodParameterScore > bestParameterScore)
1358 continue; // We already have a better option
1360 int methodMatchScore = 0;
1361 QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount);
1363 bool unknownArgument = false;
1364 for (int ii = 0; ii < methodArgumentCount; ++ii) {
1365 methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii));
1366 if (methodArgTypes[ii] == QVariant::Invalid)
1367 methodArgTypes[ii] = EnumType(object->metaObject(),
1368 QString::fromLatin1(methodArgTypeNames.at(ii)));
1369 if (methodArgTypes[ii] == QVariant::Invalid) {
1370 unknownArgument = true;
1373 methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii], methodArgTypeNames.at(ii));
1375 if (unknownArgument)
1376 continue; // We don't understand all the parameters
1378 if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) {
1380 bestParameterScore = methodParameterScore;
1381 bestMatchScore = methodMatchScore;
1384 if (bestParameterScore == 0 && bestMatchScore == 0)
1385 break; // We can't get better than that
1387 } while((attempt = RelatedMethod(object, attempt, dummy)) != 0);
1390 return CallPrecise(object, *best, engine, callArgs);
1392 QString error = QLatin1String("Unable to determine callable overload. Candidates are:");
1393 const QDeclarativePropertyCache::Data *candidate = &data;
1395 error += QLatin1String("\n ") +
1396 QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature());
1397 candidate = RelatedMethod(object, candidate, dummy);
1400 v8::ThrowException(v8::Exception::Error(engine->toString(error)));
1401 return v8::Handle<v8::Value>();
1405 static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>)
1409 QString objectName = object->objectName();
1411 result += QString::fromUtf8(object->metaObject()->className());
1412 result += QLatin1String("(0x");
1413 result += QString::number((quintptr)object,16);
1415 if (!objectName.isEmpty()) {
1416 result += QLatin1String(", \"");
1417 result += objectName;
1418 result += QLatin1Char('\"');
1421 result += QLatin1Char(')');
1423 result = QLatin1String("null");
1426 return engine->toString(result);
1429 static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args)
1431 QDeclarativeData *ddata = QDeclarativeData::get(object, false);
1432 if (!ddata || ddata->indestructible) {
1433 const char *error = "Invalid attempt to destroy() an indestructible object";
1434 v8::ThrowException(v8::Exception::Error(v8::String::New(error)));
1435 return v8::Undefined();
1440 delay = args->Get(0)->Uint32Value();
1443 QTimer::singleShot(delay, object, SLOT(deleteLater()));
1445 object->deleteLater();
1447 return v8::Undefined();
1450 v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args)
1452 // object, index, qmlglobal, argCount, args
1453 Q_ASSERT(args.Length() == 5);
1454 Q_ASSERT(args[0]->IsObject());
1456 QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject());
1459 return v8::Undefined();
1461 int argCount = args[3]->Int32Value();
1462 v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]);
1464 // Special hack to return info about this closure.
1465 if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) {
1466 v8::Local<v8::Array> data = v8::Array::New(2);
1467 data->Set(0, args[0]);
1468 data->Set(1, args[1]);
1472 QObject *object = resource->object;
1473 int index = args[1]->Int32Value();
1476 return v8::Undefined();
1479 // Builtin functions
1480 if (index == QOBJECT_TOSTRING_INDEX) {
1481 return ToString(resource->engine, object, argCount, arguments);
1482 } else if (index == QOBJECT_DESTROY_INDEX) {
1483 return Destroy(resource->engine, object, argCount, arguments);
1485 return v8::Undefined();
1489 QDeclarativePropertyCache::Data method;
1491 if (QDeclarativeData *ddata = static_cast<QDeclarativeData *>(QObjectPrivate::get(object)->declarativeData)) {
1492 if (ddata->propertyCache) {
1493 QDeclarativePropertyCache::Data *d = ddata->propertyCache->method(index);
1495 return v8::Undefined();
1500 if (method.coreIndex == -1) {
1501 QMetaMethod mm = object->metaObject()->method(index);
1502 method.load(object->metaObject()->method(index));
1504 if (method.coreIndex == -1)
1505 return v8::Undefined();
1508 if (method.flags & QDeclarativePropertyCache::Data::IsV8Function) {
1509 v8::Handle<v8::Value> rv;
1510 v8::Handle<v8::Object> qmlglobal = args[2]->ToObject();
1512 QDeclarativeV8Function func(argCount, arguments, rv, qmlglobal,
1513 resource->engine->contextWrapper()->context(qmlglobal),
1515 QDeclarativeV8Function *funcptr = &func;
1517 void *args[] = { 0, &funcptr };
1518 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args);
1520 if (rv.IsEmpty()) return v8::Undefined();
1524 CallArgs callArgs(argCount, &arguments);
1525 if (method.relatedIndex == -1) {
1526 return CallPrecise(object, method, resource->engine, callArgs);
1528 return CallOverloaded(object, method, resource->engine, callArgs);
1532 MetaCallArgument::MetaCallArgument()
1533 : type(QVariant::Invalid), isObjectType(false)
1537 MetaCallArgument::~MetaCallArgument()
1542 void MetaCallArgument::cleanup()
1544 if (type == QMetaType::QString) {
1545 ((QString *)&data)->~QString();
1546 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1547 ((QVariant *)&data)->~QVariant();
1548 } else if (type == qMetaTypeId<QScriptValue>()) {
1549 ((QScriptValue *)&data)->~QScriptValue();
1550 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1551 ((QList<QObject *> *)&data)->~QList<QObject *>();
1555 void *MetaCallArgument::dataPtr()
1558 return ((QVariant *)data)->data();
1560 return (void *)&data;
1563 void MetaCallArgument::initAsType(int callType)
1565 if (type != 0) { cleanup(); type = 0; }
1566 if (callType == 0) return;
1568 if (callType == qMetaTypeId<QScriptValue>()) {
1569 new (&data) QScriptValue();
1571 } else if (callType == QMetaType::Int ||
1572 callType == QMetaType::UInt ||
1573 callType == QMetaType::Bool ||
1574 callType == QMetaType::Double ||
1575 callType == QMetaType::Float) {
1577 } else if (callType == QMetaType::QObjectStar) {
1578 *((QObject **)&data) = 0;
1580 } else if (callType == QMetaType::QString) {
1581 new (&data) QString();
1583 } else if (callType == qMetaTypeId<QVariant>()) {
1585 new (&data) QVariant();
1586 } else if (callType == qMetaTypeId<QList<QObject *> >()) {
1588 new (&data) QList<QObject *>();
1589 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1591 new (&data) v8::Handle<v8::Value>();
1594 new (&data) QVariant(callType, (void *)0);
1598 void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value)
1600 if (type != 0) { cleanup(); type = 0; }
1602 if (callType == qMetaTypeId<QScriptValue>()) {
1603 new (&data) QScriptValue();
1604 type = qMetaTypeId<QScriptValue>();
1605 } else if (callType == QMetaType::Int) {
1606 *((int *)&data) = int(value->Int32Value());
1608 } else if (callType == QMetaType::UInt) {
1609 *((uint *)&data) = uint(value->Uint32Value());
1611 } else if (callType == QMetaType::Bool) {
1612 *((bool *)&data) = value->BooleanValue();
1614 } else if (callType == QMetaType::Double) {
1615 *((double *)&data) = double(value->NumberValue());
1617 } else if (callType == QMetaType::Float) {
1618 *((float *)&data) = float(value->NumberValue());
1620 } else if (callType == QMetaType::QString) {
1621 if (value->IsNull() || value->IsUndefined())
1622 new (&data) QString();
1624 new (&data) QString(engine->toString(value->ToString()));
1626 } else if (callType == QMetaType::QObjectStar) {
1627 *((QObject **)&data) = engine->toQObject(value);
1629 } else if (callType == qMetaTypeId<QVariant>()) {
1630 new (&data) QVariant(engine->toVariant(value, -1));
1632 } else if (callType == qMetaTypeId<QList<QObject*> >()) {
1633 QList<QObject *> *list = new (&data) QList<QObject *>();
1634 if (value->IsArray()) {
1635 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
1636 uint32_t length = array->Length();
1637 for (uint32_t ii = 0; ii < length; ++ii)
1638 list->append(engine->toQObject(array->Get(ii)));
1640 list->append(engine->toQObject(value));
1643 } else if (callType == qMetaTypeId<QDeclarativeV8Handle>()) {
1644 new (&data) v8::Handle<v8::Value>(value);
1647 new (&data) QVariant();
1650 QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine());
1651 QVariant v = engine->toVariant(value, -1);
1653 if (v.userType() == callType) {
1654 *((QVariant *)&data) = v;
1655 } else if (v.canConvert((QVariant::Type)callType)) {
1656 *((QVariant *)&data) = v;
1657 ((QVariant *)&data)->convert((QVariant::Type)callType);
1658 } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) {
1659 QObject *obj = ep->toQObject(v);
1662 const QMetaObject *objMo = obj->metaObject();
1663 while (objMo && objMo != mo) objMo = objMo->superClass();
1664 if (!objMo) obj = 0;
1667 *((QVariant *)&data) = QVariant(callType, &obj);
1669 *((QVariant *)&data) = QVariant(callType, (void *)0);
1674 v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine)
1676 if (type == qMetaTypeId<QScriptValue>()) {
1677 return v8::Undefined();
1678 } else if (type == QMetaType::Int) {
1679 return v8::Integer::New(*((int *)&data));
1680 } else if (type == QMetaType::UInt) {
1681 return v8::Integer::NewFromUnsigned(*((uint *)&data));
1682 } else if (type == QMetaType::Bool) {
1683 return v8::Boolean::New(*((bool *)&data));
1684 } else if (type == QMetaType::Double) {
1685 return v8::Number::New(*((double *)&data));
1686 } else if (type == QMetaType::Float) {
1687 return v8::Number::New(*((float *)&data));
1688 } else if (type == QMetaType::QString) {
1689 return engine->toString(*((QString *)&data));
1690 } else if (type == QMetaType::QObjectStar) {
1691 QObject *object = *((QObject **)&data);
1693 QDeclarativeData::get(object, true)->setImplicitDestructible();
1694 return engine->newQObject(object);
1695 } else if (type == qMetaTypeId<QList<QObject *> >()) {
1696 // XXX aakenned Can this be more optimal? Just use Array as a prototype and
1697 // implement directly against QList<QObject*>?
1698 QList<QObject *> &list = *(QList<QObject *>*)&data;
1699 v8::Local<v8::Array> array = v8::Array::New(list.count());
1700 for (int ii = 0; ii < list.count(); ++ii)
1701 array->Set(ii, engine->newQObject(list.at(ii)));
1703 } else if (type == qMetaTypeId<QDeclarativeV8Handle>()) {
1704 return *(v8::Handle<v8::Value>*)&data;
1705 } else if (type == -1 || type == qMetaTypeId<QVariant>()) {
1706 QVariant value = *((QVariant *)&data);
1707 v8::Handle<v8::Value> rv = engine->fromVariant(value);
1708 if (QObject *object = engine->toQObject(rv))
1709 QDeclarativeData::get(object, true)->setImplicitDestructible();
1712 return v8::Undefined();