source: trunk/src/script/bridge/qscriptqobject.cpp@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

  • Property svn:eol-style set to native
File size: 89.2 KB
Line 
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 QtScript module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL-ONLY$
10** GNU Lesser General Public License Usage
11** This file may be used under the terms of the GNU Lesser
12** General Public License version 2.1 as published by the Free Software
13** Foundation and appearing in the file LICENSE.LGPL included in the
14** packaging of this file. Please review the following information to
15** ensure the GNU Lesser General Public License version 2.1 requirements
16** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17**
18** If you have questions regarding the use of this file, please contact
19** Nokia at qt-info@nokia.com.
20** $QT_END_LICENSE$
21**
22****************************************************************************/
23
24#include "config.h"
25#include "qscriptqobject_p.h"
26
27#include <QtCore/qmetaobject.h>
28#include <QtCore/qvarlengtharray.h>
29#include <QtCore/qdebug.h>
30#include <QtScript/qscriptable.h>
31#include "../api/qscriptengine_p.h"
32#include "../api/qscriptable_p.h"
33#include "../api/qscriptcontext_p.h"
34#include "qscriptfunction_p.h"
35
36#include "Error.h"
37#include "PrototypeFunction.h"
38#include "PropertyNameArray.h"
39#include "JSFunction.h"
40#include "JSString.h"
41#include "JSValue.h"
42#include "JSArray.h"
43#include "RegExpObject.h"
44#include "RegExpConstructor.h"
45
46namespace JSC
47{
48QT_USE_NAMESPACE
49ASSERT_CLASS_FITS_IN_CELL(QScript::QObjectPrototype);
50ASSERT_CLASS_FITS_IN_CELL(QScript::QMetaObjectWrapperObject);
51ASSERT_CLASS_FITS_IN_CELL(QScript::QMetaObjectPrototype);
52ASSERT_CLASS_FITS_IN_CELL(QScript::QtFunction);
53ASSERT_CLASS_FITS_IN_CELL(QScript::QtPropertyFunction);
54}
55
56QT_BEGIN_NAMESPACE
57
58namespace QScript
59{
60
61struct QObjectConnection
62{
63 int slotIndex;
64 JSC::JSValue receiver;
65 JSC::JSValue slot;
66 JSC::JSValue senderWrapper;
67
68 QObjectConnection(int i, JSC::JSValue r, JSC::JSValue s,
69 JSC::JSValue sw)
70 : slotIndex(i), receiver(r), slot(s), senderWrapper(sw) {}
71 QObjectConnection() : slotIndex(-1) {}
72
73 bool hasTarget(JSC::JSValue r, JSC::JSValue s) const
74 {
75 if ((r && r.isObject()) != (receiver && receiver.isObject()))
76 return false;
77 if (((r && r.isObject()) && (receiver && receiver.isObject()))
78 && (r != receiver)) {
79 return false;
80 }
81 return (s == slot);
82 }
83
84 void mark(JSC::MarkStack& markStack)
85 {
86 if (senderWrapper) {
87 // see if the sender should be marked or not;
88 // if the C++ object is owned by script, we don't want
89 // it to stay alive due to a script connection.
90 Q_ASSERT(senderWrapper.inherits(&QScriptObject::info));
91 QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(senderWrapper));
92 if (!JSC::Heap::isCellMarked(scriptObject)) {
93 QScriptObjectDelegate *delegate = scriptObject->delegate();
94 Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::QtObject));
95 QObjectDelegate *inst = static_cast<QObjectDelegate*>(delegate);
96 if ((inst->ownership() == QScriptEngine::ScriptOwnership)
97 || ((inst->ownership() == QScriptEngine::AutoOwnership)
98 && inst->value() && !inst->value()->parent())) {
99 senderWrapper = JSC::JSValue();
100 } else {
101 markStack.append(senderWrapper);
102 }
103 }
104 }
105 if (receiver)
106 markStack.append(receiver);
107 if (slot)
108 markStack.append(slot);
109 }
110};
111
112class QObjectNotifyCaller : public QObject
113{
114public:
115 void callConnectNotify(const char *signal)
116 { connectNotify(signal); }
117 void callDisconnectNotify(const char *signal)
118 { disconnectNotify(signal); }
119};
120
121class QObjectConnectionManager: public QObject
122{
123public:
124 QObjectConnectionManager(QScriptEnginePrivate *engine);
125 ~QObjectConnectionManager();
126
127 bool addSignalHandler(QObject *sender, int signalIndex,
128 JSC::JSValue receiver,
129 JSC::JSValue slot,
130 JSC::JSValue senderWrapper,
131 Qt::ConnectionType type);
132 bool removeSignalHandler(QObject *sender, int signalIndex,
133 JSC::JSValue receiver,
134 JSC::JSValue slot);
135
136 static const QMetaObject staticMetaObject;
137 virtual const QMetaObject *metaObject() const;
138 virtual void *qt_metacast(const char *);
139 virtual int qt_metacall(QMetaObject::Call, int, void **argv);
140
141 void execute(int slotIndex, void **argv);
142
143 void mark(JSC::MarkStack&);
144
145private:
146 QScriptEnginePrivate *engine;
147 int slotCounter;
148 QVector<QVector<QObjectConnection> > connections;
149};
150
151static bool hasMethodAccess(const QMetaMethod &method, int index, const QScriptEngine::QObjectWrapOptions &opt)
152{
153 return (method.access() != QMetaMethod::Private)
154 && ((index != 2) || !(opt & QScriptEngine::ExcludeDeleteLater));
155}
156
157static bool isEnumerableMetaProperty(const QMetaProperty &prop,
158 const QMetaObject *mo, int index)
159{
160 return prop.isScriptable() && prop.isValid()
161 // the following lookup is to ensure that we have the
162 // "most derived" occurrence of the property with this name
163 && (mo->indexOfProperty(prop.name()) == index);
164}
165
166static inline QByteArray methodName(const QMetaMethod &method)
167{
168 QByteArray signature = method.signature();
169 return signature.left(signature.indexOf('('));
170}
171
172static QVariant variantFromValue(QScriptEnginePrivate *eng,
173 int targetType, const QScriptValue &value)
174{
175 QVariant v(targetType, (void *)0);
176 Q_ASSERT(eng);
177 if (QScriptEnginePrivate::convert(value, targetType, v.data(), eng))
178 return v;
179 if (uint(targetType) == QVariant::LastType)
180 return value.toVariant();
181 if (value.isVariant()) {
182 v = value.toVariant();
183 if (v.canConvert(QVariant::Type(targetType))) {
184 v.convert(QVariant::Type(targetType));
185 return v;
186 }
187 QByteArray typeName = v.typeName();
188 if (typeName.endsWith('*')
189 && (QMetaType::type(typeName.left(typeName.size()-1)) == targetType)) {
190 return QVariant(targetType, *reinterpret_cast<void* *>(v.data()));
191 }
192 }
193
194 return QVariant();
195}
196
197static const bool GeneratePropertyFunctions = true;
198
199static unsigned flagsForMetaProperty(const QMetaProperty &prop)
200{
201 return (JSC::DontDelete
202 | (!prop.isWritable() ? unsigned(JSC::ReadOnly) : unsigned(0))
203 | (GeneratePropertyFunctions
204 ? unsigned(JSC::Getter | JSC::Setter)
205 : unsigned(0))
206 | QObjectMemberAttribute);
207}
208
209static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str)
210{
211 QByteArray scope;
212 QByteArray name;
213 int scopeIdx = str.lastIndexOf("::");
214 if (scopeIdx != -1) {
215 scope = str.left(scopeIdx);
216 name = str.mid(scopeIdx + 2);
217 } else {
218 name = str;
219 }
220 for (int i = meta->enumeratorCount() - 1; i >= 0; --i) {
221 QMetaEnum m = meta->enumerator(i);
222 if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope)))
223 return i;
224 }
225 return -1;
226}
227
228static inline QScriptable *scriptableFromQObject(QObject *qobj)
229{
230 void *ptr = qobj->qt_metacast("QScriptable");
231 return reinterpret_cast<QScriptable*>(ptr);
232}
233
234QtFunction::QtFunction(JSC::JSValue object, int initialIndex, bool maybeOverloaded,
235 JSC::JSGlobalData *data, WTF::PassRefPtr<JSC::Structure> sid,
236 const JSC::Identifier &ident)
237 : JSC::InternalFunction(data, sid, ident),
238 data(new Data(object, initialIndex, maybeOverloaded))
239{
240}
241
242QtFunction::~QtFunction()
243{
244 delete data;
245}
246
247JSC::CallType QtFunction::getCallData(JSC::CallData &callData)
248{
249 callData.native.function = call;
250 return JSC::CallTypeHost;
251}
252
253void QtFunction::markChildren(JSC::MarkStack& markStack)
254{
255 if (data->object)
256 markStack.append(data->object);
257 JSC::InternalFunction::markChildren(markStack);
258}
259
260QScriptObject *QtFunction::wrapperObject() const
261{
262 Q_ASSERT(JSC::asObject(data->object)->inherits(&QScriptObject::info));
263 return static_cast<QScriptObject*>(JSC::asObject(data->object));
264}
265
266QObject *QtFunction::qobject() const
267{
268 QScriptObject *scriptObject = wrapperObject();
269 QScriptObjectDelegate *delegate = scriptObject->delegate();
270 Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::QtObject));
271 return static_cast<QScript::QObjectDelegate*>(delegate)->value();
272}
273
274const QMetaObject *QtFunction::metaObject() const
275{
276 QObject *qobj = qobject();
277 if (!qobj)
278 return 0;
279 return qobj->metaObject();
280}
281
282int QtFunction::initialIndex() const
283{
284 return data->initialIndex;
285}
286
287bool QtFunction::maybeOverloaded() const
288{
289 return data->maybeOverloaded;
290}
291
292int QtFunction::mostGeneralMethod(QMetaMethod *out) const
293{
294 const QMetaObject *meta = metaObject();
295 if (!meta)
296 return -1;
297 int index = initialIndex();
298 QMetaMethod method = meta->method(index);
299 if (maybeOverloaded() && (method.attributes() & QMetaMethod::Cloned)) {
300 // find the most general method
301 do {
302 method = meta->method(--index);
303 } while (method.attributes() & QMetaMethod::Cloned);
304 }
305 if (out)
306 *out = method;
307 return index;
308}
309
310QList<int> QScript::QtFunction::overloadedIndexes() const
311{
312 if (!maybeOverloaded())
313 return QList<int>();
314 QList<int> result;
315 QString name = functionName();
316 const QMetaObject *meta = metaObject();
317 for (int index = mostGeneralMethod() - 1; index >= 0; --index) {
318 QString otherName = QString::fromLatin1(methodName(meta->method(index)));
319 if (otherName == name)
320 result.append(index);
321 }
322 return result;
323}
324
325QString QtFunction::functionName() const
326{
327 const QMetaObject *meta = metaObject();
328 if (!meta)
329 return QString();
330 QMetaMethod method = meta->method(initialIndex());
331 return QLatin1String(methodName(method));
332}
333
334class QScriptMetaType
335{
336public:
337 enum Kind {
338 Invalid,
339 Variant,
340 MetaType,
341 Unresolved,
342 MetaEnum
343 };
344
345 inline QScriptMetaType()
346 : m_kind(Invalid) { }
347
348 inline Kind kind() const
349 { return m_kind; }
350
351 int typeId() const;
352
353 inline bool isValid() const
354 { return (m_kind != Invalid); }
355
356 inline bool isVariant() const
357 { return (m_kind == Variant); }
358
359 inline bool isMetaType() const
360 { return (m_kind == MetaType); }
361
362 inline bool isUnresolved() const
363 { return (m_kind == Unresolved); }
364
365 inline bool isMetaEnum() const
366 { return (m_kind == MetaEnum); }
367
368 QByteArray name() const;
369
370 inline int enumeratorIndex() const
371 { Q_ASSERT(isMetaEnum()); return m_typeId; }
372
373 inline bool operator==(const QScriptMetaType &other) const
374 {
375 return (m_kind == other.m_kind) && (m_typeId == other.m_typeId);
376 }
377
378 static inline QScriptMetaType variant()
379 { return QScriptMetaType(Variant); }
380
381 static inline QScriptMetaType metaType(int typeId, const QByteArray &name)
382 { return QScriptMetaType(MetaType, typeId, name); }
383
384 static inline QScriptMetaType metaEnum(int enumIndex, const QByteArray &name)
385 { return QScriptMetaType(MetaEnum, enumIndex, name); }
386
387 static inline QScriptMetaType unresolved(const QByteArray &name)
388 { return QScriptMetaType(Unresolved, /*typeId=*/0, name); }
389
390private:
391 inline QScriptMetaType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray())
392 : m_kind(kind), m_typeId(typeId), m_name(name) { }
393
394 Kind m_kind;
395 int m_typeId;
396 QByteArray m_name;
397};
398
399int QScriptMetaType::typeId() const
400{
401 if (isVariant())
402 return QMetaType::type("QVariant");
403 return isMetaEnum() ? 2/*int*/ : m_typeId;
404}
405
406QByteArray QScriptMetaType::name() const
407{
408 if (!m_name.isEmpty())
409 return m_name;
410 else if (m_kind == Variant)
411 return "QVariant";
412 return QMetaType::typeName(typeId());
413}
414
415class QScriptMetaMethod
416{
417public:
418 inline QScriptMetaMethod()
419 { }
420 inline QScriptMetaMethod(const QByteArray &name, const QVector<QScriptMetaType> &types)
421 : m_name(name), m_types(types), m_firstUnresolvedIndex(-1)
422 {
423 QVector<QScriptMetaType>::const_iterator it;
424 for (it = m_types.constBegin(); it != m_types.constEnd(); ++it) {
425 if ((*it).kind() == QScriptMetaType::Unresolved) {
426 m_firstUnresolvedIndex = it - m_types.constBegin();
427 break;
428 }
429 }
430 }
431 inline bool isValid() const
432 { return !m_types.isEmpty(); }
433
434 QByteArray name() const
435 { return m_name; }
436
437 inline QScriptMetaType returnType() const
438 { return m_types.at(0); }
439
440 inline int argumentCount() const
441 { return m_types.count() - 1; }
442
443 inline QScriptMetaType argumentType(int arg) const
444 { return m_types.at(arg + 1); }
445
446 inline bool fullyResolved() const
447 { return m_firstUnresolvedIndex == -1; }
448
449 inline bool hasUnresolvedReturnType() const
450 { return (m_firstUnresolvedIndex == 0); }
451
452 inline int firstUnresolvedIndex() const
453 { return m_firstUnresolvedIndex; }
454
455 inline int count() const
456 { return m_types.count(); }
457
458 inline QScriptMetaType type(int index) const
459 { return m_types.at(index); }
460
461 inline QVector<QScriptMetaType> types() const
462 { return m_types; }
463
464private:
465 QByteArray m_name;
466 QVector<QScriptMetaType> m_types;
467 int m_firstUnresolvedIndex;
468};
469
470struct QScriptMetaArguments
471{
472 int matchDistance;
473 int index;
474 QScriptMetaMethod method;
475 QVarLengthArray<QVariant, 9> args;
476
477 inline QScriptMetaArguments(int dist, int idx, const QScriptMetaMethod &mtd,
478 const QVarLengthArray<QVariant, 9> &as)
479 : matchDistance(dist), index(idx), method(mtd), args(as) { }
480 inline QScriptMetaArguments()
481 : index(-1) { }
482
483 inline bool isValid() const
484 { return (index != -1); }
485};
486
487static QMetaMethod metaMethod(const QMetaObject *meta,
488 QMetaMethod::MethodType type,
489 int index)
490{
491 if (type != QMetaMethod::Constructor)
492 return meta->method(index);
493 else
494 return meta->constructor(index);
495}
496
497static JSC::JSValue callQtMethod(JSC::ExecState *exec, QMetaMethod::MethodType callType,
498 QObject *thisQObject, const JSC::ArgList &scriptArgs,
499 const QMetaObject *meta, int initialIndex,
500 bool maybeOverloaded)
501{
502 QByteArray funName;
503 QScriptMetaMethod chosenMethod;
504 int chosenIndex = -1;
505 QVarLengthArray<QVariant, 9> args;
506 QVector<QScriptMetaArguments> candidates;
507 QVector<QScriptMetaArguments> unresolved;
508 QVector<int> tooFewArgs;
509 QVector<int> conversionFailed;
510 int index;
511 exec->clearException();
512 QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec);
513 for (index = initialIndex; index >= 0; --index) {
514 QMetaMethod method = metaMethod(meta, callType, index);
515
516 if (index == initialIndex)
517 funName = methodName(method);
518 else {
519 if (methodName(method) != funName)
520 continue;
521 }
522
523 QVector<QScriptMetaType> types;
524 // resolve return type
525 QByteArray returnTypeName = method.typeName();
526 int rtype = QMetaType::type(returnTypeName);
527 if ((rtype == 0) && !returnTypeName.isEmpty()) {
528 if (returnTypeName == "QVariant") {
529 types.append(QScriptMetaType::variant());
530 } else {
531 int enumIndex = indexOfMetaEnum(meta, returnTypeName);
532 if (enumIndex != -1)
533 types.append(QScriptMetaType::metaEnum(enumIndex, returnTypeName));
534 else
535 types.append(QScriptMetaType::unresolved(returnTypeName));
536 }
537 } else {
538 if (callType == QMetaMethod::Constructor)
539 types.append(QScriptMetaType::metaType(QMetaType::QObjectStar, "QObject*"));
540 else if (returnTypeName == "QVariant")
541 types.append(QScriptMetaType::variant());
542 else
543 types.append(QScriptMetaType::metaType(rtype, returnTypeName));
544 }
545
546 // resolve argument types
547 QList<QByteArray> parameterTypeNames = method.parameterTypes();
548 for (int i = 0; i < parameterTypeNames.count(); ++i) {
549 QByteArray argTypeName = parameterTypeNames.at(i);
550 int atype = QMetaType::type(argTypeName);
551 if (atype == 0) {
552 if (argTypeName == "QVariant") {
553 types.append(QScriptMetaType::variant());
554 } else {
555 int enumIndex = indexOfMetaEnum(meta, argTypeName);
556 if (enumIndex != -1)
557 types.append(QScriptMetaType::metaEnum(enumIndex, argTypeName));
558 else
559 types.append(QScriptMetaType::unresolved(argTypeName));
560 }
561 } else {
562 if (argTypeName == "QVariant")
563 types.append(QScriptMetaType::variant());
564 else
565 types.append(QScriptMetaType::metaType(atype, argTypeName));
566 }
567 }
568
569 QScriptMetaMethod mtd = QScriptMetaMethod(methodName(method), types);
570
571 if (int(scriptArgs.size()) < mtd.argumentCount()) {
572 tooFewArgs.append(index);
573 continue;
574 }
575
576 if (!mtd.fullyResolved()) {
577 // remember it so we can give an error message later, if necessary
578 unresolved.append(QScriptMetaArguments(/*matchDistance=*/INT_MAX, index,
579 mtd, QVarLengthArray<QVariant, 9>()));
580 if (mtd.hasUnresolvedReturnType())
581 continue;
582 }
583
584 if (args.count() != mtd.count())
585 args.resize(mtd.count());
586
587 QScriptMetaType retType = mtd.returnType();
588 args[0] = QVariant(retType.typeId(), (void *)0); // the result
589
590 // try to convert arguments
591 bool converted = true;
592 int matchDistance = 0;
593 for (int i = 0; converted && i < mtd.argumentCount(); ++i) {
594 QScriptValue actual;
595 if (i < (int)scriptArgs.size())
596 actual = engine->scriptValueFromJSCValue(scriptArgs.at(i));
597 else
598 actual = QScriptValue(QScriptValue::UndefinedValue);
599 QScriptMetaType argType = mtd.argumentType(i);
600 int tid = -1;
601 QVariant v;
602 if (argType.isUnresolved()) {
603 v = QVariant(QMetaType::QObjectStar, (void *)0);
604 converted = engine->convertToNativeQObject(
605 actual, argType.name(), reinterpret_cast<void* *>(v.data()));
606 } else if (argType.isVariant()) {
607 if (actual.isVariant()) {
608 v = actual.toVariant();
609 } else {
610 v = actual.toVariant();
611 converted = v.isValid() || actual.isUndefined() || actual.isNull();
612 }
613 } else {
614 tid = argType.typeId();
615 v = QVariant(tid, (void *)0);
616 converted = QScriptEnginePrivate::convert(actual, tid, v.data(), engine);
617 if (exec->hadException())
618 return exec->exception();
619 }
620
621 if (!converted) {
622 if (actual.isVariant()) {
623 if (tid == -1)
624 tid = argType.typeId();
625 QVariant vv = actual.toVariant();
626 if (vv.canConvert(QVariant::Type(tid))) {
627 v = vv;
628 converted = v.convert(QVariant::Type(tid));
629 if (converted && (vv.userType() != tid))
630 matchDistance += 10;
631 } else {
632 QByteArray vvTypeName = vv.typeName();
633 if (vvTypeName.endsWith('*')
634 && (vvTypeName.left(vvTypeName.size()-1) == argType.name())) {
635 v = QVariant(tid, *reinterpret_cast<void* *>(vv.data()));
636 converted = true;
637 matchDistance += 10;
638 }
639 }
640 } else if (actual.isNumber() || actual.isString()) {
641 // see if it's an enum value
642 QMetaEnum m;
643 if (argType.isMetaEnum()) {
644 m = meta->enumerator(argType.enumeratorIndex());
645 } else {
646 int mi = indexOfMetaEnum(meta, argType.name());
647 if (mi != -1)
648 m = meta->enumerator(mi);
649 }
650 if (m.isValid()) {
651 if (actual.isNumber()) {
652 int ival = actual.toInt32();
653 if (m.valueToKey(ival) != 0) {
654 qVariantSetValue(v, ival);
655 converted = true;
656 matchDistance += 10;
657 }
658 } else {
659 QString sval = actual.toString();
660 int ival = m.keyToValue(sval.toLatin1());
661 if (ival != -1) {
662 qVariantSetValue(v, ival);
663 converted = true;
664 matchDistance += 10;
665 }
666 }
667 }
668 }
669 } else {
670 // determine how well the conversion matched
671 if (actual.isNumber()) {
672 switch (tid) {
673 case QMetaType::Double:
674 // perfect
675 break;
676 case QMetaType::Float:
677 matchDistance += 1;
678 break;
679 case QMetaType::LongLong:
680 case QMetaType::ULongLong:
681 matchDistance += 2;
682 break;
683 case QMetaType::Long:
684 case QMetaType::ULong:
685 matchDistance += 3;
686 break;
687 case QMetaType::Int:
688 case QMetaType::UInt:
689 matchDistance += 4;
690 break;
691 case QMetaType::Short:
692 case QMetaType::UShort:
693 matchDistance += 5;
694 break;
695 case QMetaType::Char:
696 case QMetaType::UChar:
697 matchDistance += 6;
698 break;
699 default:
700 matchDistance += 10;
701 break;
702 }
703 } else if (actual.isString()) {
704 switch (tid) {
705 case QMetaType::QString:
706 // perfect
707 break;
708 default:
709 matchDistance += 10;
710 break;
711 }
712 } else if (actual.isBoolean()) {
713 switch (tid) {
714 case QMetaType::Bool:
715 // perfect
716 break;
717 default:
718 matchDistance += 10;
719 break;
720 }
721 } else if (actual.isDate()) {
722 switch (tid) {
723 case QMetaType::QDateTime:
724 // perfect
725 break;
726 case QMetaType::QDate:
727 matchDistance += 1;
728 break;
729 case QMetaType::QTime:
730 matchDistance += 2;
731 break;
732 default:
733 matchDistance += 10;
734 break;
735 }
736 } else if (actual.isRegExp()) {
737 switch (tid) {
738 case QMetaType::QRegExp:
739 // perfect
740 break;
741 default:
742 matchDistance += 10;
743 break;
744 }
745 } else if (actual.isVariant()) {
746 if (argType.isVariant()
747 || (actual.toVariant().userType() == tid)) {
748 // perfect
749 } else {
750 matchDistance += 10;
751 }
752 } else if (actual.isArray()) {
753 switch (tid) {
754 case QMetaType::QStringList:
755 case QMetaType::QVariantList:
756 matchDistance += 5;
757 break;
758 default:
759 matchDistance += 10;
760 break;
761 }
762 } else if (actual.isQObject()) {
763 switch (tid) {
764 case QMetaType::QObjectStar:
765 case QMetaType::QWidgetStar:
766 // perfect
767 break;
768 default:
769 matchDistance += 10;
770 break;
771 }
772 } else if (actual.isNull()) {
773 switch (tid) {
774 case QMetaType::VoidStar:
775 case QMetaType::QObjectStar:
776 case QMetaType::QWidgetStar:
777 // perfect
778 break;
779 default:
780 if (!argType.name().endsWith('*'))
781 matchDistance += 10;
782 break;
783 }
784 } else {
785 matchDistance += 10;
786 }
787 }
788
789 if (converted)
790 args[i+1] = v;
791 }
792
793 if (converted) {
794 if ((scriptArgs.size() == (size_t)mtd.argumentCount())
795 && (matchDistance == 0)) {
796 // perfect match, use this one
797 chosenMethod = mtd;
798 chosenIndex = index;
799 break;
800 } else {
801 bool redundant = false;
802 if ((callType != QMetaMethod::Constructor)
803 && (index < meta->methodOffset())) {
804 // it is possible that a virtual method is redeclared in a subclass,
805 // in which case we want to ignore the superclass declaration
806 for (int i = 0; i < candidates.size(); ++i) {
807 const QScriptMetaArguments &other = candidates.at(i);
808 if (mtd.types() == other.method.types()) {
809 redundant = true;
810 break;
811 }
812 }
813 }
814 if (!redundant) {
815 QScriptMetaArguments metaArgs(matchDistance, index, mtd, args);
816 if (candidates.isEmpty()) {
817 candidates.append(metaArgs);
818 } else {
819 const QScriptMetaArguments &otherArgs = candidates.at(0);
820 if ((args.count() > otherArgs.args.count())
821 || ((args.count() == otherArgs.args.count())
822 && (matchDistance <= otherArgs.matchDistance))) {
823 candidates.prepend(metaArgs);
824 } else {
825 candidates.append(metaArgs);
826 }
827 }
828 }
829 }
830 } else if (mtd.fullyResolved()) {
831 conversionFailed.append(index);
832 }
833
834 if (!maybeOverloaded)
835 break;
836 }
837
838 JSC::JSValue result;
839 if ((chosenIndex == -1) && candidates.isEmpty()) {
840// context->calleeMetaIndex = initialIndex;
841//#ifndef Q_SCRIPT_NO_EVENT_NOTIFY
842// engine->notifyFunctionEntry(context);
843//#endif
844 if (!conversionFailed.isEmpty()) {
845 QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n")
846 .arg(QLatin1String(funName));
847 for (int i = 0; i < conversionFailed.size(); ++i) {
848 if (i > 0)
849 message += QLatin1String("\n");
850 QMetaMethod mtd = metaMethod(meta, callType, conversionFailed.at(i));
851 message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature()));
852 }
853 result = JSC::throwError(exec, JSC::TypeError, message);
854 } else if (!unresolved.isEmpty()) {
855 QScriptMetaArguments argsInstance = unresolved.first();
856 int unresolvedIndex = argsInstance.method.firstUnresolvedIndex();
857 Q_ASSERT(unresolvedIndex != -1);
858 QScriptMetaType unresolvedType = argsInstance.method.type(unresolvedIndex);
859 QString unresolvedTypeName = QString::fromLatin1(unresolvedType.name());
860 QString message = QString::fromLatin1("cannot call %0(): ")
861 .arg(QString::fromLatin1(funName));
862 if (unresolvedIndex > 0) {
863 message.append(QString::fromLatin1("argument %0 has unknown type `%1'").
864 arg(unresolvedIndex).arg(unresolvedTypeName));
865 } else {
866 message.append(QString::fromLatin1("unknown return type `%0'")
867 .arg(unresolvedTypeName));
868 }
869 message.append(QString::fromLatin1(" (register the type with qScriptRegisterMetaType())"));
870 result = JSC::throwError(exec, JSC::TypeError, message);
871 } else {
872 QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n")
873 .arg(QLatin1String(funName));
874 for (int i = 0; i < tooFewArgs.size(); ++i) {
875 if (i > 0)
876 message += QLatin1String("\n");
877 QMetaMethod mtd = metaMethod(meta, callType, tooFewArgs.at(i));
878 message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature()));
879 }
880 result = JSC::throwError(exec, JSC::SyntaxError, message);
881 }
882 } else {
883 if (chosenIndex == -1) {
884 QScriptMetaArguments metaArgs = candidates.at(0);
885 if ((candidates.size() > 1)
886 && (metaArgs.args.count() == candidates.at(1).args.count())
887 && (metaArgs.matchDistance == candidates.at(1).matchDistance)) {
888 // ambiguous call
889 QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n")
890 .arg(QLatin1String(funName));
891 for (int i = 0; i < candidates.size(); ++i) {
892 if (i > 0)
893 message += QLatin1String("\n");
894 QMetaMethod mtd = metaMethod(meta, callType, candidates.at(i).index);
895 message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature()));
896 }
897 result = JSC::throwError(exec, JSC::TypeError, message);
898 } else {
899 chosenMethod = metaArgs.method;
900 chosenIndex = metaArgs.index;
901 args = metaArgs.args;
902 }
903 }
904
905 if (chosenIndex != -1) {
906 // call it
907// context->calleeMetaIndex = chosenIndex;
908
909 QVarLengthArray<void*, 9> array(args.count());
910 void **params = array.data();
911 for (int i = 0; i < args.count(); ++i) {
912 const QVariant &v = args[i];
913 switch (chosenMethod.type(i).kind()) {
914 case QScriptMetaType::Variant:
915 params[i] = const_cast<QVariant*>(&v);
916 break;
917 case QScriptMetaType::MetaType:
918 case QScriptMetaType::MetaEnum:
919 case QScriptMetaType::Unresolved:
920 params[i] = const_cast<void*>(v.constData());
921 break;
922 default:
923 Q_ASSERT(0);
924 }
925 }
926
927 QScriptable *scriptable = 0;
928 if (thisQObject)
929 scriptable = scriptableFromQObject(thisQObject);
930 QScriptEngine *oldEngine = 0;
931 if (scriptable) {
932 oldEngine = QScriptablePrivate::get(scriptable)->engine;
933 QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine);
934 }
935
936// ### fixme
937//#ifndef Q_SCRIPT_NO_EVENT_NOTIFY
938// engine->notifyFunctionEntry(context);
939//#endif
940
941 if (callType == QMetaMethod::Constructor) {
942 Q_ASSERT(meta != 0);
943 meta->static_metacall(QMetaObject::CreateInstance, chosenIndex, params);
944 } else {
945 QMetaObject::metacall(thisQObject, QMetaObject::InvokeMetaMethod, chosenIndex, params);
946 }
947
948 if (scriptable)
949 QScriptablePrivate::get(scriptable)->engine = oldEngine;
950
951 if (exec->hadException()) {
952 result = exec->exception() ; // propagate
953 } else {
954 QScriptMetaType retType = chosenMethod.returnType();
955 if (retType.isVariant()) {
956 result = engine->jscValueFromVariant(*(QVariant *)params[0]);
957 } else if (retType.typeId() != 0) {
958 result = engine->scriptValueToJSCValue(engine->create(retType.typeId(), params[0]));
959 if (!result) {
960 QScriptValue sv = QScriptEnginePrivate::get(engine)->newVariant(QVariant(retType.typeId(), params[0]));
961 result = engine->scriptValueToJSCValue(sv);
962 }
963 } else {
964 result = JSC::jsUndefined();
965 }
966 }
967 }
968 }
969
970 return result;
971}
972
973JSC::JSValue QtFunction::execute(JSC::ExecState *exec, JSC::JSValue thisValue,
974 const JSC::ArgList &scriptArgs)
975{
976 Q_ASSERT(data->object.inherits(&QScriptObject::info));
977 QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(data->object));
978 QScriptObjectDelegate *delegate = scriptObject->delegate();
979 Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::QtObject));
980 QObject *qobj = static_cast<QScript::QObjectDelegate*>(delegate)->value();
981 if (!qobj)
982 return JSC::throwError(exec, JSC::GeneralError, QString::fromLatin1("cannot call function of deleted QObject"));
983 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
984
985 const QMetaObject *meta = qobj->metaObject();
986 QObject *thisQObject = 0;
987 thisValue = engine->toUsableValue(thisValue);
988 if (thisValue.inherits(&QScriptObject::info)) {
989 delegate = static_cast<QScriptObject*>(JSC::asObject(thisValue))->delegate();
990 if (delegate && (delegate->type() == QScriptObjectDelegate::QtObject))
991 thisQObject = static_cast<QScript::QObjectDelegate*>(delegate)->value();
992 }
993 if (!thisQObject)
994 thisQObject = qobj; // ### TypeError
995
996 if (!meta->cast(thisQObject)) {
997 // invoking a function in the prototype
998 thisQObject = qobj;
999 }
1000
1001 return callQtMethod(exec, QMetaMethod::Method, thisQObject, scriptArgs,
1002 meta, data->initialIndex, data->maybeOverloaded);
1003}
1004
1005const JSC::ClassInfo QtFunction::info = { "QtFunction", &InternalFunction::info, 0, 0 };
1006
1007JSC::JSValue JSC_HOST_CALL QtFunction::call(JSC::ExecState *exec, JSC::JSObject *callee,
1008 JSC::JSValue thisValue, const JSC::ArgList &args)
1009{
1010 if (!callee->inherits(&QtFunction::info))
1011 return throwError(exec, JSC::TypeError, "callee is not a QtFunction object");
1012 QtFunction *qfun = static_cast<QtFunction*>(callee);
1013 QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec);
1014 JSC::ExecState *previousFrame = eng_p->currentFrame;
1015 eng_p->currentFrame = exec;
1016 eng_p->pushContext(exec, thisValue, args, callee);
1017 JSC::JSValue result = qfun->execute(eng_p->currentFrame, thisValue, args);
1018 eng_p->popContext();
1019 eng_p->currentFrame = previousFrame;
1020 return result;
1021}
1022
1023const JSC::ClassInfo QtPropertyFunction::info = { "QtPropertyFunction", &InternalFunction::info, 0, 0 };
1024
1025QtPropertyFunction::QtPropertyFunction(const QMetaObject *meta, int index,
1026 JSC::JSGlobalData *data,
1027 WTF::PassRefPtr<JSC::Structure> sid,
1028 const JSC::Identifier &ident)
1029 : JSC::InternalFunction(data, sid, ident),
1030 data(new Data(meta, index))
1031{
1032}
1033
1034QtPropertyFunction::~QtPropertyFunction()
1035{
1036 delete data;
1037}
1038
1039JSC::CallType QtPropertyFunction::getCallData(JSC::CallData &callData)
1040{
1041 callData.native.function = call;
1042 return JSC::CallTypeHost;
1043}
1044
1045JSC::JSValue JSC_HOST_CALL QtPropertyFunction::call(
1046 JSC::ExecState *exec, JSC::JSObject *callee,
1047 JSC::JSValue thisValue, const JSC::ArgList &args)
1048{
1049 if (!callee->inherits(&QtPropertyFunction::info))
1050 return throwError(exec, JSC::TypeError, "callee is not a QtPropertyFunction object");
1051 QtPropertyFunction *qfun = static_cast<QtPropertyFunction*>(callee);
1052 QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec);
1053 JSC::ExecState *previousFrame = eng_p->currentFrame;
1054 eng_p->currentFrame = exec;
1055 eng_p->pushContext(exec, thisValue, args, callee);
1056 JSC::JSValue result = qfun->execute(eng_p->currentFrame, thisValue, args);
1057 eng_p->popContext();
1058 eng_p->currentFrame = previousFrame;
1059 return result;
1060}
1061
1062JSC::JSValue QtPropertyFunction::execute(JSC::ExecState *exec,
1063 JSC::JSValue thisValue,
1064 const JSC::ArgList &args)
1065{
1066 JSC::JSValue result = JSC::jsUndefined();
1067
1068 // ### don't go via QScriptValue
1069 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
1070 thisValue = engine->toUsableValue(thisValue);
1071 QScriptValue object = engine->scriptValueFromJSCValue(thisValue);
1072 QObject *qobject = object.toQObject();
1073 while ((!qobject || (qobject->metaObject() != data->meta))
1074 && object.prototype().isObject()) {
1075 object = object.prototype();
1076 qobject = object.toQObject();
1077 }
1078 Q_ASSERT_X(qobject, Q_FUNC_INFO, "this-object must be a QObject");
1079
1080 QMetaProperty prop = data->meta->property(data->index);
1081 Q_ASSERT(prop.isScriptable());
1082 if (args.size() == 0) {
1083 // get
1084 if (prop.isValid()) {
1085 QScriptable *scriptable = scriptableFromQObject(qobject);
1086 QScriptEngine *oldEngine = 0;
1087 if (scriptable) {
1088 oldEngine = QScriptablePrivate::get(scriptable)->engine;
1089 QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine);
1090 }
1091
1092 QVariant v = prop.read(qobject);
1093
1094 if (scriptable)
1095 QScriptablePrivate::get(scriptable)->engine = oldEngine;
1096
1097 result = engine->jscValueFromVariant(v);
1098 }
1099 } else {
1100 // set
1101 JSC::JSValue arg = args.at(0);
1102 QVariant v;
1103 if (prop.isEnumType() && arg.isString()
1104 && !engine->hasDemarshalFunction(prop.userType())) {
1105 // give QMetaProperty::write() a chance to convert from
1106 // string to enum value
1107 v = (QString)arg.toString(exec);
1108 } else {
1109 // ### don't go via QScriptValue
1110 QScriptValue tmp = engine->scriptValueFromJSCValue(arg);
1111 v = variantFromValue(engine, prop.userType(), tmp);
1112 }
1113
1114 QScriptable *scriptable = scriptableFromQObject(qobject);
1115 QScriptEngine *oldEngine = 0;
1116 if (scriptable) {
1117 oldEngine = QScriptablePrivate::get(scriptable)->engine;
1118 QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine);
1119 }
1120
1121 prop.write(qobject, v);
1122
1123 if (scriptable)
1124 QScriptablePrivate::get(scriptable)->engine = oldEngine;
1125
1126 result = arg;
1127 }
1128 return result;
1129}
1130
1131const QMetaObject *QtPropertyFunction::metaObject() const
1132{
1133 return data->meta;
1134}
1135
1136int QtPropertyFunction::propertyIndex() const
1137{
1138 return data->index;
1139}
1140
1141
1142QObjectDelegate::QObjectDelegate(
1143 QObject *object, QScriptEngine::ValueOwnership ownership,
1144 const QScriptEngine::QObjectWrapOptions &options)
1145 : data(new Data(object, ownership, options))
1146{
1147}
1148
1149QObjectDelegate::~QObjectDelegate()
1150{
1151 switch (data->ownership) {
1152 case QScriptEngine::QtOwnership:
1153 break;
1154 case QScriptEngine::ScriptOwnership:
1155 if (data->value)
1156 delete data->value; // ### fixme
1157// eng->disposeQObject(value);
1158 break;
1159 case QScriptEngine::AutoOwnership:
1160 if (data->value && !data->value->parent())
1161 delete data->value; // ### fixme
1162// eng->disposeQObject(value);
1163 break;
1164 }
1165 delete data;
1166}
1167
1168QScriptObjectDelegate::Type QObjectDelegate::type() const
1169{
1170 return QtObject;
1171}
1172
1173bool QObjectDelegate::getOwnPropertySlot(QScriptObject *object, JSC::ExecState *exec,
1174 const JSC::Identifier &propertyName,
1175 JSC::PropertySlot &slot)
1176{
1177 //Note: this has to be kept in sync with getOwnPropertyDescriptor
1178#ifndef QT_NO_PROPERTIES
1179 QByteArray name = QString(propertyName.ustring()).toLatin1();
1180 QObject *qobject = data->value;
1181 if (!qobject) {
1182 QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject")
1183 .arg(QString::fromLatin1(name));
1184 slot.setValue(JSC::throwError(exec, JSC::GeneralError, message));
1185 return true;
1186 }
1187
1188 const QMetaObject *meta = qobject->metaObject();
1189 {
1190 QHash<QByteArray, JSC::JSValue>::const_iterator it = data->cachedMembers.constFind(name);
1191 if (it != data->cachedMembers.constEnd()) {
1192 if (GeneratePropertyFunctions && (meta->indexOfProperty(name) != -1))
1193 slot.setGetterSlot(JSC::asObject(it.value()));
1194 else
1195 slot.setValue(it.value());
1196 return true;
1197 }
1198 }
1199
1200 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1201 QScriptEnginePrivate *eng = scriptEngineFromExec(exec);
1202 int index = -1;
1203 if (name.contains('(')) {
1204 QByteArray normalized = QMetaObject::normalizedSignature(name);
1205 if (-1 != (index = meta->indexOfMethod(normalized))) {
1206 QMetaMethod method = meta->method(index);
1207 if (hasMethodAccess(method, index, opt)) {
1208 if (!(opt & QScriptEngine::ExcludeSuperClassMethods)
1209 || (index >= meta->methodOffset())) {
1210 QtFunction *fun = new (exec)QtFunction(
1211 object, index, /*maybeOverloaded=*/false,
1212 &exec->globalData(), eng->originalGlobalObject()->functionStructure(),
1213 propertyName);
1214 slot.setValue(fun);
1215 data->cachedMembers.insert(name, fun);
1216 return true;
1217 }
1218 }
1219 }
1220 }
1221
1222 index = meta->indexOfProperty(name);
1223 if (index != -1) {
1224 QMetaProperty prop = meta->property(index);
1225 if (prop.isScriptable()) {
1226 if (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1227 || (index >= meta->propertyOffset())) {
1228 if (GeneratePropertyFunctions) {
1229 QtPropertyFunction *fun = new (exec)QtPropertyFunction(
1230 meta, index, &exec->globalData(),
1231 eng->originalGlobalObject()->functionStructure(),
1232 propertyName);
1233 data->cachedMembers.insert(name, fun);
1234 slot.setGetterSlot(fun);
1235 } else {
1236 JSC::JSValue val;
1237 if (!prop.isValid())
1238 val = JSC::jsUndefined();
1239 else
1240 val = eng->jscValueFromVariant(prop.read(qobject));
1241 slot.setValue(val);
1242 }
1243 return true;
1244 }
1245 }
1246 }
1247
1248 index = qobject->dynamicPropertyNames().indexOf(name);
1249 if (index != -1) {
1250 JSC::JSValue val = eng->jscValueFromVariant(qobject->property(name));
1251 slot.setValue(val);
1252 return true;
1253 }
1254
1255 const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods)
1256 ? meta->methodOffset() : 0;
1257 for (index = meta->methodCount() - 1; index >= offset; --index) {
1258 QMetaMethod method = meta->method(index);
1259 if (hasMethodAccess(method, index, opt)
1260 && (methodName(method) == name)) {
1261 QtFunction *fun = new (exec)QtFunction(
1262 object, index, /*maybeOverloaded=*/true,
1263 &exec->globalData(), eng->originalGlobalObject()->functionStructure(),
1264 propertyName);
1265 slot.setValue(fun);
1266 data->cachedMembers.insert(name, fun);
1267 return true;
1268 }
1269 }
1270
1271 if (!(opt & QScriptEngine::ExcludeChildObjects)) {
1272 QList<QObject*> children = qobject->children();
1273 for (index = 0; index < children.count(); ++index) {
1274 QObject *child = children.at(index);
1275 if (child->objectName() == QString(propertyName.ustring())) {
1276 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
1277 QScriptValue tmp = QScriptEnginePrivate::get(eng)->newQObject(child, QScriptEngine::QtOwnership, opt);
1278 slot.setValue(eng->scriptValueToJSCValue(tmp));
1279 return true;
1280 }
1281 }
1282 }
1283
1284 return QScriptObjectDelegate::getOwnPropertySlot(object, exec, propertyName, slot);
1285#else //QT_NO_PROPERTIES
1286 return false;
1287#endif //QT_NO_PROPERTIES
1288}
1289
1290
1291bool QObjectDelegate::getOwnPropertyDescriptor(QScriptObject *object, JSC::ExecState *exec,
1292 const JSC::Identifier &propertyName,
1293 JSC::PropertyDescriptor &descriptor)
1294{
1295 //Note: this has to be kept in sync with getOwnPropertySlot abd getPropertyAttributes
1296#ifndef QT_NO_PROPERTIES
1297 QByteArray name = QString(propertyName.ustring()).toLatin1();
1298 QObject *qobject = data->value;
1299 if (!qobject) {
1300 QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject")
1301 .arg(QString::fromLatin1(name));
1302 descriptor.setValue(JSC::throwError(exec, JSC::GeneralError, message));
1303 return true;
1304 }
1305
1306 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1307
1308 const QMetaObject *meta = qobject->metaObject();
1309 {
1310 QHash<QByteArray, JSC::JSValue>::const_iterator it = data->cachedMembers.constFind(name);
1311 if (it != data->cachedMembers.constEnd()) {
1312 int index;
1313 if (GeneratePropertyFunctions && ((index = meta->indexOfProperty(name)) != -1)) {
1314 QMetaProperty prop = meta->property(index);
1315 descriptor.setAccessorDescriptor(it.value(), it.value(), flagsForMetaProperty(prop));
1316 if (!prop.isWritable())
1317 descriptor.setWritable(false);
1318 } else {
1319 unsigned attributes = QObjectMemberAttribute;
1320 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1321 attributes |= JSC::DontEnum;
1322 descriptor.setDescriptor(it.value(), attributes);
1323 }
1324 return true;
1325 }
1326 }
1327
1328 QScriptEnginePrivate *eng = scriptEngineFromExec(exec);
1329 int index = -1;
1330 if (name.contains('(')) {
1331 QByteArray normalized = QMetaObject::normalizedSignature(name);
1332 if (-1 != (index = meta->indexOfMethod(normalized))) {
1333 QMetaMethod method = meta->method(index);
1334 if (hasMethodAccess(method, index, opt)) {
1335 if (!(opt & QScriptEngine::ExcludeSuperClassMethods)
1336 || (index >= meta->methodOffset())) {
1337 QtFunction *fun = new (exec)QtFunction(
1338 object, index, /*maybeOverloaded=*/false,
1339 &exec->globalData(), eng->originalGlobalObject()->functionStructure(),
1340 propertyName);
1341 data->cachedMembers.insert(name, fun);
1342 unsigned attributes = QObjectMemberAttribute;
1343 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1344 attributes |= JSC::DontEnum;
1345 descriptor.setDescriptor(fun, attributes);
1346 return true;
1347 }
1348 }
1349 }
1350 }
1351
1352 index = meta->indexOfProperty(name);
1353 if (index != -1) {
1354 QMetaProperty prop = meta->property(index);
1355 if (prop.isScriptable()) {
1356 if (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1357 || (index >= meta->propertyOffset())) {
1358 unsigned attributes = flagsForMetaProperty(prop);
1359 if (GeneratePropertyFunctions) {
1360 QtPropertyFunction *fun = new (exec)QtPropertyFunction(
1361 meta, index, &exec->globalData(),
1362 eng->originalGlobalObject()->functionStructure(),
1363 propertyName);
1364 data->cachedMembers.insert(name, fun);
1365 descriptor.setAccessorDescriptor(fun, fun, attributes);
1366 if (attributes & JSC::ReadOnly)
1367 descriptor.setWritable(false);
1368 } else {
1369 JSC::JSValue val;
1370 if (!prop.isValid())
1371 val = JSC::jsUndefined();
1372 else
1373 val = eng->jscValueFromVariant(prop.read(qobject));
1374 descriptor.setDescriptor(val, attributes);
1375 }
1376 return true;
1377 }
1378 }
1379 }
1380
1381 index = qobject->dynamicPropertyNames().indexOf(name);
1382 if (index != -1) {
1383 JSC::JSValue val = eng->jscValueFromVariant(qobject->property(name));
1384 descriptor.setDescriptor(val, QObjectMemberAttribute);
1385 return true;
1386 }
1387
1388 const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods)
1389 ? meta->methodOffset() : 0;
1390 for (index = meta->methodCount() - 1; index >= offset; --index) {
1391 QMetaMethod method = meta->method(index);
1392 if (hasMethodAccess(method, index, opt)
1393 && (methodName(method) == name)) {
1394 QtFunction *fun = new (exec)QtFunction(
1395 object, index, /*maybeOverloaded=*/true,
1396 &exec->globalData(), eng->originalGlobalObject()->functionStructure(),
1397 propertyName);
1398 unsigned attributes = QObjectMemberAttribute;
1399 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1400 attributes |= JSC::DontEnum;
1401 descriptor.setDescriptor(fun, attributes);
1402 data->cachedMembers.insert(name, fun);
1403 return true;
1404 }
1405 }
1406
1407 if (!(opt & QScriptEngine::ExcludeChildObjects)) {
1408 QList<QObject*> children = qobject->children();
1409 for (index = 0; index < children.count(); ++index) {
1410 QObject *child = children.at(index);
1411 if (child->objectName() == QString(propertyName.ustring())) {
1412 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
1413 QScriptValue tmp = QScriptEnginePrivate::get(eng)->newQObject(child, QScriptEngine::QtOwnership, opt);
1414 descriptor.setDescriptor(eng->scriptValueToJSCValue(tmp), JSC::ReadOnly | JSC::DontDelete | JSC::DontEnum);
1415 return true;
1416 }
1417 }
1418 }
1419
1420 return QScriptObjectDelegate::getOwnPropertyDescriptor(object, exec, propertyName, descriptor);
1421#else //QT_NO_PROPERTIES
1422 return false;
1423#endif //QT_NO_PROPERTIES
1424}
1425
1426void QObjectDelegate::put(QScriptObject *object, JSC::ExecState* exec,
1427 const JSC::Identifier& propertyName,
1428 JSC::JSValue value, JSC::PutPropertySlot &slot)
1429{
1430#ifndef QT_NO_PROPERTIES
1431 QByteArray name = ((QString)propertyName.ustring()).toLatin1();
1432 QObject *qobject = data->value;
1433 if (!qobject) {
1434 QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject")
1435 .arg(QString::fromLatin1(name));
1436 JSC::throwError(exec, JSC::GeneralError, message);
1437 return;
1438 }
1439
1440 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1441 const QMetaObject *meta = qobject->metaObject();
1442 QScriptEnginePrivate *eng = scriptEngineFromExec(exec);
1443 int index = -1;
1444 if (name.contains('(')) {
1445 QByteArray normalized = QMetaObject::normalizedSignature(name);
1446 if (-1 != (index = meta->indexOfMethod(normalized))) {
1447 QMetaMethod method = meta->method(index);
1448 if (hasMethodAccess(method, index, opt)) {
1449 if (!(opt & QScriptEngine::ExcludeSuperClassMethods)
1450 || (index >= meta->methodOffset())) {
1451 data->cachedMembers.insert(name, value);
1452 return;
1453 }
1454 }
1455 }
1456 }
1457
1458 index = meta->indexOfProperty(name);
1459 if (index != -1) {
1460 QMetaProperty prop = meta->property(index);
1461 if (prop.isScriptable()) {
1462 if (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1463 || (index >= meta->propertyOffset())) {
1464 if (GeneratePropertyFunctions) {
1465 // ### ideally JSC would do this for us already, i.e. find out
1466 // that the property is a setter and call the setter.
1467 // Maybe QtPropertyFunction needs to inherit JSC::GetterSetter.
1468 JSC::JSValue fun;
1469 QHash<QByteArray, JSC::JSValue>::const_iterator it;
1470 it = data->cachedMembers.constFind(name);
1471 if (it != data->cachedMembers.constEnd()) {
1472 fun = it.value();
1473 } else {
1474 fun = new (exec)QtPropertyFunction(
1475 meta, index, &exec->globalData(),
1476 eng->originalGlobalObject()->functionStructure(),
1477 propertyName);
1478 data->cachedMembers.insert(name, fun);
1479 }
1480 JSC::CallData callData;
1481 JSC::CallType callType = fun.getCallData(callData);
1482 JSC::JSValue argv[1] = { value };
1483 JSC::ArgList args(argv, 1);
1484 (void)JSC::call(exec, fun, callType, callData, object, args);
1485 } else {
1486 QVariant v;
1487 if (prop.isEnumType() && value.isString()
1488 && !eng->hasDemarshalFunction(prop.userType())) {
1489 // give QMetaProperty::write() a chance to convert from
1490 // string to enum value
1491 v = (QString)value.toString(exec);
1492 } else {
1493 v = eng->jscValueToVariant(value, prop.userType());
1494 }
1495 (void)prop.write(qobject, v);
1496 }
1497 return;
1498 }
1499 }
1500 }
1501
1502 const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods)
1503 ? meta->methodOffset() : 0;
1504 for (index = meta->methodCount() - 1; index >= offset; --index) {
1505 QMetaMethod method = meta->method(index);
1506 if (hasMethodAccess(method, index, opt)
1507 && (methodName(method) == name)) {
1508 data->cachedMembers.insert(name, value);
1509 return;
1510 }
1511 }
1512
1513 index = qobject->dynamicPropertyNames().indexOf(name);
1514 if ((index != -1) || (opt & QScriptEngine::AutoCreateDynamicProperties)) {
1515 QVariant v = eng->scriptValueFromJSCValue(value).toVariant();
1516 (void)qobject->setProperty(name, v);
1517 return;
1518 }
1519
1520 QScriptObjectDelegate::put(object, exec, propertyName, value, slot);
1521#endif //QT_NO_PROPERTIES
1522}
1523
1524bool QObjectDelegate::deleteProperty(QScriptObject *object, JSC::ExecState *exec,
1525 const JSC::Identifier& propertyName,
1526 bool checkDontDelete)
1527{
1528#ifndef QT_NO_PROPERTIES
1529 QByteArray name = ((QString)propertyName.ustring()).toLatin1();
1530 QObject *qobject = data->value;
1531 if (!qobject) {
1532 QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject")
1533 .arg(QString::fromLatin1(name));
1534 JSC::throwError(exec, JSC::GeneralError, message);
1535 return false;
1536 }
1537
1538 const QMetaObject *meta = qobject->metaObject();
1539 {
1540 QHash<QByteArray, JSC::JSValue>::iterator it = data->cachedMembers.find(name);
1541 if (it != data->cachedMembers.end()) {
1542 if (GeneratePropertyFunctions && (meta->indexOfProperty(name) != -1))
1543 return false;
1544 data->cachedMembers.erase(it);
1545 return true;
1546 }
1547 }
1548
1549 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1550 int index = meta->indexOfProperty(name);
1551 if (index != -1) {
1552 QMetaProperty prop = meta->property(index);
1553 if (prop.isScriptable() &&
1554 (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1555 || (index >= meta->propertyOffset()))) {
1556 return false;
1557 }
1558 }
1559
1560 index = qobject->dynamicPropertyNames().indexOf(name);
1561 if (index != -1) {
1562 (void)qobject->setProperty(name, QVariant());
1563 return true;
1564 }
1565
1566 return QScriptObjectDelegate::deleteProperty(object, exec, propertyName, checkDontDelete);
1567#else //QT_NO_PROPERTIES
1568 return false;
1569#endif //QT_NO_PROPERTIES
1570}
1571
1572bool QObjectDelegate::getPropertyAttributes(const QScriptObject *object,
1573 JSC::ExecState *exec,
1574 const JSC::Identifier &propertyName,
1575 unsigned &attributes) const
1576{
1577#ifndef QT_NO_PROPERTIES
1578 //Note: this has to be kept in sync with getOwnPropertyDescriptor and getOwnPropertySlot
1579 QByteArray name = ((QString)propertyName.ustring()).toLatin1();
1580 QObject *qobject = data->value;
1581 if (!qobject)
1582 return false;
1583
1584 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1585 const QMetaObject *meta = qobject->metaObject();
1586 int index = -1;
1587 if (name.contains('(')) {
1588 QByteArray normalized = QMetaObject::normalizedSignature(name);
1589 if (-1 != (index = meta->indexOfMethod(normalized))) {
1590 QMetaMethod method = meta->method(index);
1591 if (hasMethodAccess(method, index, opt)) {
1592 if (!(opt & QScriptEngine::ExcludeSuperClassMethods)
1593 || (index >= meta->methodOffset())) {
1594 attributes = QObjectMemberAttribute;
1595 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1596 attributes |= JSC::DontEnum;
1597 return true;
1598 }
1599 }
1600 }
1601 }
1602
1603 index = meta->indexOfProperty(name);
1604 if (index != -1) {
1605 QMetaProperty prop = meta->property(index);
1606 if (prop.isScriptable()) {
1607 if (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1608 || (index >= meta->propertyOffset())) {
1609 attributes = flagsForMetaProperty(prop);
1610 return true;
1611 }
1612 }
1613 }
1614
1615 index = qobject->dynamicPropertyNames().indexOf(name);
1616 if (index != -1) {
1617 attributes = QObjectMemberAttribute;
1618 return true;
1619 }
1620
1621 const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods)
1622 ? meta->methodOffset() : 0;
1623 for (index = meta->methodCount() - 1; index >= offset; --index) {
1624 QMetaMethod method = meta->method(index);
1625 if (hasMethodAccess(method, index, opt)
1626 && (methodName(method) == name)) {
1627 attributes = QObjectMemberAttribute;
1628 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1629 attributes |= JSC::DontEnum;
1630 return true;
1631 }
1632 }
1633
1634 if (!(opt & QScriptEngine::ExcludeChildObjects)) {
1635 QList<QObject*> children = qobject->children();
1636 for (index = 0; index < children.count(); ++index) {
1637 QObject *child = children.at(index);
1638 if (child->objectName() == (QString)(propertyName.ustring())) {
1639 attributes = JSC::ReadOnly | JSC::DontDelete | JSC::DontEnum;
1640 return true;
1641 }
1642 }
1643 }
1644
1645 return QScriptObjectDelegate::getPropertyAttributes(object, exec, propertyName, attributes);
1646#else //QT_NO_PROPERTIES
1647 return false;
1648#endif //QT_NO_PROPERTIES
1649}
1650
1651void QObjectDelegate::getOwnPropertyNames(QScriptObject *object, JSC::ExecState *exec,
1652 JSC::PropertyNameArray &propertyNames,
1653 bool includeNonEnumerable)
1654{
1655#ifndef QT_NO_PROPERTIES
1656 QObject *qobject = data->value;
1657 if (!qobject) {
1658 QString message = QString::fromLatin1("cannot get property names of deleted QObject");
1659 JSC::throwError(exec, JSC::GeneralError, message);
1660 return;
1661 }
1662
1663 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1664 const QMetaObject *meta = qobject->metaObject();
1665 {
1666 int i = (opt & QScriptEngine::ExcludeSuperClassProperties)
1667 ? meta->propertyOffset() : 0;
1668 for ( ; i < meta->propertyCount(); ++i) {
1669 QMetaProperty prop = meta->property(i);
1670 if (isEnumerableMetaProperty(prop, meta, i)) {
1671 QString name = QString::fromLatin1(prop.name());
1672 propertyNames.add(JSC::Identifier(exec, name));
1673 }
1674 }
1675 }
1676
1677 {
1678 QList<QByteArray> dpNames = qobject->dynamicPropertyNames();
1679 for (int i = 0; i < dpNames.size(); ++i) {
1680 QString name = QString::fromLatin1(dpNames.at(i));
1681 propertyNames.add(JSC::Identifier(exec, name));
1682 }
1683 }
1684
1685 if (!(opt & QScriptEngine::SkipMethodsInEnumeration)) {
1686 int i = (opt & QScriptEngine::ExcludeSuperClassMethods)
1687 ? meta->methodOffset() : 0;
1688 for ( ; i < meta->methodCount(); ++i) {
1689 QMetaMethod method = meta->method(i);
1690 if (hasMethodAccess(method, i, opt)) {
1691 QMetaMethod method = meta->method(i);
1692 QString sig = QString::fromLatin1(method.signature());
1693 propertyNames.add(JSC::Identifier(exec, sig));
1694 }
1695 }
1696 }
1697
1698 QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, includeNonEnumerable);
1699#endif //QT_NO_PROPERTIES
1700}
1701
1702void QObjectDelegate::markChildren(QScriptObject *object, JSC::MarkStack& markStack)
1703{
1704 QHash<QByteArray, JSC::JSValue>::const_iterator it;
1705 for (it = data->cachedMembers.constBegin(); it != data->cachedMembers.constEnd(); ++it) {
1706 JSC::JSValue val = it.value();
1707 if (val)
1708 markStack.append(val);
1709 }
1710
1711 QScriptObjectDelegate::markChildren(object, markStack);
1712}
1713
1714bool QObjectDelegate::compareToObject(QScriptObject *, JSC::ExecState *exec, JSC::JSObject *o2)
1715{
1716 if (!o2->inherits(&QScriptObject::info))
1717 return false;
1718 QScriptObject *object = static_cast<QScriptObject*>(o2);
1719 QScriptObjectDelegate *delegate = object->delegate();
1720 if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject))
1721 return false;
1722 return value() == static_cast<QObjectDelegate *>(delegate)->value();
1723}
1724
1725static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncFindChild(JSC::ExecState *exec, JSC::JSObject*,
1726 JSC::JSValue thisValue, const JSC::ArgList &args)
1727{
1728 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
1729 thisValue = engine->toUsableValue(thisValue);
1730 if (!thisValue.inherits(&QScriptObject::info))
1731 return throwError(exec, JSC::TypeError, "this object is not a QObject");
1732 QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(thisValue));
1733 QScriptObjectDelegate *delegate = scriptObject->delegate();
1734 if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject))
1735 return throwError(exec, JSC::TypeError, "this object is not a QObject");
1736 QObject *obj = static_cast<QObjectDelegate*>(delegate)->value();
1737 QString name;
1738 if (args.size() != 0)
1739 name = args.at(0).toString(exec);
1740 QObject *child = qFindChild<QObject*>(obj, name);
1741 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
1742 return engine->newQObject(child, QScriptEngine::QtOwnership, opt);
1743}
1744
1745static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncFindChildren(JSC::ExecState *exec, JSC::JSObject*,
1746 JSC::JSValue thisValue, const JSC::ArgList &args)
1747{
1748 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
1749 thisValue = engine->toUsableValue(thisValue);
1750 // extract the QObject
1751 if (!thisValue.inherits(&QScriptObject::info))
1752 return throwError(exec, JSC::TypeError, "this object is not a QObject");
1753 QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(thisValue));
1754 QScriptObjectDelegate *delegate = scriptObject->delegate();
1755 if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject))
1756 return throwError(exec, JSC::TypeError, "this object is not a QObject");
1757 const QObject *const obj = static_cast<QObjectDelegate*>(delegate)->value();
1758
1759 // find the children
1760 QList<QObject *> children;
1761 if (args.size() != 0) {
1762 const JSC::JSValue arg = args.at(0);
1763 if (arg.inherits(&JSC::RegExpObject::info)) {
1764 const QObjectList allChildren= obj->children();
1765
1766 JSC::RegExpObject *const regexp = JSC::asRegExpObject(arg);
1767
1768 const int allChildrenCount = allChildren.size();
1769 for (int i = 0; i < allChildrenCount; ++i) {
1770 QObject *const child = allChildren.at(i);
1771 const JSC::UString childName = child->objectName();
1772 JSC::RegExpConstructor* regExpConstructor = engine->originalGlobalObject()->regExpConstructor();
1773 int position;
1774 int length;
1775 regExpConstructor->performMatch(regexp->regExp(), childName, 0, position, length);
1776 if (position >= 0)
1777 children.append(child);
1778 }
1779 } else {
1780 const QString name(args.at(0).toString(exec));
1781 children = qFindChildren<QObject*>(obj, name);
1782 }
1783 } else {
1784 children = qFindChildren<QObject*>(obj, QString());
1785 }
1786 // create the result array with the children
1787 const int length = children.size();
1788 JSC::JSArray *const result = JSC::constructEmptyArray(exec, length);
1789
1790 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
1791 for (int i = 0; i < length; ++i) {
1792 QObject *const child = children.at(i);
1793 result->put(exec, i, engine->newQObject(child, QScriptEngine::QtOwnership, opt));
1794 }
1795 return JSC::JSValue(result);
1796}
1797
1798static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncToString(JSC::ExecState *exec, JSC::JSObject*,
1799 JSC::JSValue thisValue, const JSC::ArgList&)
1800{
1801 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
1802 thisValue = engine->toUsableValue(thisValue);
1803 if (!thisValue.inherits(&QScriptObject::info))
1804 return JSC::jsUndefined();
1805 QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(thisValue));
1806 QScriptObjectDelegate *delegate = scriptObject->delegate();
1807 if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject))
1808 return JSC::jsUndefined();
1809 QObject *obj = static_cast<QObjectDelegate*>(delegate)->value();
1810 const QMetaObject *meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
1811 QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
1812 QString str = QString::fromUtf8("%0(name = \"%1\")")
1813 .arg(QLatin1String(meta->className())).arg(name);
1814 return JSC::jsString(exec, str);
1815}
1816
1817QObjectPrototype::QObjectPrototype(JSC::ExecState* exec, WTF::PassRefPtr<JSC::Structure> structure,
1818 JSC::Structure* prototypeFunctionStructure)
1819 : QScriptObject(structure)
1820{
1821 setDelegate(new QObjectDelegate(new QObjectPrototypeObject(), QScriptEngine::AutoOwnership,
1822 QScriptEngine::ExcludeSuperClassMethods
1823 | QScriptEngine::ExcludeSuperClassProperties
1824 | QScriptEngine::ExcludeChildObjects));
1825
1826 putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/0, exec->propertyNames().toString, qobjectProtoFuncToString), JSC::DontEnum);
1827 putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChild"), qobjectProtoFuncFindChild), JSC::DontEnum);
1828 putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChildren"), qobjectProtoFuncFindChildren), JSC::DontEnum);
1829 this->structure()->setHasGetterSetterProperties(true);
1830}
1831
1832const JSC::ClassInfo QMetaObjectWrapperObject::info = { "QMetaObject", 0, 0, 0 };
1833
1834QMetaObjectWrapperObject::QMetaObjectWrapperObject(
1835 JSC::ExecState *exec, const QMetaObject *metaObject, JSC::JSValue ctor,
1836 WTF::PassRefPtr<JSC::Structure> sid)
1837 : JSC::JSObject(sid),
1838 data(new Data(metaObject, ctor))
1839{
1840 if (!ctor)
1841 data->prototype = new (exec)JSC::JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
1842}
1843
1844QMetaObjectWrapperObject::~QMetaObjectWrapperObject()
1845{
1846 delete data;
1847}
1848
1849bool QMetaObjectWrapperObject::getOwnPropertySlot(
1850 JSC::ExecState *exec, const JSC::Identifier& propertyName,
1851 JSC::PropertySlot &slot)
1852{
1853 const QMetaObject *meta = data->value;
1854 if (!meta)
1855 return false;
1856
1857 if (propertyName == exec->propertyNames().prototype) {
1858 if (data->ctor)
1859 slot.setValue(data->ctor.get(exec, propertyName));
1860 else
1861 slot.setValue(data->prototype);
1862 return true;
1863 }
1864
1865 QByteArray name = QString(propertyName.ustring()).toLatin1();
1866
1867 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1868 QMetaEnum e = meta->enumerator(i);
1869 for (int j = 0; j < e.keyCount(); ++j) {
1870 const char *key = e.key(j);
1871 if (!qstrcmp(key, name.constData())) {
1872 slot.setValue(JSC::JSValue(exec, e.value(j)));
1873 return true;
1874 }
1875 }
1876 }
1877
1878 return JSC::JSObject::getOwnPropertySlot(exec, propertyName, slot);
1879}
1880
1881void QMetaObjectWrapperObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName,
1882 JSC::JSValue value, JSC::PutPropertySlot &slot)
1883{
1884 if (propertyName == exec->propertyNames().prototype) {
1885 if (data->ctor)
1886 data->ctor.put(exec, propertyName, value, slot);
1887 else
1888 data->prototype = value;
1889 return;
1890 }
1891 const QMetaObject *meta = data->value;
1892 if (meta) {
1893 QByteArray name = QString(propertyName.ustring()).toLatin1();
1894 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1895 QMetaEnum e = meta->enumerator(i);
1896 for (int j = 0; j < e.keyCount(); ++j) {
1897 if (!qstrcmp(e.key(j), name.constData()))
1898 return;
1899 }
1900 }
1901 }
1902 JSC::JSObject::put(exec, propertyName, value, slot);
1903}
1904
1905bool QMetaObjectWrapperObject::deleteProperty(
1906 JSC::ExecState *exec, const JSC::Identifier& propertyName,
1907 bool checkDontDelete)
1908{
1909 if (propertyName == exec->propertyNames().prototype)
1910 return false;
1911 const QMetaObject *meta = data->value;
1912 if (meta) {
1913 QByteArray name = QString(propertyName.ustring()).toLatin1();
1914 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1915 QMetaEnum e = meta->enumerator(i);
1916 for (int j = 0; j < e.keyCount(); ++j) {
1917 if (!qstrcmp(e.key(j), name.constData()))
1918 return false;
1919 }
1920 }
1921 }
1922 return JSC::JSObject::deleteProperty(exec, propertyName, checkDontDelete);
1923}
1924
1925bool QMetaObjectWrapperObject::getPropertyAttributes(JSC::ExecState *exec,
1926 const JSC::Identifier &propertyName,
1927 unsigned &attributes) const
1928{
1929 if (propertyName == exec->propertyNames().prototype) {
1930 attributes = JSC::DontDelete;
1931 return true;
1932 }
1933 const QMetaObject *meta = data->value;
1934 if (meta) {
1935 QByteArray name = QString(propertyName.ustring()).toLatin1();
1936 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1937 QMetaEnum e = meta->enumerator(i);
1938 for (int j = 0; j < e.keyCount(); ++j) {
1939 if (!qstrcmp(e.key(j), name.constData())) {
1940 attributes = JSC::ReadOnly | JSC::DontDelete;
1941 return true;
1942 }
1943 }
1944 }
1945 }
1946 return JSC::JSObject::getPropertyAttributes(exec, propertyName, attributes);
1947}
1948
1949void QMetaObjectWrapperObject::getOwnPropertyNames(JSC::ExecState *exec,
1950 JSC::PropertyNameArray &propertyNames,
1951 bool includeNonEnumerable)
1952{
1953 const QMetaObject *meta = data->value;
1954 if (!meta)
1955 return;
1956 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1957 QMetaEnum e = meta->enumerator(i);
1958 for (int j = 0; j < e.keyCount(); ++j)
1959 propertyNames.add(JSC::Identifier(exec, e.key(j)));
1960 }
1961 JSC::JSObject::getOwnPropertyNames(exec, propertyNames, includeNonEnumerable);
1962}
1963
1964void QMetaObjectWrapperObject::markChildren(JSC::MarkStack& markStack)
1965{
1966 if (data->ctor)
1967 markStack.append(data->ctor);
1968 if (data->prototype)
1969 markStack.append(data->prototype);
1970 JSC::JSObject::markChildren(markStack);
1971}
1972
1973JSC::CallType QMetaObjectWrapperObject::getCallData(JSC::CallData& callData)
1974{
1975 callData.native.function = call;
1976 return JSC::CallTypeHost;
1977}
1978
1979JSC::ConstructType QMetaObjectWrapperObject::getConstructData(JSC::ConstructData& constructData)
1980{
1981 constructData.native.function = construct;
1982 return JSC::ConstructTypeHost;
1983}
1984
1985JSC::JSValue JSC_HOST_CALL QMetaObjectWrapperObject::call(
1986 JSC::ExecState *exec, JSC::JSObject *callee,
1987 JSC::JSValue thisValue, const JSC::ArgList &args)
1988{
1989 QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec);
1990 thisValue = eng_p->toUsableValue(thisValue);
1991 if (!callee->inherits(&QMetaObjectWrapperObject::info))
1992 return throwError(exec, JSC::TypeError, "callee is not a QMetaObject");
1993 QMetaObjectWrapperObject *self = static_cast<QMetaObjectWrapperObject*>(callee);
1994 JSC::ExecState *previousFrame = eng_p->currentFrame;
1995 eng_p->pushContext(exec, thisValue, args, callee);
1996 JSC::JSValue result = self->execute(eng_p->currentFrame, args);
1997 eng_p->popContext();
1998 eng_p->currentFrame = previousFrame;
1999 return result;
2000}
2001
2002JSC::JSObject* QMetaObjectWrapperObject::construct(JSC::ExecState *exec, JSC::JSObject *callee, const JSC::ArgList &args)
2003{
2004 QMetaObjectWrapperObject *self = static_cast<QMetaObjectWrapperObject*>(callee);
2005 QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec);
2006 JSC::ExecState *previousFrame = eng_p->currentFrame;
2007 eng_p->pushContext(exec, JSC::JSValue(), args, callee, true);
2008 JSC::JSValue result = self->execute(eng_p->currentFrame, args);
2009 eng_p->popContext();
2010 eng_p->currentFrame = previousFrame;
2011 if (!result || !result.isObject())
2012 return 0;
2013 return JSC::asObject(result);
2014}
2015
2016JSC::JSValue QMetaObjectWrapperObject::execute(JSC::ExecState *exec,
2017 const JSC::ArgList &args)
2018{
2019 if (data->ctor) {
2020 QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec);
2021 QScriptContext *ctx = eng_p->contextForFrame(exec);
2022 JSC::CallData callData;
2023 JSC::CallType callType = data->ctor.getCallData(callData);
2024 Q_UNUSED(callType);
2025 Q_ASSERT_X(callType == JSC::CallTypeHost, Q_FUNC_INFO, "script constructors not supported");
2026 if (data->ctor.inherits(&FunctionWithArgWrapper::info)) {
2027 FunctionWithArgWrapper *wrapper = static_cast<FunctionWithArgWrapper*>(JSC::asObject(data->ctor));
2028 QScriptValue result = wrapper->function()(ctx, QScriptEnginePrivate::get(eng_p), wrapper->arg());
2029 return eng_p->scriptValueToJSCValue(result);
2030 } else {
2031 Q_ASSERT(data->ctor.inherits(&FunctionWrapper::info));
2032 FunctionWrapper *wrapper = static_cast<FunctionWrapper*>(JSC::asObject(data->ctor));
2033 QScriptValue result = wrapper->function()(ctx, QScriptEnginePrivate::get(eng_p));
2034 return eng_p->scriptValueToJSCValue(result);
2035 }
2036 } else {
2037 const QMetaObject *meta = data->value;
2038 if (meta->constructorCount() > 0) {
2039 JSC::JSValue result = callQtMethod(exec, QMetaMethod::Constructor, /*thisQObject=*/0,
2040 args, meta, meta->constructorCount()-1, /*maybeOverloaded=*/true);
2041 if (!exec->hadException()) {
2042 Q_ASSERT(result && result.inherits(&QScriptObject::info));
2043 QScriptObject *object = static_cast<QScriptObject*>(JSC::asObject(result));
2044 QScript::QObjectDelegate *delegate = static_cast<QScript::QObjectDelegate*>(object->delegate());
2045 delegate->setOwnership(QScriptEngine::AutoOwnership);
2046 if (data->prototype)
2047 object->setPrototype(data->prototype);
2048 }
2049 return result;
2050 } else {
2051 QString message = QString::fromLatin1("no constructor for %0")
2052 .arg(QLatin1String(meta->className()));
2053 return JSC::throwError(exec, JSC::TypeError, message);
2054 }
2055 }
2056}
2057
2058struct StaticQtMetaObject : public QObject
2059{
2060 static const QMetaObject *get()
2061 { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
2062};
2063
2064static JSC::JSValue JSC_HOST_CALL qmetaobjectProtoFuncClassName(
2065 JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisValue, const JSC::ArgList&)
2066{
2067 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
2068 thisValue = engine->toUsableValue(thisValue);
2069 if (!thisValue.inherits(&QMetaObjectWrapperObject::info))
2070 return throwError(exec, JSC::TypeError, "this object is not a QMetaObject");
2071 const QMetaObject *meta = static_cast<QMetaObjectWrapperObject*>(JSC::asObject(thisValue))->value();
2072 return JSC::jsString(exec, meta->className());
2073}
2074
2075QMetaObjectPrototype::QMetaObjectPrototype(
2076 JSC::ExecState *exec, WTF::PassRefPtr<JSC::Structure> structure,
2077 JSC::Structure* prototypeFunctionStructure)
2078 : QMetaObjectWrapperObject(exec, StaticQtMetaObject::get(), /*ctor=*/JSC::JSValue(), structure)
2079{
2080 putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/0, JSC::Identifier(exec, "className"), qmetaobjectProtoFuncClassName), JSC::DontEnum);
2081}
2082
2083static const uint qt_meta_data_QObjectConnectionManager[] = {
2084
2085 // content:
2086 1, // revision
2087 0, // classname
2088 0, 0, // classinfo
2089 1, 10, // methods
2090 0, 0, // properties
2091 0, 0, // enums/sets
2092
2093 // slots: signature, parameters, type, tag, flags
2094 35, 34, 34, 34, 0x0a,
2095
2096 0 // eod
2097};
2098
2099static const char qt_meta_stringdata_QObjectConnectionManager[] = {
2100 "QScript::QObjectConnectionManager\0\0execute()\0"
2101};
2102
2103const QMetaObject QObjectConnectionManager::staticMetaObject = {
2104 { &QObject::staticMetaObject, qt_meta_stringdata_QObjectConnectionManager,
2105 qt_meta_data_QObjectConnectionManager, 0 }
2106};
2107
2108const QMetaObject *QObjectConnectionManager::metaObject() const
2109{
2110 return &staticMetaObject;
2111}
2112
2113void *QObjectConnectionManager::qt_metacast(const char *_clname)
2114{
2115 if (!_clname) return 0;
2116 if (!strcmp(_clname, qt_meta_stringdata_QObjectConnectionManager))
2117 return static_cast<void*>(const_cast<QObjectConnectionManager*>(this));
2118 return QObject::qt_metacast(_clname);
2119}
2120
2121int QObjectConnectionManager::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
2122{
2123 _id = QObject::qt_metacall(_c, _id, _a);
2124 if (_id < 0)
2125 return _id;
2126 if (_c == QMetaObject::InvokeMetaMethod) {
2127 execute(_id, _a);
2128 _id -= slotCounter;
2129 }
2130 return _id;
2131}
2132
2133void QObjectConnectionManager::execute(int slotIndex, void **argv)
2134{
2135 JSC::JSValue receiver;
2136 JSC::JSValue slot;
2137 JSC::JSValue senderWrapper;
2138 int signalIndex = -1;
2139 for (int i = 0; i < connections.size(); ++i) {
2140 const QVector<QObjectConnection> &cs = connections.at(i);
2141 for (int j = 0; j < cs.size(); ++j) {
2142 const QObjectConnection &c = cs.at(j);
2143 if (c.slotIndex == slotIndex) {
2144 receiver = c.receiver;
2145 slot = c.slot;
2146 senderWrapper = c.senderWrapper;
2147 signalIndex = i;
2148 break;
2149 }
2150 }
2151 }
2152 Q_ASSERT(slot && slot.isObject());
2153
2154 if (engine->isCollecting()) {
2155 qWarning("QtScript: can't execute signal handler during GC");
2156 // we can't do a script function call during GC,
2157 // so we're forced to ignore this signal
2158 return;
2159 }
2160
2161#if 0
2162 QScriptFunction *fun = engine->convertToNativeFunction(slot);
2163 if (fun == 0) {
2164 // the signal handler has been GC'ed. This can only happen when
2165 // a QObject is owned by the engine, the engine is destroyed, and
2166 // there is a script function connected to the destroyed() signal
2167 Q_ASSERT(signalIndex <= 1); // destroyed(QObject*)
2168 return;
2169 }
2170#endif
2171
2172 const QMetaObject *meta = sender()->metaObject();
2173 const QMetaMethod method = meta->method(signalIndex);
2174
2175 QList<QByteArray> parameterTypes = method.parameterTypes();
2176 int argc = parameterTypes.count();
2177
2178 JSC::ExecState *exec = engine->currentFrame;
2179 QVarLengthArray<JSC::JSValue, 8> argsVector(argc);
2180 for (int i = 0; i < argc; ++i) {
2181 // ### optimize -- no need to convert via QScriptValue
2182 QScriptValue actual;
2183 void *arg = argv[i + 1];
2184 QByteArray typeName = parameterTypes.at(i);
2185 int argType = QMetaType::type(parameterTypes.at(i));
2186 if (!argType) {
2187 if (typeName == "QVariant") {
2188 actual = engine->scriptValueFromVariant(*reinterpret_cast<QVariant*>(arg));
2189 } else {
2190 qWarning("QScriptEngine: Unable to handle unregistered datatype '%s' "
2191 "when invoking handler of signal %s::%s",
2192 typeName.constData(), meta->className(), method.signature());
2193 actual = QScriptValue(QScriptValue::UndefinedValue);
2194 }
2195 } else {
2196 actual = engine->create(argType, arg);
2197 }
2198 argsVector[i] = engine->scriptValueToJSCValue(actual);
2199 }
2200 JSC::ArgList jscArgs(argsVector.data(), argsVector.size());
2201
2202 JSC::JSValue senderObject;
2203 if (senderWrapper && senderWrapper.inherits(&QScriptObject::info)) // ### check if it's actually a QObject wrapper
2204 senderObject = senderWrapper;
2205 else {
2206 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
2207 senderObject = engine->newQObject(sender(), QScriptEngine::QtOwnership, opt);
2208 }
2209
2210 JSC::JSValue thisObject;
2211 if (receiver && receiver.isObject())
2212 thisObject = receiver;
2213 else
2214 thisObject = engine->globalObject();
2215
2216 JSC::CallData callData;
2217 JSC::CallType callType = slot.getCallData(callData);
2218 if (exec->hadException())
2219 exec->clearException(); // ### otherwise JSC asserts
2220 JSC::call(exec, slot, callType, callData, thisObject, jscArgs);
2221
2222 if (exec->hadException()) {
2223 if (slot.inherits(&QtFunction::info) && !static_cast<QtFunction*>(JSC::asObject(slot))->qobject()) {
2224 // The function threw an error because the target QObject has been deleted.
2225 // The connections list is stale; remove the signal handler and ignore the exception.
2226 removeSignalHandler(sender(), signalIndex, receiver, slot);
2227 exec->clearException();
2228 } else {
2229 engine->emitSignalHandlerException();
2230 }
2231 }
2232}
2233
2234QObjectConnectionManager::QObjectConnectionManager(QScriptEnginePrivate *eng)
2235 : engine(eng), slotCounter(0)
2236{
2237}
2238
2239QObjectConnectionManager::~QObjectConnectionManager()
2240{
2241}
2242
2243void QObjectConnectionManager::mark(JSC::MarkStack& markStack)
2244{
2245 for (int i = 0; i < connections.size(); ++i) {
2246 QVector<QObjectConnection> &cs = connections[i];
2247 for (int j = 0; j < cs.size(); ++j)
2248 cs[j].mark(markStack);
2249 }
2250}
2251
2252bool QObjectConnectionManager::addSignalHandler(
2253 QObject *sender, int signalIndex, JSC::JSValue receiver,
2254 JSC::JSValue function, JSC::JSValue senderWrapper,
2255 Qt::ConnectionType type)
2256{
2257 if (connections.size() <= signalIndex)
2258 connections.resize(signalIndex+1);
2259 QVector<QObjectConnection> &cs = connections[signalIndex];
2260 int absSlotIndex = slotCounter + metaObject()->methodOffset();
2261 bool ok = QMetaObject::connect(sender, signalIndex, this, absSlotIndex, type);
2262 if (ok) {
2263 cs.append(QObjectConnection(slotCounter++, receiver, function, senderWrapper));
2264 QMetaMethod signal = sender->metaObject()->method(signalIndex);
2265 QByteArray signalString;
2266 signalString.append('2'); // signal code
2267 signalString.append(signal.signature());
2268 static_cast<QObjectNotifyCaller*>(sender)->callConnectNotify(signalString);
2269 }
2270 return ok;
2271}
2272
2273bool QObjectConnectionManager::removeSignalHandler(
2274 QObject *sender, int signalIndex,
2275 JSC::JSValue receiver, JSC::JSValue slot)
2276{
2277 if (connections.size() <= signalIndex)
2278 return false;
2279 QVector<QObjectConnection> &cs = connections[signalIndex];
2280 for (int i = 0; i < cs.size(); ++i) {
2281 const QObjectConnection &c = cs.at(i);
2282 if (c.hasTarget(receiver, slot)) {
2283 int absSlotIndex = c.slotIndex + metaObject()->methodOffset();
2284 bool ok = QMetaObject::disconnect(sender, signalIndex, this, absSlotIndex);
2285 if (ok) {
2286 cs.remove(i);
2287 QMetaMethod signal = sender->metaObject()->method(signalIndex);
2288 QByteArray signalString;
2289 signalString.append('2'); // signal code
2290 signalString.append(signal.signature());
2291 static_cast<QScript::QObjectNotifyCaller*>(sender)->callDisconnectNotify(signalString);
2292 }
2293 return ok;
2294 }
2295 }
2296 return false;
2297}
2298
2299QObjectData::QObjectData(QScriptEnginePrivate *eng)
2300 : engine(eng), connectionManager(0)
2301{
2302}
2303
2304QObjectData::~QObjectData()
2305{
2306 if (connectionManager) {
2307 delete connectionManager;
2308 connectionManager = 0;
2309 }
2310}
2311
2312void QObjectData::mark(JSC::MarkStack& markStack)
2313{
2314 if (connectionManager)
2315 connectionManager->mark(markStack);
2316 {
2317 QList<QScript::QObjectWrapperInfo>::iterator it;
2318 for (it = wrappers.begin(); it != wrappers.end(); ) {
2319 const QScript::QObjectWrapperInfo &info = *it;
2320 // ### don't mark if there are no other references.
2321 // we need something like isMarked()
2322 markStack.append(info.object);
2323 ++it;
2324 }
2325 }
2326}
2327
2328bool QObjectData::addSignalHandler(QObject *sender,
2329 int signalIndex,
2330 JSC::JSValue receiver,
2331 JSC::JSValue slot,
2332 JSC::JSValue senderWrapper,
2333 Qt::ConnectionType type)
2334{
2335 if (!connectionManager)
2336 connectionManager = new QObjectConnectionManager(engine);
2337 return connectionManager->addSignalHandler(
2338 sender, signalIndex, receiver, slot, senderWrapper, type);
2339}
2340
2341bool QObjectData::removeSignalHandler(QObject *sender,
2342 int signalIndex,
2343 JSC::JSValue receiver,
2344 JSC::JSValue slot)
2345{
2346 if (!connectionManager)
2347 return false;
2348 return connectionManager->removeSignalHandler(
2349 sender, signalIndex, receiver, slot);
2350}
2351
2352QScriptObject *QObjectData::findWrapper(QScriptEngine::ValueOwnership ownership,
2353 const QScriptEngine::QObjectWrapOptions &options) const
2354{
2355 for (int i = 0; i < wrappers.size(); ++i) {
2356 const QObjectWrapperInfo &info = wrappers.at(i);
2357 if ((info.ownership == ownership) && (info.options == options))
2358 return info.object;
2359 }
2360 return 0;
2361}
2362
2363void QObjectData::registerWrapper(QScriptObject *wrapper,
2364 QScriptEngine::ValueOwnership ownership,
2365 const QScriptEngine::QObjectWrapOptions &options)
2366{
2367 wrappers.append(QObjectWrapperInfo(wrapper, ownership, options));
2368}
2369
2370} // namespace QScript
2371
2372QT_END_NAMESPACE
2373
2374namespace JSC
2375{
2376 ASSERT_CLASS_FITS_IN_CELL(QScript::QtFunction);
2377}
2378
2379#include "moc_qscriptqobject_p.cpp"
2380
Note: See TracBrowser for help on using the repository browser.