|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 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 ** 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 |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
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::HandlesReadAccess | 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 enginePriv->capturedProperties << |
|
243 QDeclarativeEnginePrivate::CapturedProperty(obj, lastData->coreIndex, lastData->notifyIndex); |
|
244 } |
|
245 |
|
246 if (QDeclarativeValueTypeFactory::isValueType((uint)lastData->propType)) { |
|
247 QDeclarativeValueType *valueType = enginePriv->valueTypes[lastData->propType]; |
|
248 if (valueType) |
|
249 return Value(scriptEngine, enginePriv->valueTypeClass->newObject(obj, lastData->coreIndex, valueType)); |
|
250 } |
|
251 |
|
252 if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) { |
|
253 return Value(scriptEngine, enginePriv->listClass->newList(obj, lastData->coreIndex, lastData->propType)); |
|
254 } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { |
|
255 QObject *rv = 0; |
|
256 void *args[] = { &rv, 0 }; |
|
257 QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); |
|
258 return Value(scriptEngine, newQObject(rv, lastData->propType)); |
|
259 } else if (lastData->flags & QDeclarativePropertyCache::Data::IsQScriptValue) { |
|
260 QScriptValue rv = scriptEngine->nullValue(); |
|
261 void *args[] = { &rv, 0 }; |
|
262 QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); |
|
263 return Value(scriptEngine, rv); |
|
264 } else if (lastData->propType == QMetaType::QReal) { |
|
265 qreal rv = 0; |
|
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::Int || lastData->flags & QDeclarativePropertyCache::Data::IsEnumType) { |
|
270 int 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::Bool) { |
|
275 bool rv = false; |
|
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::QString) { |
|
280 QString rv; |
|
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::UInt) { |
|
285 uint rv = 0; |
|
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::Float) { |
|
290 float 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::Double) { |
|
295 double rv = 0; |
|
296 void *args[] = { &rv, 0 }; |
|
297 QMetaObject::metacall(obj, QMetaObject::ReadProperty, lastData->coreIndex, args); |
|
298 return Value(scriptEngine, rv); |
|
299 } else { |
|
300 QVariant var = obj->metaObject()->property(lastData->coreIndex).read(obj); |
|
301 return Value(scriptEngine, enginePriv->scriptValueFromVariant(var)); |
|
302 } |
|
303 } |
|
304 } |
|
305 |
|
306 void QDeclarativeObjectScriptClass::setProperty(Object *object, |
|
307 const Identifier &name, |
|
308 const QScriptValue &value) |
|
309 { |
|
310 return setProperty(toQObject(object), name, value, context()); |
|
311 } |
|
312 |
|
313 void QDeclarativeObjectScriptClass::setProperty(QObject *obj, |
|
314 const Identifier &name, |
|
315 const QScriptValue &value, |
|
316 QScriptContext *context, |
|
317 QDeclarativeContextData *evalContext) |
|
318 { |
|
319 Q_UNUSED(name); |
|
320 |
|
321 Q_ASSERT(obj); |
|
322 Q_ASSERT(lastData); |
|
323 Q_ASSERT(context); |
|
324 |
|
325 if (!lastData->isValid()) { |
|
326 QString error = QLatin1String("Cannot assign to non-existent property \"") + |
|
327 toString(name) + QLatin1Char('\"'); |
|
328 context->throwError(error); |
|
329 return; |
|
330 } |
|
331 |
|
332 if (!(lastData->flags & QDeclarativePropertyCache::Data::IsWritable) && |
|
333 !(lastData->flags & QDeclarativePropertyCache::Data::IsQList)) { |
|
334 QString error = QLatin1String("Cannot assign to read-only property \"") + |
|
335 toString(name) + QLatin1Char('\"'); |
|
336 context->throwError(error); |
|
337 return; |
|
338 } |
|
339 |
|
340 QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(engine); |
|
341 |
|
342 if (!evalContext) { |
|
343 // Global object, QScriptContext activation object, QDeclarativeContext object |
|
344 QScriptValue scopeNode = scopeChainValue(context, -3); |
|
345 if (scopeNode.isValid()) { |
|
346 Q_ASSERT(scriptClass(scopeNode) == enginePriv->contextClass); |
|
347 |
|
348 evalContext = enginePriv->contextClass->contextFromValue(scopeNode); |
|
349 } |
|
350 } |
|
351 |
|
352 QDeclarativeAbstractBinding *delBinding = |
|
353 QDeclarativePropertyPrivate::setBinding(obj, lastData->coreIndex, -1, 0); |
|
354 if (delBinding) |
|
355 delBinding->destroy(); |
|
356 |
|
357 if (value.isNull() && lastData->flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { |
|
358 QObject *o = 0; |
|
359 int status = -1; |
|
360 int flags = 0; |
|
361 void *argv[] = { &o, 0, &status, &flags }; |
|
362 QMetaObject::metacall(obj, QMetaObject::WriteProperty, lastData->coreIndex, argv); |
|
363 } else if (value.isUndefined() && lastData->flags & QDeclarativePropertyCache::Data::IsResettable) { |
|
364 void *a[] = { 0 }; |
|
365 QMetaObject::metacall(obj, QMetaObject::ResetProperty, lastData->coreIndex, a); |
|
366 } else if (value.isUndefined() && lastData->propType == qMetaTypeId<QVariant>()) { |
|
367 QDeclarativePropertyPrivate::write(obj, *lastData, QVariant(), evalContext); |
|
368 } else if (value.isUndefined()) { |
|
369 QString error = QLatin1String("Cannot assign [undefined] to ") + |
|
370 QLatin1String(QMetaType::typeName(lastData->propType)); |
|
371 context->throwError(error); |
|
372 } else if (!value.isRegExp() && value.isFunction()) { |
|
373 QString error = QLatin1String("Cannot assign a function to a property."); |
|
374 context->throwError(error); |
|
375 } else { |
|
376 QVariant v; |
|
377 if (lastData->flags & QDeclarativePropertyCache::Data::IsQList) |
|
378 v = enginePriv->scriptValueToVariant(value, qMetaTypeId<QList<QObject *> >()); |
|
379 else |
|
380 v = enginePriv->scriptValueToVariant(value, lastData->propType); |
|
381 |
|
382 if (!QDeclarativePropertyPrivate::write(obj, *lastData, v, evalContext)) { |
|
383 const char *valueType = 0; |
|
384 if (v.userType() == QVariant::Invalid) valueType = "null"; |
|
385 else valueType = QMetaType::typeName(v.userType()); |
|
386 |
|
387 QString error = QLatin1String("Cannot assign ") + |
|
388 QLatin1String(valueType) + |
|
389 QLatin1String(" to ") + |
|
390 QLatin1String(QMetaType::typeName(lastData->propType)); |
|
391 context->throwError(error); |
|
392 } |
|
393 } |
|
394 } |
|
395 |
|
396 bool QDeclarativeObjectScriptClass::isQObject() const |
|
397 { |
|
398 return true; |
|
399 } |
|
400 |
|
401 QObject *QDeclarativeObjectScriptClass::toQObject(Object *object, bool *ok) |
|
402 { |
|
403 if (ok) *ok = true; |
|
404 |
|
405 ObjectData *data = (ObjectData*)object; |
|
406 return data->object.data(); |
|
407 } |
|
408 |
|
409 QScriptValue QDeclarativeObjectScriptClass::tostring(QScriptContext *context, QScriptEngine *) |
|
410 { |
|
411 QObject* obj = context->thisObject().toQObject(); |
|
412 |
|
413 QString ret; |
|
414 if(obj){ |
|
415 QString objectName = obj->objectName(); |
|
416 |
|
417 ret += QString::fromUtf8(obj->metaObject()->className()); |
|
418 ret += QLatin1String("(0x"); |
|
419 ret += QString::number((intptr_t)obj,16); |
|
420 |
|
421 if (!objectName.isEmpty()) { |
|
422 ret += QLatin1String(", \""); |
|
423 ret += objectName; |
|
424 ret += QLatin1Char('\"'); |
|
425 } |
|
426 |
|
427 ret += QLatin1Char(')'); |
|
428 }else{ |
|
429 ret += QLatin1String("null"); |
|
430 } |
|
431 return QScriptValue(ret); |
|
432 } |
|
433 |
|
434 QScriptValue QDeclarativeObjectScriptClass::destroy(QScriptContext *context, QScriptEngine *engine) |
|
435 { |
|
436 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); |
|
437 QScriptValue that = context->thisObject(); |
|
438 |
|
439 if (scriptClass(that) != p->objectClass) |
|
440 return engine->undefinedValue(); |
|
441 |
|
442 ObjectData *data = (ObjectData *)p->objectClass->object(that); |
|
443 if (!data->object) |
|
444 return engine->undefinedValue(); |
|
445 |
|
446 QDeclarativeData *ddata = QDeclarativeData::get(data->object, false); |
|
447 if (!ddata || ddata->indestructible) |
|
448 return engine->currentContext()->throwError(QLatin1String("Invalid attempt to destroy() an indestructible object")); |
|
449 |
|
450 QObject *obj = data->object; |
|
451 int delay = 0; |
|
452 if (context->argumentCount() > 0) |
|
453 delay = context->argument(0).toInt32(); |
|
454 if (delay > 0) |
|
455 QTimer::singleShot(delay, obj, SLOT(deleteLater())); |
|
456 else |
|
457 obj->deleteLater(); |
|
458 |
|
459 return engine->undefinedValue(); |
|
460 } |
|
461 |
|
462 QStringList QDeclarativeObjectScriptClass::propertyNames(Object *object) |
|
463 { |
|
464 QObject *obj = toQObject(object); |
|
465 if (!obj) |
|
466 return QStringList(); |
|
467 |
|
468 QDeclarativeEnginePrivate *enginePrivate = QDeclarativeEnginePrivate::get(engine); |
|
469 |
|
470 QDeclarativePropertyCache *cache = 0; |
|
471 QDeclarativeData *ddata = QDeclarativeData::get(obj); |
|
472 if (ddata) |
|
473 cache = ddata->propertyCache; |
|
474 if (!cache) { |
|
475 cache = enginePrivate->cache(obj); |
|
476 if (cache) { |
|
477 if (ddata) { cache->addref(); ddata->propertyCache = cache; } |
|
478 } else { |
|
479 // Not cachable - fall back to QMetaObject (eg. dynamic meta object) |
|
480 // XXX QDeclarativeOpenMetaObject has a cache, so this is suboptimal. |
|
481 // XXX This is a workaround for QTBUG-9420. |
|
482 const QMetaObject *mo = obj->metaObject(); |
|
483 QStringList r; |
|
484 int pc = mo->propertyCount(); |
|
485 int po = mo->propertyOffset(); |
|
486 for (int i=po; i<pc; ++i) |
|
487 r += QString::fromUtf8(mo->property(i).name()); |
|
488 return r; |
|
489 } |
|
490 } |
|
491 return cache->propertyNames(); |
|
492 } |
|
493 |
|
494 bool QDeclarativeObjectScriptClass::compare(Object *o1, Object *o2) |
|
495 { |
|
496 ObjectData *d1 = (ObjectData *)o1; |
|
497 ObjectData *d2 = (ObjectData *)o2; |
|
498 |
|
499 return d1 == d2 || d1->object == d2->object; |
|
500 } |
|
501 |
|
502 struct MethodData : public QScriptDeclarativeClass::Object { |
|
503 MethodData(QObject *o, const QDeclarativePropertyCache::Data &d) : object(o), data(d) {} |
|
504 |
|
505 QDeclarativeGuard<QObject> object; |
|
506 QDeclarativePropertyCache::Data data; |
|
507 }; |
|
508 |
|
509 QDeclarativeObjectMethodScriptClass::QDeclarativeObjectMethodScriptClass(QDeclarativeEngine *bindEngine) |
|
510 : QScriptDeclarativeClass(QDeclarativeEnginePrivate::getScriptEngine(bindEngine)), |
|
511 engine(bindEngine) |
|
512 { |
|
513 qRegisterMetaType<QList<QObject *> >("QList<QObject *>"); |
|
514 |
|
515 setSupportsCall(true); |
|
516 |
|
517 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); |
|
518 |
|
519 m_connect = scriptEngine->newFunction(connect); |
|
520 m_connectId = createPersistentIdentifier(QLatin1String("connect")); |
|
521 m_disconnect = scriptEngine->newFunction(disconnect); |
|
522 m_disconnectId = createPersistentIdentifier(QLatin1String("disconnect")); |
|
523 } |
|
524 |
|
525 QDeclarativeObjectMethodScriptClass::~QDeclarativeObjectMethodScriptClass() |
|
526 { |
|
527 } |
|
528 |
|
529 QScriptValue QDeclarativeObjectMethodScriptClass::newMethod(QObject *object, const QDeclarativePropertyCache::Data *method) |
|
530 { |
|
531 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); |
|
532 |
|
533 return newObject(scriptEngine, this, new MethodData(object, *method)); |
|
534 } |
|
535 |
|
536 QScriptValue QDeclarativeObjectMethodScriptClass::connect(QScriptContext *context, QScriptEngine *engine) |
|
537 { |
|
538 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); |
|
539 |
|
540 QScriptValue that = context->thisObject(); |
|
541 if (&p->objectClass->methods != scriptClass(that)) |
|
542 return engine->undefinedValue(); |
|
543 |
|
544 MethodData *data = (MethodData *)object(that); |
|
545 |
|
546 if (!data->object || context->argumentCount() == 0) |
|
547 return engine->undefinedValue(); |
|
548 |
|
549 QByteArray signal("2"); |
|
550 signal.append(data->object->metaObject()->method(data->data.coreIndex).signature()); |
|
551 |
|
552 if (context->argumentCount() == 1) { |
|
553 qScriptConnect(data->object, signal.constData(), QScriptValue(), context->argument(0)); |
|
554 } else { |
|
555 qScriptConnect(data->object, signal.constData(), context->argument(0), context->argument(1)); |
|
556 } |
|
557 |
|
558 return engine->undefinedValue(); |
|
559 } |
|
560 |
|
561 QScriptValue QDeclarativeObjectMethodScriptClass::disconnect(QScriptContext *context, QScriptEngine *engine) |
|
562 { |
|
563 QDeclarativeEnginePrivate *p = QDeclarativeEnginePrivate::get(engine); |
|
564 |
|
565 QScriptValue that = context->thisObject(); |
|
566 if (&p->objectClass->methods != scriptClass(that)) |
|
567 return engine->undefinedValue(); |
|
568 |
|
569 MethodData *data = (MethodData *)object(that); |
|
570 |
|
571 if (!data->object || context->argumentCount() == 0) |
|
572 return engine->undefinedValue(); |
|
573 |
|
574 QByteArray signal("2"); |
|
575 signal.append(data->object->metaObject()->method(data->data.coreIndex).signature()); |
|
576 |
|
577 if (context->argumentCount() == 1) { |
|
578 qScriptDisconnect(data->object, signal.constData(), QScriptValue(), context->argument(0)); |
|
579 } else { |
|
580 qScriptDisconnect(data->object, signal.constData(), context->argument(0), context->argument(1)); |
|
581 } |
|
582 |
|
583 return engine->undefinedValue(); |
|
584 } |
|
585 |
|
586 QScriptClass::QueryFlags |
|
587 QDeclarativeObjectMethodScriptClass::queryProperty(Object *, const Identifier &name, |
|
588 QScriptClass::QueryFlags flags) |
|
589 { |
|
590 Q_UNUSED(flags); |
|
591 if (name == m_connectId.identifier || name == m_disconnectId.identifier) |
|
592 return QScriptClass::HandlesReadAccess; |
|
593 else |
|
594 return 0; |
|
595 |
|
596 } |
|
597 |
|
598 QDeclarativeObjectMethodScriptClass::Value |
|
599 QDeclarativeObjectMethodScriptClass::property(Object *, const Identifier &name) |
|
600 { |
|
601 QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine); |
|
602 |
|
603 if (name == m_connectId.identifier) |
|
604 return Value(scriptEngine, m_connect); |
|
605 else if (name == m_disconnectId.identifier) |
|
606 return Value(scriptEngine, m_disconnect); |
|
607 else |
|
608 return Value(); |
|
609 } |
|
610 |
|
611 namespace { |
|
612 struct MetaCallArgument { |
|
613 inline MetaCallArgument(); |
|
614 inline ~MetaCallArgument(); |
|
615 inline void *dataPtr(); |
|
616 |
|
617 inline void initAsType(int type, QDeclarativeEngine *); |
|
618 void fromScriptValue(int type, QDeclarativeEngine *, const QScriptValue &); |
|
619 inline QScriptDeclarativeClass::Value toValue(QDeclarativeEngine *); |
|
620 |
|
621 private: |
|
622 MetaCallArgument(const MetaCallArgument &); |
|
623 |
|
624 inline void cleanup(); |
|
625 |
|
626 char data[4 * sizeof(void *)]; |
|
627 int type; |
|
628 }; |
|
629 } |
|
630 |
|
631 MetaCallArgument::MetaCallArgument() |
|
632 : type(QVariant::Invalid) |
|
633 { |
|
634 } |
|
635 |
|
636 MetaCallArgument::~MetaCallArgument() |
|
637 { |
|
638 cleanup(); |
|
639 } |
|
640 |
|
641 void MetaCallArgument::cleanup() |
|
642 { |
|
643 if (type == QMetaType::QString) { |
|
644 ((QString *)&data)->~QString(); |
|
645 } else if (type == -1 || type == qMetaTypeId<QVariant>()) { |
|
646 ((QVariant *)&data)->~QVariant(); |
|
647 } else if (type == qMetaTypeId<QScriptValue>()) { |
|
648 ((QScriptValue *)&data)->~QScriptValue(); |
|
649 } else if (type == qMetaTypeId<QList<QObject *> >()) { |
|
650 ((QList<QObject *> *)&data)->~QList<QObject *>(); |
|
651 } |
|
652 } |
|
653 |
|
654 void *MetaCallArgument::dataPtr() |
|
655 { |
|
656 if (type == -1) |
|
657 return ((QVariant *)data)->data(); |
|
658 else |
|
659 return (void *)&data; |
|
660 } |
|
661 |
|
662 void MetaCallArgument::initAsType(int callType, QDeclarativeEngine *e) |
|
663 { |
|
664 if (type != 0) { cleanup(); type = 0; } |
|
665 if (callType == 0) return; |
|
666 |
|
667 QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e); |
|
668 |
|
669 if (callType == qMetaTypeId<QScriptValue>()) { |
|
670 new (&data) QScriptValue(engine->undefinedValue()); |
|
671 type = callType; |
|
672 } else if (callType == QMetaType::Int || |
|
673 callType == QMetaType::UInt || |
|
674 callType == QMetaType::Bool || |
|
675 callType == QMetaType::Double || |
|
676 callType == QMetaType::Float) { |
|
677 type = callType; |
|
678 } else if (callType == QMetaType::QObjectStar) { |
|
679 *((QObject **)&data) = 0; |
|
680 type = callType; |
|
681 } else if (callType == QMetaType::QString) { |
|
682 new (&data) QString(); |
|
683 type = callType; |
|
684 } else if (callType == qMetaTypeId<QVariant>()) { |
|
685 type = callType; |
|
686 new (&data) QVariant(); |
|
687 } else if (callType == qMetaTypeId<QList<QObject *> >()) { |
|
688 type = callType; |
|
689 new (&data) QList<QObject *>(); |
|
690 } else { |
|
691 type = -1; |
|
692 new (&data) QVariant(callType, (void *)0); |
|
693 } |
|
694 } |
|
695 |
|
696 void MetaCallArgument::fromScriptValue(int callType, QDeclarativeEngine *engine, const QScriptValue &value) |
|
697 { |
|
698 if (type != 0) { cleanup(); type = 0; } |
|
699 |
|
700 if (callType == qMetaTypeId<QScriptValue>()) { |
|
701 new (&data) QScriptValue(value); |
|
702 type = qMetaTypeId<QScriptValue>(); |
|
703 } else if (callType == QMetaType::Int) { |
|
704 *((int *)&data) = int(value.toInt32()); |
|
705 type = callType; |
|
706 } else if (callType == QMetaType::UInt) { |
|
707 *((uint *)&data) = uint(value.toUInt32()); |
|
708 type = callType; |
|
709 } else if (callType == QMetaType::Bool) { |
|
710 *((bool *)&data) = value.toBool(); |
|
711 type = callType; |
|
712 } else if (callType == QMetaType::Double) { |
|
713 *((double *)&data) = double(value.toNumber()); |
|
714 type = callType; |
|
715 } else if (callType == QMetaType::Float) { |
|
716 *((float *)&data) = float(value.toNumber()); |
|
717 type = callType; |
|
718 } else if (callType == QMetaType::QString) { |
|
719 if (value.isNull() || value.isUndefined()) |
|
720 new (&data) QString(); |
|
721 else |
|
722 new (&data) QString(value.toString()); |
|
723 type = callType; |
|
724 } else if (callType == QMetaType::QObjectStar) { |
|
725 *((QObject **)&data) = value.toQObject(); |
|
726 type = callType; |
|
727 } else if (callType == qMetaTypeId<QVariant>()) { |
|
728 new (&data) QVariant(QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value)); |
|
729 type = callType; |
|
730 } else if (callType == qMetaTypeId<QList<QObject*> >()) { |
|
731 QList<QObject *> *list = new (&data) QList<QObject *>(); |
|
732 if (value.isArray()) { |
|
733 int length = value.property(QLatin1String("length")).toInt32(); |
|
734 for (int ii = 0; ii < length; ++ii) { |
|
735 QScriptValue arrayItem = value.property(ii); |
|
736 QObject *d = arrayItem.toQObject(); |
|
737 list->append(d); |
|
738 } |
|
739 } else if (QObject *d = value.toQObject()) { |
|
740 list->append(d); |
|
741 } |
|
742 type = callType; |
|
743 } else { |
|
744 new (&data) QVariant(); |
|
745 type = -1; |
|
746 |
|
747 QVariant v = QDeclarativeEnginePrivate::get(engine)->scriptValueToVariant(value); |
|
748 if (v.userType() == callType) { |
|
749 *((QVariant *)&data) = v; |
|
750 } else if (v.canConvert((QVariant::Type)callType)) { |
|
751 *((QVariant *)&data) = v; |
|
752 ((QVariant *)&data)->convert((QVariant::Type)callType); |
|
753 } else { |
|
754 *((QVariant *)&data) = QVariant(callType, (void *)0); |
|
755 } |
|
756 } |
|
757 } |
|
758 |
|
759 QScriptDeclarativeClass::Value MetaCallArgument::toValue(QDeclarativeEngine *e) |
|
760 { |
|
761 QScriptEngine *engine = QDeclarativeEnginePrivate::getScriptEngine(e); |
|
762 |
|
763 if (type == qMetaTypeId<QScriptValue>()) { |
|
764 return QScriptDeclarativeClass::Value(engine, *((QScriptValue *)&data)); |
|
765 } else if (type == QMetaType::Int) { |
|
766 return QScriptDeclarativeClass::Value(engine, *((int *)&data)); |
|
767 } else if (type == QMetaType::UInt) { |
|
768 return QScriptDeclarativeClass::Value(engine, *((uint *)&data)); |
|
769 } else if (type == QMetaType::Bool) { |
|
770 return QScriptDeclarativeClass::Value(engine, *((bool *)&data)); |
|
771 } else if (type == QMetaType::Double) { |
|
772 return QScriptDeclarativeClass::Value(engine, *((double *)&data)); |
|
773 } else if (type == QMetaType::Float) { |
|
774 return QScriptDeclarativeClass::Value(engine, *((float *)&data)); |
|
775 } else if (type == QMetaType::QString) { |
|
776 return QScriptDeclarativeClass::Value(engine, *((QString *)&data)); |
|
777 } else if (type == QMetaType::QObjectStar) { |
|
778 QObject *object = *((QObject **)&data); |
|
779 if (object) |
|
780 QDeclarativeData::get(object, true)->setImplicitDestructible(); |
|
781 QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e); |
|
782 return QScriptDeclarativeClass::Value(engine, priv->objectClass->newQObject(object)); |
|
783 } else if (type == qMetaTypeId<QList<QObject *> >()) { |
|
784 QList<QObject *> &list = *(QList<QObject *>*)&data; |
|
785 QScriptValue rv = engine->newArray(list.count()); |
|
786 QDeclarativeEnginePrivate *priv = QDeclarativeEnginePrivate::get(e); |
|
787 for (int ii = 0; ii < list.count(); ++ii) { |
|
788 QObject *object = list.at(ii); |
|
789 QDeclarativeData::get(object, true)->setImplicitDestructible(); |
|
790 rv.setProperty(ii, priv->objectClass->newQObject(object)); |
|
791 } |
|
792 return QScriptDeclarativeClass::Value(engine, rv); |
|
793 } else if (type == -1 || type == qMetaTypeId<QVariant>()) { |
|
794 return QScriptDeclarativeClass::Value(engine, QDeclarativeEnginePrivate::get(e)->scriptValueFromVariant(*((QVariant *)&data))); |
|
795 } else { |
|
796 return QScriptDeclarativeClass::Value(); |
|
797 } |
|
798 } |
|
799 |
|
800 QDeclarativeObjectMethodScriptClass::Value QDeclarativeObjectMethodScriptClass::call(Object *o, QScriptContext *ctxt) |
|
801 { |
|
802 MethodData *method = static_cast<MethodData *>(o); |
|
803 |
|
804 if (method->data.flags & QDeclarativePropertyCache::Data::HasArguments) { |
|
805 |
|
806 QMetaMethod m = method->object->metaObject()->method(method->data.coreIndex); |
|
807 QList<QByteArray> argTypeNames = m.parameterTypes(); |
|
808 QVarLengthArray<int, 9> argTypes(argTypeNames.count()); |
|
809 |
|
810 // ### Cache |
|
811 for (int ii = 0; ii < argTypeNames.count(); ++ii) { |
|
812 argTypes[ii] = QMetaType::type(argTypeNames.at(ii)); |
|
813 if (argTypes[ii] == QVariant::Invalid) |
|
814 return Value(ctxt, ctxt->throwError(QString::fromLatin1("Unknown method parameter type: %1").arg(QLatin1String(argTypeNames.at(ii))))); |
|
815 } |
|
816 |
|
817 if (argTypes.count() > ctxt->argumentCount()) |
|
818 return Value(ctxt, ctxt->throwError(QLatin1String("Insufficient arguments"))); |
|
819 |
|
820 QVarLengthArray<MetaCallArgument, 9> args(argTypes.count() + 1); |
|
821 args[0].initAsType(method->data.propType, engine); |
|
822 |
|
823 for (int ii = 0; ii < argTypes.count(); ++ii) |
|
824 args[ii + 1].fromScriptValue(argTypes[ii], engine, ctxt->argument(ii)); |
|
825 |
|
826 QVarLengthArray<void *, 9> argData(args.count()); |
|
827 for (int ii = 0; ii < args.count(); ++ii) |
|
828 argData[ii] = args[ii].dataPtr(); |
|
829 |
|
830 QMetaObject::metacall(method->object, QMetaObject::InvokeMetaMethod, method->data.coreIndex, argData.data()); |
|
831 |
|
832 return args[0].toValue(engine); |
|
833 |
|
834 } else if (method->data.propType != 0) { |
|
835 |
|
836 MetaCallArgument arg; |
|
837 arg.initAsType(method->data.propType, engine); |
|
838 |
|
839 void *args[] = { arg.dataPtr() }; |
|
840 |
|
841 QMetaObject::metacall(method->object, QMetaObject::InvokeMetaMethod, method->data.coreIndex, args); |
|
842 |
|
843 return arg.toValue(engine); |
|
844 |
|
845 } else { |
|
846 |
|
847 void *args[] = { 0 }; |
|
848 QMetaObject::metacall(method->object, QMetaObject::InvokeMetaMethod, method->data.coreIndex, args); |
|
849 return Value(); |
|
850 |
|
851 } |
|
852 return Value(); |
|
853 } |
|
854 |
|
855 QT_END_NAMESPACE |
|
856 |