| 1 | /**************************************************************************** | 
|---|
| 2 | ** | 
|---|
| 3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | 
|---|
| 4 | ** All rights reserved. | 
|---|
| 5 | ** Contact: Nokia Corporation (qt-info@nokia.com) | 
|---|
| 6 | ** | 
|---|
| 7 | ** This file is part of the QtDeclarative module of the Qt Toolkit. | 
|---|
| 8 | ** | 
|---|
| 9 | ** $QT_BEGIN_LICENSE:LGPL$ | 
|---|
| 10 | ** Commercial Usage | 
|---|
| 11 | ** Licensees holding valid Qt Commercial licenses may use this file in | 
|---|
| 12 | ** accordance with the Qt Commercial License Agreement provided with the | 
|---|
| 13 | ** Software or, alternatively, in accordance with the terms contained in | 
|---|
| 14 | ** a written agreement between you and Nokia. | 
|---|
| 15 | ** | 
|---|
| 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. | 
|---|
| 23 | ** | 
|---|
| 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. | 
|---|
| 27 | ** | 
|---|
| 28 | ** GNU General Public License Usage | 
|---|
| 29 | ** Alternatively, this file may be used under the terms of the GNU | 
|---|
| 30 | ** General Public License version 3.0 as published by the Free Software | 
|---|
| 31 | ** Foundation and appearing in the file LICENSE.GPL included in the | 
|---|
| 32 | ** packaging of this file.  Please review the following information to | 
|---|
| 33 | ** ensure the GNU General Public License version 3.0 requirements will be | 
|---|
| 34 | ** met: http://www.gnu.org/copyleft/gpl.html. | 
|---|
| 35 | ** | 
|---|
| 36 | ** If you have questions regarding the use of this file, please contact | 
|---|
| 37 | ** Nokia at qt-info@nokia.com. | 
|---|
| 38 | ** $QT_END_LICENSE$ | 
|---|
| 39 | ** | 
|---|
| 40 | ****************************************************************************/ | 
|---|
| 41 |  | 
|---|
| 42 | #include "private/qdeclarativeobjectscriptclass_p.h" | 
|---|
| 43 |  | 
|---|
| 44 | #include "private/qdeclarativeengine_p.h" | 
|---|
| 45 | #include "private/qdeclarativecontext_p.h" | 
|---|
| 46 | #include "private/qdeclarativedata_p.h" | 
|---|
| 47 | #include "private/qdeclarativetypenamescriptclass_p.h" | 
|---|
| 48 | #include "private/qdeclarativelistscriptclass_p.h" | 
|---|
| 49 | #include "private/qdeclarativebinding_p.h" | 
|---|
| 50 | #include "private/qdeclarativeguard_p.h" | 
|---|
| 51 | #include "private/qdeclarativevmemetaobject_p.h" | 
|---|
| 52 |  | 
|---|
| 53 | #include <QtCore/qtimer.h> | 
|---|
| 54 | #include <QtCore/qvarlengtharray.h> | 
|---|
| 55 | #include <QtScript/qscriptcontextinfo.h> | 
|---|
| 56 |  | 
|---|
| 57 | Q_DECLARE_METATYPE(QScriptValue); | 
|---|
| 58 |  | 
|---|
| 59 | QT_BEGIN_NAMESPACE | 
|---|
| 60 |  | 
|---|
| 61 | struct ObjectData : public QScriptDeclarativeClass::Object { | 
|---|
| 62 | ObjectData(QObject *o, int t) : object(o), type(t) { | 
|---|
| 63 | if (o) { | 
|---|
| 64 | QDeclarativeData *ddata = QDeclarativeData::get(object, true); | 
|---|
| 65 | if (ddata) ddata->objectDataRefCount++; | 
|---|
| 66 | } | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | virtual ~ObjectData() { | 
|---|
| 70 | if (object && !object->parent()) { | 
|---|
| 71 | QDeclarativeData *ddata = QDeclarativeData::get(object, false); | 
|---|
| 72 | if (ddata && !ddata->indestructible && 0 == --ddata->objectDataRefCount) | 
|---|
| 73 | object->deleteLater(); | 
|---|
| 74 | } | 
|---|
| 75 | } | 
|---|
| 76 |  | 
|---|
| 77 | QDeclarativeGuard<QObject> object; | 
|---|
| 78 | int type; | 
|---|
| 79 | }; | 
|---|
| 80 |  | 
|---|
| 81 | /* | 
|---|
| 82 | The QDeclarativeObjectScriptClass handles property access for QObjects | 
|---|
| 83 | via QtScript. It is also used to provide a more useful API in | 
|---|
| 84 | QtScript for QML. | 
|---|
| 85 | */ | 
|---|
| 86 | QDeclarativeObjectScriptClass::QDeclarativeObjectScriptClass(QDeclarativeEngine *bindEngine) | 
|---|
| 87 | : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), | 
|---|
| 88 | methods(bindEngine), lastData(0), engine(bindEngine) | 
|---|
| 89 | { | 
|---|
| 90 | QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); | 
|---|
| 91 |  | 
|---|
| 92 | m_destroy = scriptEngine->newFunction(destroy); | 
|---|
| 93 | m_destroyId = createPersistentIdentifier(QLatin1String("destroy")); | 
|---|
| 94 | m_toString = scriptEngine->newFunction(tostring); | 
|---|
| 95 | m_toStringId = createPersistentIdentifier(QLatin1String("toString")); | 
|---|
| 96 | } | 
|---|
| 97 |  | 
|---|
| 98 | QDeclarativeObjectScriptClass::~QDeclarativeObjectScriptClass() | 
|---|
| 99 | { | 
|---|
| 100 | } | 
|---|
| 101 |  | 
|---|
| 102 | QScriptValue QDeclarativeObjectScriptClass::newQObject(QObject *object, int type) | 
|---|
| 103 | { | 
|---|
| 104 | QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); | 
|---|
| 105 |  | 
|---|
| 106 | if (!object) | 
|---|
| 107 | return scriptEngine->nullValue(); | 
|---|
| 108 | //        return newObject(scriptEngine, this, new ObjectData(object, type)); | 
|---|
| 109 |  | 
|---|
| 110 | if (QObjectPrivate::get(object)->wasDeleted) | 
|---|
| 111 | return scriptEngine->undefinedValue(); | 
|---|
| 112 |  | 
|---|
| 113 | QDeclarativeData *ddata = QDeclarativeData::get(object, true); | 
|---|
| 114 |  | 
|---|
| 115 | if (!ddata) { | 
|---|
| 116 | return scriptEngine->undefinedValue(); | 
|---|
| 117 | } else if (!ddata->indestructible && !object->parent()) { | 
|---|
| 118 | return newObject(scriptEngine, this, new ObjectData(object, type)); | 
|---|
| 119 | } else if (!ddata->scriptValue) { | 
|---|
| 120 | ddata->scriptValue = new QScriptValue(newObject(scriptEngine, this, new ObjectData(object, type))); | 
|---|
| 121 | return *ddata->scriptValue; | 
|---|
| 122 | } else if (ddata->scriptValue->engine() == QDeclarativeEnginePrivate::getScriptEngine(engine)) { | 
|---|
| 123 | return *ddata->scriptValue; | 
|---|
| 124 | } else { | 
|---|
| 125 | return newObject(scriptEngine, this, new ObjectData(object, type)); | 
|---|
| 126 | } | 
|---|
| 127 | } | 
|---|
| 128 |  | 
|---|
| 129 | QObject *QDeclarativeObjectScriptClass::toQObject(const QScriptValue &value) const | 
|---|
| 130 | { | 
|---|
| 131 | return value.toQObject(); | 
|---|
| 132 | } | 
|---|
| 133 |  | 
|---|
| 134 | int QDeclarativeObjectScriptClass::objectType(const QScriptValue &value) const | 
|---|
| 135 | { | 
|---|
| 136 | if (scriptClass(value) != this) | 
|---|
| 137 | return QVariant::Invalid; | 
|---|
| 138 |  | 
|---|
| 139 | Object *o = object(value); | 
|---|
| 140 | return ((ObjectData*)(o))->type; | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|
| 143 | QScriptClass::QueryFlags | 
|---|
| 144 | QDeclarativeObjectScriptClass::queryProperty(Object *object, const Identifier &name, | 
|---|
| 145 | QScriptClass::QueryFlags flags) | 
|---|
| 146 | { | 
|---|
| 147 | return queryProperty(toQObject(object), name, flags, 0); | 
|---|
| 148 | } | 
|---|
| 149 |  | 
|---|
| 150 | QScriptClass::QueryFlags | 
|---|
| 151 | QDeclarativeObjectScriptClass::queryProperty(QObject *obj, const Identifier &name, | 
|---|
| 152 | QScriptClass::QueryFlags flags, QDeclarativeContextData *evalContext, | 
|---|
| 153 | QueryHints hints) | 
|---|
| 154 | { | 
|---|
| 155 | Q_UNUSED(flags); | 
|---|
| 156 | lastData = 0; | 
|---|
| 157 | lastTNData = 0; | 
|---|
| 158 |  | 
|---|
| 159 | if (name == m_destroyId.identifier || | 
|---|
| 160 | name == m_toStringId.identifier) | 
|---|
| 161 | return QScriptClass::HandlesReadAccess; | 
|---|
| 162 |  | 
|---|
| 163 | if (!obj) | 
|---|
| 164 | return 0; | 
|---|
| 165 |  | 
|---|
| 166 | QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine); | 
|---|
| 167 | lastData = QDeclarativePropertyCache::property(engine, obj, name, local); | 
|---|
| 168 |  | 
|---|
| 169 | if (lastData) | 
|---|
| 170 | return QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess; | 
|---|
| 171 |  | 
|---|
| 172 | if (!(hints & SkipAttachedProperties)) { | 
|---|
| 173 | if (!evalContext && context()) { | 
|---|
| 174 | // Global object, QScriptContext activation object, QDeclarativeContext object | 
|---|
| 175 | QScriptValue scopeNode = scopeChainValue(context(), -3); | 
|---|
| 176 | if (scopeNode.isValid()) { | 
|---|
| 177 | Q_ASSERT(scriptClass(scopeNode) == enginePrivate->contextClass); | 
|---|
| 178 |  | 
|---|
| 179 | evalContext = enginePrivate->contextClass->contextFromValue(scopeNode); | 
|---|
| 180 | } | 
|---|
| 181 | } | 
|---|
| 182 |  | 
|---|
| 183 | if (evalContext && evalContext->imports) { | 
|---|
| 184 | QDeclarativeTypeNameCache::Data *data = evalContext->imports->data(name); | 
|---|
| 185 | if (data) { | 
|---|
| 186 | lastTNData = data; | 
|---|
| 187 | return QScriptClass::HandlesReadAccess; | 
|---|
| 188 | } | 
|---|
| 189 | } | 
|---|
| 190 | } | 
|---|
| 191 |  | 
|---|
| 192 | if (!(hints & ImplicitObject)) { | 
|---|
| 193 | local.coreIndex = -1; | 
|---|
| 194 | lastData = &local; | 
|---|
| 195 | return QScriptClass::HandlesWriteAccess; | 
|---|
| 196 | } | 
|---|
| 197 |  | 
|---|
| 198 | return 0; | 
|---|
| 199 | } | 
|---|
| 200 |  | 
|---|
| 201 | QDeclarativeObjectScriptClass::Value | 
|---|
| 202 | QDeclarativeObjectScriptClass::property(Object *object, const Identifier &name) | 
|---|
| 203 | { | 
|---|
| 204 | return property(toQObject(object), name); | 
|---|
| 205 | } | 
|---|
| 206 |  | 
|---|
| 207 | QDeclarativeObjectScriptClass::Value | 
|---|
| 208 | QDeclarativeObjectScriptClass::property(QObject *obj, const Identifier &name) | 
|---|
| 209 | { | 
|---|
| 210 | QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); | 
|---|
| 211 |  | 
|---|
| 212 | if (name == m_destroyId.identifier) | 
|---|
| 213 | return Value(scriptEngine, m_destroy); | 
|---|
| 214 | else if (name == m_toStringId.identifier) | 
|---|
| 215 | return Value(scriptEngine, m_toString); | 
|---|
| 216 |  | 
|---|
| 217 | if (lastData && !lastData->isValid()) | 
|---|
| 218 | return Value(); | 
|---|
| 219 |  | 
|---|
| 220 | Q_ASSERT(obj); | 
|---|
| 221 |  | 
|---|
| 222 | QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); | 
|---|
| 223 |  | 
|---|
| 224 | if (lastTNData) { | 
|---|
| 225 |  | 
|---|
| 226 | if (lastTNData->type) | 
|---|
| 227 | return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->type)); | 
|---|
| 228 | else | 
|---|
| 229 | return Value(scriptEngine, enginePriv->typeNameClass->newObject(obj, lastTNData->typeNamespace)); | 
|---|
| 230 |  | 
|---|
| 231 | } else if (lastData->flags & QDeclarativePropertyCache::Data::IsFunction) { | 
|---|
| 232 | if (lastData->flags & QDeclarativePropertyCache::Data::IsVMEFunction) { | 
|---|
| 233 | return Value(scriptEngine, ((QDeclarativeVMEMetaObject *)(obj->metaObject()))->vmeMethod(lastData->coreIndex)); | 
|---|
| 234 | } else { | 
|---|
| 235 | // Uncomment to use QtScript method call logic | 
|---|
| 236 | // QScriptValue sobj = scriptEngine->newQObject(obj); | 
|---|
| 237 | // return Value(scriptEngine, sobj.property(toString(name))); | 
|---|
| 238 | return Value(scriptEngine, methods.newMethod(obj, lastData)); | 
|---|
| 239 | } | 
|---|
| 240 | } else { | 
|---|
| 241 | if (enginePriv->captureProperties && !(lastData->flags & QDeclarativePropertyCache::Data::IsConstant)) { | 
|---|
| 242 | if (lastData->coreIndex == 0) { | 
|---|
| 243 | enginePriv->capturedProperties << | 
|---|
| 244 | QDeclarativeEnginePrivate::CapturedProperty(QDeclarativeData::get(obj, true)->objectNameNotifier()); | 
|---|
| 245 | } else { | 
|---|
| 246 | enginePriv->capturedProperties << | 
|---|
| 247 | QDeclarativeEnginePrivate::CapturedProperty(obj, lastData->coreIndex, lastData->notifyIndex); | 
|---|
| 248 | } | 
|---|
| 249 | } | 
|---|
| 250 |  | 
|---|
| 251 | if (QDeclarativeValueTypeFactory::isValueType((uint)lastData->propType)) { | 
|---|
| 252 | QDeclarativeValueType *valueType = enginePriv->valueTypes[lastData->propType]; | 
|---|
| 253 | if (valueType) | 
|---|
| 254 | return Value(scriptEngine, enginePriv->valueTypeClass->newObject(obj, lastData->coreIndex, valueType)); | 
|---|
| 255 | } | 
|---|
| 256 |  | 
|---|
| 257 | if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) { | 
|---|
| 258 | return Value(scriptEngine, enginePriv->listClass->newList(obj, lastData->coreIndex, lastData->propType)); | 
|---|
| 259 | } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { | 
|---|
| 260 | QObject *rv = 0; | 
|---|
| 261 | void *args[] = { &rv, 0 }; | 
|---|
| 262 | QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); | 
|---|
| 263 | return Value(scriptEngine, newQObject(rv, lastData->propType)); | 
|---|
| 264 | } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQScriptValue) { | 
|---|
| 265 | QScriptValue rv = scriptEngine->nullValue(); | 
|---|
| 266 | void *args[] = { &rv, 0 }; | 
|---|
| 267 | QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); | 
|---|
| 268 | return Value(scriptEngine, rv); | 
|---|
| 269 | } else if (lastData->propType == QMetaType::QReal) { | 
|---|
| 270 | qreal rv = 0; | 
|---|
| 271 | void *args[] = { &rv, 0 }; | 
|---|
| 272 | QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); | 
|---|
| 273 | return Value(scriptEngine, rv); | 
|---|
| 274 | } else if (lastData->propType == QMetaType::Int || lastData->flags & QDeclarativePropertyCache::Data::IsEnumType) { | 
|---|
| 275 | int rv = 0; | 
|---|
| 276 | void *args[] = { &rv, 0 }; | 
|---|
| 277 | QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); | 
|---|
| 278 | return Value(scriptEngine, rv); | 
|---|
| 279 | } else if (lastData->propType == QMetaType::Bool) { | 
|---|
| 280 | bool rv = false; | 
|---|
| 281 | void *args[] = { &rv, 0 }; | 
|---|
| 282 | QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); | 
|---|
| 283 | return Value(scriptEngine, rv); | 
|---|
| 284 | } else if (lastData->propType == QMetaType::QString) { | 
|---|
| 285 | QString rv; | 
|---|
| 286 | void *args[] = { &rv, 0 }; | 
|---|
| 287 | QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); | 
|---|
| 288 | return Value(scriptEngine, rv); | 
|---|
| 289 | } else if (lastData->propType == QMetaType::UInt) { | 
|---|
| 290 | uint rv = 0; | 
|---|
| 291 | void *args[] = { &rv, 0 }; | 
|---|
| 292 | QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); | 
|---|
| 293 | return Value(scriptEngine, rv); | 
|---|
| 294 | } else if (lastData->propType == QMetaType::Float) { | 
|---|
| 295 | float rv = 0; | 
|---|
| 296 | void *args[] = { &rv, 0 }; | 
|---|
| 297 | QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); | 
|---|
| 298 | return Value(scriptEngine, rv); | 
|---|
| 299 | } else if (lastData->propType == QMetaType::Double) { | 
|---|
| 300 | double rv = 0; | 
|---|
| 301 | void *args[] = { &rv, 0 }; | 
|---|
| 302 | QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); | 
|---|
| 303 | return Value(scriptEngine, rv); | 
|---|
| 304 | } else { | 
|---|
| 305 | QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj); | 
|---|
| 306 | return Value(scriptEngine, enginePriv->scriptValueFromVariant(var)); | 
|---|
| 307 | } | 
|---|
| 308 | } | 
|---|
| 309 | } | 
|---|
| 310 |  | 
|---|
| 311 | void QDeclarativeObjectScriptClass::setProperty(Object *object, | 
|---|
| 312 | const Identifier &name, | 
|---|
| 313 | const QScriptValue &value) | 
|---|
| 314 | { | 
|---|
| 315 | return setProperty(toQObject(object), name, value, context()); | 
|---|
| 316 | } | 
|---|
| 317 |  | 
|---|
| 318 | void QDeclarativeObjectScriptClass::setProperty(QObject *obj, | 
|---|
| 319 | const Identifier &name, | 
|---|
| 320 | const QScriptValue &value, | 
|---|
| 321 | QScriptContext *context, | 
|---|
| 322 | QDeclarativeContextData *evalContext) | 
|---|
| 323 | { | 
|---|
| 324 | Q_UNUSED(name); | 
|---|
| 325 |  | 
|---|
| 326 | Q_ASSERT(obj); | 
|---|
| 327 | Q_ASSERT(lastData); | 
|---|
| 328 | Q_ASSERT(context); | 
|---|
| 329 |  | 
|---|
| 330 | if (!lastData->isValid()) { | 
|---|
| 331 | QString error = QLatin1String("Cannot assign to non-existent property \"") + | 
|---|
| 332 | toString(name) + QLatin1Char('\"'); | 
|---|
| 333 | context->throwError(error); | 
|---|
| 334 | return; | 
|---|
| 335 | } | 
|---|
| 336 |  | 
|---|
| 337 | if (!(lastData->flags & QDeclarativePropertyCache::Data::IsWritable) && | 
|---|
| 338 | !(lastData->flags & QDeclarativePropertyCache::Data::IsQList)) { | 
|---|
| 339 | QString error = QLatin1String("Cannot assign to read-only property \"") + | 
|---|
| 340 | toString(name) + QLatin1Char('\"'); | 
|---|
| 341 | context->throwError(error); | 
|---|
| 342 | return; | 
|---|
| 343 | } | 
|---|
| 344 |  | 
|---|
| 345 | QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); | 
|---|
| 346 |  | 
|---|
| 347 | if (!evalContext) { | 
|---|
| 348 | // Global object, QScriptContext activation object, QDeclarativeContext object | 
|---|
| 349 | QScriptValue scopeNode = scopeChainValue(context, -3); | 
|---|
| 350 | if (scopeNode.isValid()) { | 
|---|
| 351 | Q_ASSERT(scriptClass(scopeNode) == enginePriv->contextClass); | 
|---|
| 352 |  | 
|---|
| 353 | evalContext = enginePriv->contextClass->contextFromValue(scopeNode); | 
|---|
| 354 | } | 
|---|
| 355 | } | 
|---|
| 356 |  | 
|---|
| 357 | QDeclarativeAbstractBinding *delBinding = | 
|---|
| 358 | QDeclarativePropertyPrivate::setBinding(obj, lastData->coreIndex, -1, 0); | 
|---|
| 359 | if (delBinding) | 
|---|
| 360 | delBinding->destroy(); | 
|---|
| 361 |  | 
|---|
| 362 | if (value.isNull() && lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { | 
|---|
| 363 | QObject *o = 0; | 
|---|
| 364 | int status = -1; | 
|---|
| 365 | int flags = 0; | 
|---|
| 366 | void *argv[] = { &o, 0, &status, &flags }; | 
|---|
| 367 | QMetaObject::metacall(obj, QMetaObject::WriteProperty, lastData->coreIndex, argv); | 
|---|
| 368 | } else if (value.isUndefined() && lastData->flags & QDeclarativePropertyCache::Data::IsResettable) { | 
|---|
| 369 | void *a[] = { 0 }; | 
|---|
| 370 | QMetaObject::metacall(obj, QMetaObject::ResetProperty, lastData->coreIndex, a); | 
|---|
| 371 | } else if (value.isUndefined() && lastData->propType == qMetaTypeId<QVariant>()) { | 
|---|
| 372 | QDeclarativePropertyPrivate::write(obj, *lastData, QVariant(), evalContext); | 
|---|
| 373 | } else if (value.isUndefined()) { | 
|---|
| 374 | QString error = QLatin1String("Cannot assign [undefined] to ") + | 
|---|
| 375 | QLatin1String(QMetaType::typeName(lastData->propType)); | 
|---|
| 376 | context->throwError(error); | 
|---|
| 377 | } else if (!value.isRegExp() && value.isFunction()) { | 
|---|
| 378 | QString error = QLatin1String("Cannot assign a function to a property."); | 
|---|
| 379 | context->throwError(error); | 
|---|
| 380 | } else { | 
|---|
| 381 | QVariant v; | 
|---|
| 382 | if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) | 
|---|
| 383 | v = enginePriv->scriptValueToVariant(value, qMetaTypeId<QList<QObject *> >()); | 
|---|
| 384 | else | 
|---|
| 385 | v = enginePriv->scriptValueToVariant(value, lastData->propType); | 
|---|
| 386 |  | 
|---|
| 387 | if (!QDeclarativePropertyPrivate::write(obj, *lastData, v, evalContext)) { | 
|---|
| 388 | const char *valueType = 0; | 
|---|
| 389 | if (v.userType() == QVariant::Invalid) valueType = "null"; | 
|---|
| 390 | else valueType = QMetaType::typeName(v.userType()); | 
|---|
| 391 |  | 
|---|
| 392 | QString error = QLatin1String("Cannot assign ") + | 
|---|
| 393 | QLatin1String(valueType) + | 
|---|
| 394 | QLatin1String(" to ") + | 
|---|
| 395 | QLatin1String(QMetaType::typeName(lastData->propType)); | 
|---|
| 396 | context->throwError(error); | 
|---|
| 397 | } | 
|---|
| 398 | } | 
|---|
| 399 | } | 
|---|
| 400 |  | 
|---|
| 401 | bool QDeclarativeObjectScriptClass::isQObject() const | 
|---|
| 402 | { | 
|---|
| 403 | return true; | 
|---|
| 404 | } | 
|---|
| 405 |  | 
|---|
| 406 | QObject *QDeclarativeObjectScriptClass::toQObject(Object *object, bool *ok) | 
|---|
| 407 | { | 
|---|
| 408 | if (ok) *ok = true; | 
|---|
| 409 |  | 
|---|
| 410 | ObjectData *data = (ObjectData*)object; | 
|---|
| 411 | return data->object.data(); | 
|---|
| 412 | } | 
|---|
| 413 |  | 
|---|
| 414 | QScriptValue QDeclarativeObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *) | 
|---|
| 415 | { | 
|---|
| 416 | QObject* obj = context->thisObject().toQObject(); | 
|---|
| 417 |  | 
|---|
| 418 | QString ret; | 
|---|
| 419 | if(obj){ | 
|---|
| 420 | QString objectName = obj->objectName(); | 
|---|
| 421 |  | 
|---|
| 422 | ret += QString::fromUtf8(obj->metaObject()->className()); | 
|---|
| 423 | ret += QLatin1String("(0x"); | 
|---|
| 424 | ret += QString::number((quintptr)obj,16); | 
|---|
| 425 |  | 
|---|
| 426 | if (!objectName.isEmpty()) { | 
|---|
| 427 | ret += QLatin1String(", \""); | 
|---|
| 428 | ret += objectName; | 
|---|
| 429 | ret += QLatin1Char('\"'); | 
|---|
| 430 | } | 
|---|
| 431 |  | 
|---|
| 432 | ret += QLatin1Char(')'); | 
|---|
| 433 | }else{ | 
|---|
| 434 | ret += QLatin1String("null"); | 
|---|
| 435 | } | 
|---|
| 436 | return QScriptValue(ret); | 
|---|
| 437 | } | 
|---|
| 438 |  | 
|---|
| 439 | QScriptValue QDeclarativeObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine) | 
|---|
| 440 | { | 
|---|
| 441 | QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); | 
|---|
| 442 | QScriptValue that = context->thisObject(); | 
|---|
| 443 |  | 
|---|
| 444 | if (scriptClass(that) != p->objectClass) | 
|---|
| 445 | return engine->undefinedValue(); | 
|---|
| 446 |  | 
|---|
| 447 | ObjectData *data = (ObjectData *)p->objectClass->object(that); | 
|---|
| 448 | if (!data->object) | 
|---|
| 449 | return engine->undefinedValue(); | 
|---|
| 450 |  | 
|---|
| 451 | QDeclarativeData *ddata = QDeclarativeData::get(data->object, false); | 
|---|
| 452 | if (!ddata || ddata->indestructible) | 
|---|
| 453 | return engine->currentContext()->throwError(QLatin1String("Invalid attempt to destroy() an indestructible object")); | 
|---|
| 454 |  | 
|---|
| 455 | QObject *obj = data->object; | 
|---|
| 456 | int delay = 0; | 
|---|
| 457 | if (context->argumentCount() > 0) | 
|---|
| 458 | delay = context->argument(0).toInt32(); | 
|---|
| 459 | if (delay > 0) | 
|---|
| 460 | QTimer::singleShot(delay, obj, SLOT(deleteLater())); | 
|---|
| 461 | else | 
|---|
| 462 | obj->deleteLater(); | 
|---|
| 463 |  | 
|---|
| 464 | return engine->undefinedValue(); | 
|---|
| 465 | } | 
|---|
| 466 |  | 
|---|
| 467 | QStringList QDeclarativeObjectScriptClass::propertyNames(Object *object) | 
|---|
| 468 | { | 
|---|
| 469 | QObject *obj = toQObject(object); | 
|---|
| 470 | if (!obj) | 
|---|
| 471 | return QStringList(); | 
|---|
| 472 |  | 
|---|
| 473 | QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine); | 
|---|
| 474 |  | 
|---|
| 475 | QDeclarativePropertyCache *cache = 0; | 
|---|
| 476 | QDeclarativeData *ddata = QDeclarativeData::get(obj); | 
|---|
| 477 | if (ddata) | 
|---|
| 478 | cache = ddata->propertyCache; | 
|---|
| 479 | if (!cache) { | 
|---|
| 480 | cache = enginePrivate->cache(obj); | 
|---|
| 481 | if (cache) { | 
|---|
| 482 | if (ddata) { cache->addref(); ddata->propertyCache = cache; } | 
|---|
| 483 | } else { | 
|---|
| 484 | // Not cachable - fall back to QMetaObject (eg. dynamic meta object) | 
|---|
| 485 | // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal. | 
|---|
| 486 | // XXX This is a workaround for QTBUG-9420. | 
|---|
| 487 | const QMetaObject *mo = obj->metaObject(); | 
|---|
| 488 | QStringList r; | 
|---|
| 489 | int pc = mo->propertyCount(); | 
|---|
| 490 | int po = mo->propertyOffset(); | 
|---|
| 491 | for (int i=po; i<pc; ++i) | 
|---|
| 492 | r += QString::fromUtf8(mo->property(i).name()); | 
|---|
| 493 | return r; | 
|---|
| 494 | } | 
|---|
| 495 | } | 
|---|
| 496 | return cache->propertyNames(); | 
|---|
| 497 | } | 
|---|
| 498 |  | 
|---|
| 499 | bool QDeclarativeObjectScriptClass::compare(Object *o1, Object *o2) | 
|---|
| 500 | { | 
|---|
| 501 | ObjectData *d1 = (ObjectData *)o1; | 
|---|
| 502 | ObjectData *d2 = (ObjectData *)o2; | 
|---|
| 503 |  | 
|---|
| 504 | return d1 == d2 || d1->object == d2->object; | 
|---|
| 505 | } | 
|---|
| 506 |  | 
|---|
| 507 | struct MethodData : public QScriptDeclarativeClass::Object { | 
|---|
| 508 | MethodData(QObject *o, const QDeclarativePropertyCache::Data &d) : object(o), data(d) {} | 
|---|
| 509 |  | 
|---|
| 510 | QDeclarativeGuard<QObject> object; | 
|---|
| 511 | QDeclarativePropertyCache::Data data; | 
|---|
| 512 | }; | 
|---|
| 513 |  | 
|---|
| 514 | QDeclarativeObjectMethodScriptClass::QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *bindEngine) | 
|---|
| 515 | : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), | 
|---|
| 516 | engine(bindEngine) | 
|---|
| 517 | { | 
|---|
| 518 | qRegisterMetaType<QList<QObject *> >("QList<QObject *>"); | 
|---|
| 519 |  | 
|---|
| 520 | setSupportsCall(true); | 
|---|
| 521 |  | 
|---|
| 522 | QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); | 
|---|
| 523 |  | 
|---|
| 524 | m_connect = scriptEngine->newFunction(connect); | 
|---|
| 525 | m_connectId = createPersistentIdentifier(QLatin1String("connect")); | 
|---|
| 526 | m_disconnect = scriptEngine->newFunction(disconnect); | 
|---|
| 527 | m_disconnectId = createPersistentIdentifier(QLatin1String("disconnect")); | 
|---|
| 528 | } | 
|---|
| 529 |  | 
|---|
| 530 | QDeclarativeObjectMethodScriptClass::~QDeclarativeObjectMethodScriptClass() | 
|---|
| 531 | { | 
|---|
| 532 | } | 
|---|
| 533 |  | 
|---|
| 534 | QScriptValue QDeclarativeObjectMethodScriptClass::newMethod(QObject *object, const QDeclarativePropertyCache::Data *method) | 
|---|
| 535 | { | 
|---|
| 536 | QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); | 
|---|
| 537 |  | 
|---|
| 538 | return newObject(scriptEngine, this, new MethodData(object, *method)); | 
|---|
| 539 | } | 
|---|
| 540 |  | 
|---|
| 541 | QScriptValue QDeclarativeObjectMethodScriptClass::connect(QScriptContext *context, QScriptEngine *engine) | 
|---|
| 542 | { | 
|---|
| 543 | QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); | 
|---|
| 544 |  | 
|---|
| 545 | QScriptValue that = context->thisObject(); | 
|---|
| 546 | if (&p->objectClass->methods != scriptClass(that)) | 
|---|
| 547 | return engine->undefinedValue(); | 
|---|
| 548 |  | 
|---|
| 549 | MethodData *data = (MethodData *)object(that); | 
|---|
| 550 |  | 
|---|
| 551 | if (!data->object || context->argumentCount() == 0) | 
|---|
| 552 | return engine->undefinedValue(); | 
|---|
| 553 |  | 
|---|
| 554 | QByteArray signal("2"); | 
|---|
| 555 | signal.append(data->object->metaObject()->method(data->data.coreIndex).signature()); | 
|---|
| 556 |  | 
|---|
| 557 | if (context->argumentCount() == 1) { | 
|---|
| 558 | qScriptConnect(data->object, signal.constData(), QScriptValue(), context->argument(0)); | 
|---|
| 559 | } else { | 
|---|
| 560 | qScriptConnect(data->object, signal.constData(), context->argument(0), context->argument(1)); | 
|---|
| 561 | } | 
|---|
| 562 |  | 
|---|
| 563 | return engine->undefinedValue(); | 
|---|
| 564 | } | 
|---|
| 565 |  | 
|---|
| 566 | QScriptValue QDeclarativeObjectMethodScriptClass::disconnect(QScriptContext *context, QScriptEngine *engine) | 
|---|
| 567 | { | 
|---|
| 568 | QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); | 
|---|
| 569 |  | 
|---|
| 570 | QScriptValue that = context->thisObject(); | 
|---|
| 571 | if (&p->objectClass->methods != scriptClass(that)) | 
|---|
| 572 | return engine->undefinedValue(); | 
|---|
| 573 |  | 
|---|
| 574 | MethodData *data = (MethodData *)object(that); | 
|---|
| 575 |  | 
|---|
| 576 | if (!data->object || context->argumentCount() == 0) | 
|---|
| 577 | return engine->undefinedValue(); | 
|---|
| 578 |  | 
|---|
| 579 | QByteArray signal("2"); | 
|---|
| 580 | signal.append(data->object->metaObject()->method(data->data.coreIndex).signature()); | 
|---|
| 581 |  | 
|---|
| 582 | if (context->argumentCount() == 1) { | 
|---|
| 583 | qScriptDisconnect(data->object, signal.constData(), QScriptValue(), context->argument(0)); | 
|---|
| 584 | } else { | 
|---|
| 585 | qScriptDisconnect(data->object, signal.constData(), context->argument(0), context->argument(1)); | 
|---|
| 586 | } | 
|---|
| 587 |  | 
|---|
| 588 | return engine->undefinedValue(); | 
|---|
| 589 | } | 
|---|
| 590 |  | 
|---|
| 591 | QScriptClass::QueryFlags | 
|---|
| 592 | QDeclarativeObjectMethodScriptClass::queryProperty(Object *, const Identifier &name, | 
|---|
| 593 | QScriptClass::QueryFlags flags) | 
|---|
| 594 | { | 
|---|
| 595 | Q_UNUSED(flags); | 
|---|
| 596 | if (name == m_connectId.identifier || name == m_disconnectId.identifier) | 
|---|
| 597 | return QScriptClass::HandlesReadAccess; | 
|---|
| 598 | else | 
|---|
| 599 | return 0; | 
|---|
| 600 |  | 
|---|
| 601 | } | 
|---|
| 602 |  | 
|---|
| 603 | QDeclarativeObjectMethodScriptClass::Value | 
|---|
| 604 | QDeclarativeObjectMethodScriptClass::property(Object *, const Identifier &name) | 
|---|
| 605 | { | 
|---|
| 606 | QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); | 
|---|
| 607 |  | 
|---|
| 608 | if (name == m_connectId.identifier) | 
|---|
| 609 | return Value(scriptEngine, m_connect); | 
|---|
| 610 | else if (name == m_disconnectId.identifier) | 
|---|
| 611 | return Value(scriptEngine, m_disconnect); | 
|---|
| 612 | else | 
|---|
| 613 | return Value(); | 
|---|
| 614 | } | 
|---|
| 615 |  | 
|---|
| 616 | namespace { | 
|---|
| 617 | struct MetaCallArgument { | 
|---|
| 618 | inline MetaCallArgument(); | 
|---|
| 619 | inline ~MetaCallArgument(); | 
|---|
| 620 | inline void *dataPtr(); | 
|---|
| 621 |  | 
|---|
| 622 | inline void initAsType(int type, QDeclarativeEngine *); | 
|---|
| 623 | void fromScriptValue(int type, QDeclarativeEngine *, const QScriptValue &); | 
|---|
| 624 | inline QScriptDeclarativeClass::Value toValue(QDeclarativeEngine *); | 
|---|
| 625 |  | 
|---|
| 626 | private: | 
|---|
| 627 | MetaCallArgument(const MetaCallArgument &); | 
|---|
| 628 |  | 
|---|
| 629 | inline void cleanup(); | 
|---|
| 630 |  | 
|---|
| 631 | char data[4 * sizeof(void *)]; | 
|---|
| 632 | int type; | 
|---|
| 633 | bool isObjectType; | 
|---|
| 634 | }; | 
|---|
| 635 | } | 
|---|
| 636 |  | 
|---|
| 637 | MetaCallArgument::MetaCallArgument() | 
|---|
| 638 | : type(QVariant::Invalid), isObjectType(false) | 
|---|
| 639 | { | 
|---|
| 640 | } | 
|---|
| 641 |  | 
|---|
| 642 | MetaCallArgument::~MetaCallArgument() | 
|---|
| 643 | { | 
|---|
| 644 | cleanup(); | 
|---|
| 645 | } | 
|---|
| 646 |  | 
|---|
| 647 | void MetaCallArgument::cleanup() | 
|---|
| 648 | { | 
|---|
| 649 | if (type == QMetaType::QString) { | 
|---|
| 650 | ((QString *)&data)->~QString(); | 
|---|
| 651 | } else if (type == -1 || type == qMetaTypeId<QVariant>()) { | 
|---|
| 652 | ((QVariant *)&data)->~QVariant(); | 
|---|
| 653 | } else if (type == qMetaTypeId<QScriptValue>()) { | 
|---|
| 654 | ((QScriptValue *)&data)->~QScriptValue(); | 
|---|
| 655 | } else if (type == qMetaTypeId<QList<QObject *> >()) { | 
|---|
| 656 | ((QList<QObject *> *)&data)->~QList<QObject *>(); | 
|---|
| 657 | } | 
|---|
| 658 | } | 
|---|
| 659 |  | 
|---|
| 660 | void *MetaCallArgument::dataPtr() | 
|---|
| 661 | { | 
|---|
| 662 | if (type == -1) | 
|---|
| 663 | return ((QVariant *)data)->data(); | 
|---|
| 664 | else | 
|---|
| 665 | return (void *)&data; | 
|---|
| 666 | } | 
|---|
| 667 |  | 
|---|
| 668 | void MetaCallArgument::initAsType(int callType, QDeclarativeEngine *e) | 
|---|
| 669 | { | 
|---|
| 670 | if (type != 0) { cleanup(); type = 0; } | 
|---|
| 671 | if (callType == 0) return; | 
|---|
| 672 |  | 
|---|
| 673 | QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e); | 
|---|
| 674 |  | 
|---|
| 675 | if (callType == qMetaTypeId<QScriptValue>()) { | 
|---|
| 676 | new (&data) QScriptValue(engine->undefinedValue()); | 
|---|
| 677 | type = callType; | 
|---|
| 678 | } else if (callType == QMetaType::Int || | 
|---|
| 679 | callType == QMetaType::UInt || | 
|---|
| 680 | callType == QMetaType::Bool || | 
|---|
| 681 | callType == QMetaType::Double || | 
|---|
| 682 | callType == QMetaType::Float) { | 
|---|
| 683 | type = callType; | 
|---|
| 684 | } else if (callType == QMetaType::QObjectStar) { | 
|---|
| 685 | *((QObject **)&data) = 0; | 
|---|
| 686 | type = callType; | 
|---|
| 687 | } else if (callType == QMetaType::QString) { | 
|---|
| 688 | new (&data) QString(); | 
|---|
| 689 | type = callType; | 
|---|
| 690 | } else if (callType == qMetaTypeId<QVariant>()) { | 
|---|
| 691 | type = callType; | 
|---|
| 692 | new (&data) QVariant(); | 
|---|
| 693 | } else if (callType == qMetaTypeId<QList<QObject *> >()) { | 
|---|
| 694 | type = callType; | 
|---|
| 695 | new (&data) QList<QObject *>(); | 
|---|
| 696 | } else { | 
|---|
| 697 | type = -1; | 
|---|
| 698 | new (&data) QVariant(callType, (void *)0); | 
|---|
| 699 | } | 
|---|
| 700 | } | 
|---|
| 701 |  | 
|---|
| 702 | void MetaCallArgument::fromScriptValue(int callType, QDeclarativeEngine *engine, const QScriptValue &value) | 
|---|
| 703 | { | 
|---|
| 704 | if (type != 0) { cleanup(); type = 0; } | 
|---|
| 705 |  | 
|---|
| 706 | if (callType == qMetaTypeId<QScriptValue>()) { | 
|---|
| 707 | new (&data) QScriptValue(value); | 
|---|
| 708 | type = qMetaTypeId<QScriptValue>(); | 
|---|
| 709 | } else if (callType == QMetaType::Int) { | 
|---|
| 710 | *((int *)&data) = int(value.toInt32()); | 
|---|
| 711 | type = callType; | 
|---|
| 712 | } else if (callType == QMetaType::UInt) { | 
|---|
| 713 | *((uint *)&data) = uint(value.toUInt32()); | 
|---|
| 714 | type = callType; | 
|---|
| 715 | } else if (callType == QMetaType::Bool) { | 
|---|
| 716 | *((bool *)&data) = value.toBool(); | 
|---|
| 717 | type = callType; | 
|---|
| 718 | } else if (callType == QMetaType::Double) { | 
|---|
| 719 | *((double *)&data) = double(value.toNumber()); | 
|---|
| 720 | type = callType; | 
|---|
| 721 | } else if (callType == QMetaType::Float) { | 
|---|
| 722 | *((float *)&data) = float(value.toNumber()); | 
|---|
| 723 | type = callType; | 
|---|
| 724 | } else if (callType == QMetaType::QString) { | 
|---|
| 725 | if (value.isNull() || value.isUndefined()) | 
|---|
| 726 | new (&data) QString(); | 
|---|
| 727 | else | 
|---|
| 728 | new (&data) QString(value.toString()); | 
|---|
| 729 | type = callType; | 
|---|
| 730 | } else if (callType == QMetaType::QObjectStar) { | 
|---|
| 731 | *((QObject **)&data) = value.toQObject(); | 
|---|
| 732 | type = callType; | 
|---|
| 733 | } else if (callType == qMetaTypeId<QVariant>()) { | 
|---|
| 734 | new (&data) QVariant(QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value)); | 
|---|
| 735 | type = callType; | 
|---|
| 736 | } else if (callType == qMetaTypeId<QList<QObject*> >()) { | 
|---|
| 737 | QList<QObject *> *list = new (&data) QList<QObject *>(); | 
|---|
| 738 | if (value.isArray()) { | 
|---|
| 739 | int length = value.property(QLatin1String("length")).toInt32(); | 
|---|
| 740 | for (int ii = 0; ii < length; ++ii) { | 
|---|
| 741 | QScriptValue arrayItem = value.property(ii); | 
|---|
| 742 | QObject *d = arrayItem.toQObject(); | 
|---|
| 743 | list->append(d); | 
|---|
| 744 | } | 
|---|
| 745 | } else if (QObject *d = value.toQObject()) { | 
|---|
| 746 | list->append(d); | 
|---|
| 747 | } | 
|---|
| 748 | type = callType; | 
|---|
| 749 | } else { | 
|---|
| 750 | new (&data) QVariant(); | 
|---|
| 751 | type = -1; | 
|---|
| 752 |  | 
|---|
| 753 | QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(engine); | 
|---|
| 754 | QVariant v = priv->scriptValueToVariant(value); | 
|---|
| 755 | if (v.userType() == callType) { | 
|---|
| 756 | *((QVariant *)&data) = v; | 
|---|
| 757 | } else if (v.canConvert((QVariant::Type)callType)) { | 
|---|
| 758 | *((QVariant *)&data) = v; | 
|---|
| 759 | ((QVariant *)&data)->convert((QVariant::Type)callType); | 
|---|
| 760 | } else if (const QMetaObject *mo = priv->rawMetaObjectForType(callType)) { | 
|---|
| 761 | QObject *obj = priv->toQObject(v); | 
|---|
| 762 |  | 
|---|
| 763 | if (obj) { | 
|---|
| 764 | const QMetaObject *objMo = obj->metaObject(); | 
|---|
| 765 | while (objMo && objMo != mo) objMo = objMo->superClass(); | 
|---|
| 766 | if (!objMo) obj = 0; | 
|---|
| 767 | } | 
|---|
| 768 |  | 
|---|
| 769 | *((QVariant *)&data) = QVariant(callType, &obj); | 
|---|
| 770 | } else { | 
|---|
| 771 | *((QVariant *)&data) = QVariant(callType, (void *)0); | 
|---|
| 772 | } | 
|---|
| 773 | } | 
|---|
| 774 | } | 
|---|
| 775 |  | 
|---|
| 776 | QScriptDeclarativeClass::Value MetaCallArgument::toValue(QDeclarativeEngine *e) | 
|---|
| 777 | { | 
|---|
| 778 | QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e); | 
|---|
| 779 |  | 
|---|
| 780 | if (type == qMetaTypeId<QScriptValue>()) { | 
|---|
| 781 | return QScriptDeclarativeClass::Value(engine, *((QScriptValue *)&data)); | 
|---|
| 782 | } else if (type == QMetaType::Int) { | 
|---|
| 783 | return QScriptDeclarativeClass::Value(engine, *((int *)&data)); | 
|---|
| 784 | } else if (type == QMetaType::UInt) { | 
|---|
| 785 | return QScriptDeclarativeClass::Value(engine, *((uint *)&data)); | 
|---|
| 786 | } else if (type == QMetaType::Bool) { | 
|---|
| 787 | return QScriptDeclarativeClass::Value(engine, *((bool *)&data)); | 
|---|
| 788 | } else if (type == QMetaType::Double) { | 
|---|
| 789 | return QScriptDeclarativeClass::Value(engine, *((double *)&data)); | 
|---|
| 790 | } else if (type == QMetaType::Float) { | 
|---|
| 791 | return QScriptDeclarativeClass::Value(engine, *((float *)&data)); | 
|---|
| 792 | } else if (type == QMetaType::QString) { | 
|---|
| 793 | return QScriptDeclarativeClass::Value(engine, *((QString *)&data)); | 
|---|
| 794 | } else if (type == QMetaType::QObjectStar) { | 
|---|
| 795 | QObject *object = *((QObject **)&data); | 
|---|
| 796 | if (object) | 
|---|
| 797 | QDeclarativeData::get(object, true)->setImplicitDestructible(); | 
|---|
| 798 | QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e); | 
|---|
| 799 | return QScriptDeclarativeClass::Value(engine, priv->objectClass->newQObject(object)); | 
|---|
| 800 | } else if (type == qMetaTypeId<QList<QObject *> >()) { | 
|---|
| 801 | QList<QObject *> &list = *(QList<QObject *>*)&data; | 
|---|
| 802 | QScriptValue rv = engine->newArray(list.count()); | 
|---|
| 803 | QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e); | 
|---|
| 804 | for (int ii = 0; ii < list.count(); ++ii) { | 
|---|
| 805 | QObject *object = list.at(ii); | 
|---|
| 806 | QDeclarativeData::get(object, true)->setImplicitDestructible(); | 
|---|
| 807 | rv.setProperty(ii, priv->objectClass->newQObject(object)); | 
|---|
| 808 | } | 
|---|
| 809 | return QScriptDeclarativeClass::Value(engine, rv); | 
|---|
| 810 | } else if (type == -1 || type == qMetaTypeId<QVariant>()) { | 
|---|
| 811 | QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(e); | 
|---|
| 812 | QScriptValue rv = ep->scriptValueFromVariant(*((QVariant *)&data)); | 
|---|
| 813 | if (rv.isQObject()) { | 
|---|
| 814 | QObject *object = rv.toQObject(); | 
|---|
| 815 | if (object) | 
|---|
| 816 | QDeclarativeData::get(object, true)->setImplicitDestructible(); | 
|---|
| 817 | } | 
|---|
| 818 | return QScriptDeclarativeClass::Value(engine, rv); | 
|---|
| 819 | } else { | 
|---|
| 820 | return QScriptDeclarativeClass::Value(); | 
|---|
| 821 | } | 
|---|
| 822 | } | 
|---|
| 823 |  | 
|---|
| 824 | int QDeclarativeObjectMethodScriptClass::enumType(const QMetaObject *meta, const QString &strname) | 
|---|
| 825 | { | 
|---|
| 826 | QByteArray str = strname.toUtf8(); | 
|---|
| 827 | QByteArray scope; | 
|---|
| 828 | QByteArray name; | 
|---|
| 829 | int scopeIdx = str.lastIndexOf("::"); | 
|---|
| 830 | if (scopeIdx != -1) { | 
|---|
| 831 | scope = str.left(scopeIdx); | 
|---|
| 832 | name = str.mid(scopeIdx + 2); | 
|---|
| 833 | } else { | 
|---|
| 834 | name = str; | 
|---|
| 835 | } | 
|---|
| 836 | for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { | 
|---|
| 837 | QMetaEnum m = meta->enumerator(i); | 
|---|
| 838 | if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) | 
|---|
| 839 | return QVariant::Int; | 
|---|
| 840 | } | 
|---|
| 841 | return QVariant::Invalid; | 
|---|
| 842 | } | 
|---|
| 843 |  | 
|---|
| 844 | QDeclarativeObjectMethodScriptClass::Value QDeclarativeObjectMethodScriptClass::call(Object *o, QScriptContext *ctxt) | 
|---|
| 845 | { | 
|---|
| 846 | MethodData *method = static_cast<MethodData *>(o); | 
|---|
| 847 |  | 
|---|
| 848 | if (method->data.relatedIndex == -1) | 
|---|
| 849 | return callPrecise(method->object, method->data, ctxt); | 
|---|
| 850 | else | 
|---|
| 851 | return callOverloaded(method, ctxt); | 
|---|
| 852 | } | 
|---|
| 853 |  | 
|---|
| 854 | QDeclarativeObjectMethodScriptClass::Value | 
|---|
| 855 | QDeclarativeObjectMethodScriptClass::callPrecise(QObject *object, const QDeclarativePropertyCache::Data &data, | 
|---|
| 856 | QScriptContext *ctxt) | 
|---|
| 857 | { | 
|---|
| 858 | if (data.flags & QDeclarativePropertyCache::Data::HasArguments) { | 
|---|
| 859 |  | 
|---|
| 860 | QMetaMethod m = object->metaObject()->method(data.coreIndex); | 
|---|
| 861 | QList<QByteArray> argTypeNames = m.parameterTypes(); | 
|---|
| 862 | QVarLengthArray<int, 9> argTypes(argTypeNames.count()); | 
|---|
| 863 |  | 
|---|
| 864 | // ### Cache | 
|---|
| 865 | for (int ii = 0; ii < argTypeNames.count(); ++ii) { | 
|---|
| 866 | argTypes[ii] = QMetaType::type(argTypeNames.at(ii)); | 
|---|
| 867 | if (argTypes[ii] == QVariant::Invalid) | 
|---|
| 868 | argTypes[ii] = enumType(object->metaObject(), QString::fromLatin1(argTypeNames.at(ii))); | 
|---|
| 869 | if (argTypes[ii] == QVariant::Invalid) | 
|---|
| 870 | return Value(ctxt, ctxt->throwError(QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii))))); | 
|---|
| 871 | } | 
|---|
| 872 |  | 
|---|
| 873 | if (argTypes.count() > ctxt->argumentCount()) | 
|---|
| 874 | return Value(ctxt, ctxt->throwError(QLatin1String("Insufficient arguments"))); | 
|---|
| 875 |  | 
|---|
| 876 | return callMethod(object, data.coreIndex, data.propType, argTypes.count(), argTypes.data(), ctxt); | 
|---|
| 877 |  | 
|---|
| 878 | } else { | 
|---|
| 879 |  | 
|---|
| 880 | return callMethod(object, data.coreIndex, data.propType, 0, 0, ctxt); | 
|---|
| 881 |  | 
|---|
| 882 | } | 
|---|
| 883 | } | 
|---|
| 884 |  | 
|---|
| 885 | QDeclarativeObjectMethodScriptClass::Value | 
|---|
| 886 | QDeclarativeObjectMethodScriptClass::callMethod(QObject *object, int index, | 
|---|
| 887 | int returnType, int argCount, int *argTypes, | 
|---|
| 888 | QScriptContext *ctxt) | 
|---|
| 889 | { | 
|---|
| 890 | if (argCount > 0) { | 
|---|
| 891 |  | 
|---|
| 892 | QVarLengthArray<MetaCallArgument, 9> args(argCount + 1); | 
|---|
| 893 | args[0].initAsType(returnType, engine); | 
|---|
| 894 |  | 
|---|
| 895 | for (int ii = 0; ii < argCount; ++ii) | 
|---|
| 896 | args[ii + 1].fromScriptValue(argTypes[ii], engine, ctxt->argument(ii)); | 
|---|
| 897 |  | 
|---|
| 898 | QVarLengthArray<void *, 9> argData(args.count()); | 
|---|
| 899 | for (int ii = 0; ii < args.count(); ++ii) | 
|---|
| 900 | argData[ii] = args[ii].dataPtr(); | 
|---|
| 901 |  | 
|---|
| 902 | QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data()); | 
|---|
| 903 |  | 
|---|
| 904 | return args[0].toValue(engine); | 
|---|
| 905 |  | 
|---|
| 906 | } else if (returnType != 0) { | 
|---|
| 907 |  | 
|---|
| 908 | MetaCallArgument arg; | 
|---|
| 909 | arg.initAsType(returnType, engine); | 
|---|
| 910 |  | 
|---|
| 911 | void *args[] = { arg.dataPtr() }; | 
|---|
| 912 |  | 
|---|
| 913 | QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); | 
|---|
| 914 |  | 
|---|
| 915 | return arg.toValue(engine); | 
|---|
| 916 |  | 
|---|
| 917 | } else { | 
|---|
| 918 |  | 
|---|
| 919 | void *args[] = { 0 }; | 
|---|
| 920 | QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); | 
|---|
| 921 | return Value(); | 
|---|
| 922 |  | 
|---|
| 923 | } | 
|---|
| 924 | } | 
|---|
| 925 |  | 
|---|
| 926 | /*! | 
|---|
| 927 | Resolve the overloaded method to call.  The algorithm works conceptually like this: | 
|---|
| 928 | 1.  Resolve the set of overloads it is *possible* to call. | 
|---|
| 929 | Impossible overloads include those that have too many parameters or have parameters | 
|---|
| 930 | of unknown type. | 
|---|
| 931 | 2.  Filter the set of overloads to only contain those with the closest number of | 
|---|
| 932 | parameters. | 
|---|
| 933 | For example, if we are called with 3 parameters and there are 2 overloads that | 
|---|
| 934 | take 2 parameters and one that takes 3, eliminate the 2 parameter overloads. | 
|---|
| 935 | 3.  Find the best remaining overload based on its match score. | 
|---|
| 936 | If two or more overloads have the same match score, call the last one.  The match | 
|---|
| 937 | score is constructed by adding the matchScore() result for each of the parameters. | 
|---|
| 938 | */ | 
|---|
| 939 | QDeclarativeObjectMethodScriptClass::Value | 
|---|
| 940 | QDeclarativeObjectMethodScriptClass::callOverloaded(MethodData *method, QScriptContext *ctxt) | 
|---|
| 941 | { | 
|---|
| 942 | int argumentCount = ctxt->argumentCount(); | 
|---|
| 943 |  | 
|---|
| 944 | QDeclarativePropertyCache::Data *best = 0; | 
|---|
| 945 | int bestParameterScore = INT_MAX; | 
|---|
| 946 | int bestMatchScore = INT_MAX; | 
|---|
| 947 |  | 
|---|
| 948 | QDeclarativePropertyCache::Data dummy; | 
|---|
| 949 | QDeclarativePropertyCache::Data *attempt = &method->data; | 
|---|
| 950 |  | 
|---|
| 951 | do { | 
|---|
| 952 | QList<QByteArray> methodArgTypeNames; | 
|---|
| 953 |  | 
|---|
| 954 | if (attempt->flags & QDeclarativePropertyCache::Data::HasArguments) | 
|---|
| 955 | methodArgTypeNames = method->object->metaObject()->method(attempt->coreIndex).parameterTypes(); | 
|---|
| 956 |  | 
|---|
| 957 | int methodArgumentCount = methodArgTypeNames.count(); | 
|---|
| 958 |  | 
|---|
| 959 | if (methodArgumentCount > argumentCount) | 
|---|
| 960 | continue; // We don't have sufficient arguments to call this method | 
|---|
| 961 |  | 
|---|
| 962 | int methodParameterScore = argumentCount - methodArgumentCount; | 
|---|
| 963 | if (methodParameterScore > bestParameterScore) | 
|---|
| 964 | continue; // We already have a better option | 
|---|
| 965 |  | 
|---|
| 966 | int methodMatchScore = 0; | 
|---|
| 967 | QVarLengthArray<int, 9> methodArgTypes(methodArgumentCount); | 
|---|
| 968 |  | 
|---|
| 969 | bool unknownArgument = false; | 
|---|
| 970 | for (int ii = 0; ii < methodArgumentCount; ++ii) { | 
|---|
| 971 | methodArgTypes[ii] = QMetaType::type(methodArgTypeNames.at(ii)); | 
|---|
| 972 | if (methodArgTypes[ii] == QVariant::Invalid) | 
|---|
| 973 | methodArgTypes[ii] = enumType(method->object->metaObject(), | 
|---|
| 974 | QString::fromLatin1(methodArgTypeNames.at(ii))); | 
|---|
| 975 | if (methodArgTypes[ii] == QVariant::Invalid) { | 
|---|
| 976 | unknownArgument = true; | 
|---|
| 977 | break; | 
|---|
| 978 | } | 
|---|
| 979 | methodMatchScore += matchScore(ctxt->argument(ii), methodArgTypes[ii], methodArgTypeNames.at(ii)); | 
|---|
| 980 | } | 
|---|
| 981 | if (unknownArgument) | 
|---|
| 982 | continue; // We don't understand all the parameters | 
|---|
| 983 |  | 
|---|
| 984 | if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { | 
|---|
| 985 | best = attempt; | 
|---|
| 986 | bestParameterScore = methodParameterScore; | 
|---|
| 987 | bestMatchScore = methodMatchScore; | 
|---|
| 988 | } | 
|---|
| 989 |  | 
|---|
| 990 | if (bestParameterScore == 0 && bestMatchScore == 0) | 
|---|
| 991 | break; // We can't get better than that | 
|---|
| 992 |  | 
|---|
| 993 | } while((attempt = relatedMethod(method->object, attempt, dummy)) != 0); | 
|---|
| 994 |  | 
|---|
| 995 | if (best) { | 
|---|
| 996 | return callPrecise(method->object, *best, ctxt); | 
|---|
| 997 | } else { | 
|---|
| 998 | QString error = QLatin1String("Unable to determine callable overload.  Candidates are:"); | 
|---|
| 999 | QDeclarativePropertyCache::Data *candidate = &method->data; | 
|---|
| 1000 | while (candidate) { | 
|---|
| 1001 | error += QLatin1String("\n    ") + QString::fromUtf8(method->object->metaObject()->method(candidate->coreIndex).signature()); | 
|---|
| 1002 | candidate = relatedMethod(method->object, candidate, dummy); | 
|---|
| 1003 | } | 
|---|
| 1004 | return Value(ctxt, ctxt->throwError(error)); | 
|---|
| 1005 | } | 
|---|
| 1006 | } | 
|---|
| 1007 |  | 
|---|
| 1008 | /*! | 
|---|
| 1009 | Returns the match score for converting \a actual to be of type \a conversionType.  A | 
|---|
| 1010 | zero score means "perfect match" whereas a higher score is worse. | 
|---|
| 1011 |  | 
|---|
| 1012 | The conversion table is copied out of the QtScript callQtMethod() function. | 
|---|
| 1013 | */ | 
|---|
| 1014 | int QDeclarativeObjectMethodScriptClass::matchScore(const QScriptValue &actual, int conversionType, | 
|---|
| 1015 | const QByteArray &conversionTypeName) | 
|---|
| 1016 | { | 
|---|
| 1017 | if (actual.isNumber()) { | 
|---|
| 1018 | switch (conversionType) { | 
|---|
| 1019 | case QMetaType::Double: | 
|---|
| 1020 | return 0; | 
|---|
| 1021 | case QMetaType::Float: | 
|---|
| 1022 | return 1; | 
|---|
| 1023 | case QMetaType::LongLong: | 
|---|
| 1024 | case QMetaType::ULongLong: | 
|---|
| 1025 | return 2; | 
|---|
| 1026 | case QMetaType::Long: | 
|---|
| 1027 | case QMetaType::ULong: | 
|---|
| 1028 | return 3; | 
|---|
| 1029 | case QMetaType::Int: | 
|---|
| 1030 | case QMetaType::UInt: | 
|---|
| 1031 | return 4; | 
|---|
| 1032 | case QMetaType::Short: | 
|---|
| 1033 | case QMetaType::UShort: | 
|---|
| 1034 | return 5; | 
|---|
| 1035 | break; | 
|---|
| 1036 | case QMetaType::Char: | 
|---|
| 1037 | case QMetaType::UChar: | 
|---|
| 1038 | return 6; | 
|---|
| 1039 | default: | 
|---|
| 1040 | return 10; | 
|---|
| 1041 | } | 
|---|
| 1042 | } else if (actual.isString()) { | 
|---|
| 1043 | switch (conversionType) { | 
|---|
| 1044 | case QMetaType::QString: | 
|---|
| 1045 | return 0; | 
|---|
| 1046 | default: | 
|---|
| 1047 | return 10; | 
|---|
| 1048 | } | 
|---|
| 1049 | } else if (actual.isBoolean()) { | 
|---|
| 1050 | switch (conversionType) { | 
|---|
| 1051 | case QMetaType::Bool: | 
|---|
| 1052 | return 0; | 
|---|
| 1053 | default: | 
|---|
| 1054 | return 10; | 
|---|
| 1055 | } | 
|---|
| 1056 | } else if (actual.isDate()) { | 
|---|
| 1057 | switch (conversionType) { | 
|---|
| 1058 | case QMetaType::QDateTime: | 
|---|
| 1059 | return 0; | 
|---|
| 1060 | case QMetaType::QDate: | 
|---|
| 1061 | return 1; | 
|---|
| 1062 | case QMetaType::QTime: | 
|---|
| 1063 | return 2; | 
|---|
| 1064 | default: | 
|---|
| 1065 | return 10; | 
|---|
| 1066 | } | 
|---|
| 1067 | } else if (actual.isRegExp()) { | 
|---|
| 1068 | switch (conversionType) { | 
|---|
| 1069 | case QMetaType::QRegExp: | 
|---|
| 1070 | return 0; | 
|---|
| 1071 | default: | 
|---|
| 1072 | return 10; | 
|---|
| 1073 | } | 
|---|
| 1074 | } else if (actual.isVariant()) { | 
|---|
| 1075 | if (conversionType == qMetaTypeId<QVariant>()) | 
|---|
| 1076 | return 0; | 
|---|
| 1077 | else if (actual.toVariant().userType() == conversionType) | 
|---|
| 1078 | return 0; | 
|---|
| 1079 | else | 
|---|
| 1080 | return 10; | 
|---|
| 1081 | } else if (actual.isArray()) { | 
|---|
| 1082 | switch (conversionType) { | 
|---|
| 1083 | case QMetaType::QStringList: | 
|---|
| 1084 | case QMetaType::QVariantList: | 
|---|
| 1085 | return 5; | 
|---|
| 1086 | default: | 
|---|
| 1087 | return 10; | 
|---|
| 1088 | } | 
|---|
| 1089 | } else if (actual.isQObject()) { | 
|---|
| 1090 | switch (conversionType) { | 
|---|
| 1091 | case QMetaType::QObjectStar: | 
|---|
| 1092 | return 0; | 
|---|
| 1093 | default: | 
|---|
| 1094 | return 10; | 
|---|
| 1095 | } | 
|---|
| 1096 | } else if (actual.isNull()) { | 
|---|
| 1097 | switch (conversionType) { | 
|---|
| 1098 | case QMetaType::VoidStar: | 
|---|
| 1099 | case QMetaType::QObjectStar: | 
|---|
| 1100 | return 0; | 
|---|
| 1101 | default: | 
|---|
| 1102 | if (!conversionTypeName.endsWith('*')) | 
|---|
| 1103 | return 10; | 
|---|
| 1104 | else | 
|---|
| 1105 | return 0; | 
|---|
| 1106 | } | 
|---|
| 1107 | } else { | 
|---|
| 1108 | return 10; | 
|---|
| 1109 | } | 
|---|
| 1110 | } | 
|---|
| 1111 |  | 
|---|
| 1112 | static inline int QMetaObject_methods(const QMetaObject *metaObject) | 
|---|
| 1113 | { | 
|---|
| 1114 | struct Private | 
|---|
| 1115 | { | 
|---|
| 1116 | int revision; | 
|---|
| 1117 | int className; | 
|---|
| 1118 | int classInfoCount, classInfoData; | 
|---|
| 1119 | int methodCount, methodData; | 
|---|
| 1120 | }; | 
|---|
| 1121 |  | 
|---|
| 1122 | return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount; | 
|---|
| 1123 | } | 
|---|
| 1124 |  | 
|---|
| 1125 | static QByteArray QMetaMethod_name(const QMetaMethod &m) | 
|---|
| 1126 | { | 
|---|
| 1127 | QByteArray sig = m.signature(); | 
|---|
| 1128 | int paren = sig.indexOf('('); | 
|---|
| 1129 | if (paren == -1) | 
|---|
| 1130 | return sig; | 
|---|
| 1131 | else | 
|---|
| 1132 | return sig.left(paren); | 
|---|
| 1133 | } | 
|---|
| 1134 |  | 
|---|
| 1135 | /*! | 
|---|
| 1136 | Returns the next related method, if one, or 0. | 
|---|
| 1137 | */ | 
|---|
| 1138 | QDeclarativePropertyCache::Data * | 
|---|
| 1139 | QDeclarativeObjectMethodScriptClass::relatedMethod(QObject *object, QDeclarativePropertyCache::Data *current, | 
|---|
| 1140 | QDeclarativePropertyCache::Data &dummy) | 
|---|
| 1141 | { | 
|---|
| 1142 | QDeclarativePropertyCache *cache = QDeclarativeData::get(object)->propertyCache; | 
|---|
| 1143 | if (current->relatedIndex == -1) | 
|---|
| 1144 | return 0; | 
|---|
| 1145 |  | 
|---|
| 1146 | if (cache) { | 
|---|
| 1147 | return cache->method(current->relatedIndex); | 
|---|
| 1148 | } else { | 
|---|
| 1149 | const QMetaObject *mo = object->metaObject(); | 
|---|
| 1150 | int methodOffset = mo->methodCount() - QMetaObject_methods(mo); | 
|---|
| 1151 |  | 
|---|
| 1152 | while (methodOffset > current->relatedIndex) { | 
|---|
| 1153 | mo = mo->superClass(); | 
|---|
| 1154 | methodOffset -= QMetaObject_methods(mo); | 
|---|
| 1155 | } | 
|---|
| 1156 |  | 
|---|
| 1157 | QMetaMethod method = mo->method(current->relatedIndex); | 
|---|
| 1158 | dummy.load(method); | 
|---|
| 1159 |  | 
|---|
| 1160 | // Look for overloaded methods | 
|---|
| 1161 | QByteArray methodName = QMetaMethod_name(method); | 
|---|
| 1162 | for (int ii = current->relatedIndex - 1; ii >= methodOffset; --ii) { | 
|---|
| 1163 | if (methodName == QMetaMethod_name(mo->method(ii))) { | 
|---|
| 1164 | dummy.relatedIndex = ii; | 
|---|
| 1165 | return &dummy; | 
|---|
| 1166 | } | 
|---|
| 1167 | } | 
|---|
| 1168 |  | 
|---|
| 1169 | return &dummy; | 
|---|
| 1170 | } | 
|---|
| 1171 | } | 
|---|
| 1172 |  | 
|---|
| 1173 | QT_END_NAMESPACE | 
|---|
| 1174 |  | 
|---|