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

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

trunk: Merged in qt 4.6.1 sources.

  • Property svn:eol-style set to native
File size: 88.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 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 Q_ASSERT_X(qobj != 0, "QtFunction::call", "handle the case when QObject has been deleted");
982 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
983
984 const QMetaObject *meta = qobj->metaObject();
985 QObject *thisQObject = 0;
986 thisValue = engine->toUsableValue(thisValue);
987 if (thisValue.inherits(&QScriptObject::info)) {
988 delegate = static_cast<QScriptObject*>(JSC::asObject(thisValue))->delegate();
989 if (delegate && (delegate->type() == QScriptObjectDelegate::QtObject))
990 thisQObject = static_cast<QScript::QObjectDelegate*>(delegate)->value();
991 }
992 if (!thisQObject)
993 thisQObject = qobj; // ### TypeError
994
995 if (!meta->cast(thisQObject)) {
996 // invoking a function in the prototype
997 thisQObject = qobj;
998 }
999
1000 return callQtMethod(exec, QMetaMethod::Method, thisQObject, scriptArgs,
1001 meta, data->initialIndex, data->maybeOverloaded);
1002}
1003
1004const JSC::ClassInfo QtFunction::info = { "QtFunction", &InternalFunction::info, 0, 0 };
1005
1006JSC::JSValue JSC_HOST_CALL QtFunction::call(JSC::ExecState *exec, JSC::JSObject *callee,
1007 JSC::JSValue thisValue, const JSC::ArgList &args)
1008{
1009 if (!callee->inherits(&QtFunction::info))
1010 return throwError(exec, JSC::TypeError, "callee is not a QtFunction object");
1011 QtFunction *qfun = static_cast<QtFunction*>(callee);
1012 QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec);
1013 JSC::ExecState *previousFrame = eng_p->currentFrame;
1014 eng_p->currentFrame = exec;
1015 eng_p->pushContext(exec, thisValue, args, callee);
1016 JSC::JSValue result = qfun->execute(eng_p->currentFrame, thisValue, args);
1017 eng_p->popContext();
1018 eng_p->currentFrame = previousFrame;
1019 return result;
1020}
1021
1022const JSC::ClassInfo QtPropertyFunction::info = { "QtPropertyFunction", &InternalFunction::info, 0, 0 };
1023
1024QtPropertyFunction::QtPropertyFunction(const QMetaObject *meta, int index,
1025 JSC::JSGlobalData *data,
1026 WTF::PassRefPtr<JSC::Structure> sid,
1027 const JSC::Identifier &ident)
1028 : JSC::InternalFunction(data, sid, ident),
1029 data(new Data(meta, index))
1030{
1031}
1032
1033QtPropertyFunction::~QtPropertyFunction()
1034{
1035 delete data;
1036}
1037
1038JSC::CallType QtPropertyFunction::getCallData(JSC::CallData &callData)
1039{
1040 callData.native.function = call;
1041 return JSC::CallTypeHost;
1042}
1043
1044JSC::JSValue JSC_HOST_CALL QtPropertyFunction::call(
1045 JSC::ExecState *exec, JSC::JSObject *callee,
1046 JSC::JSValue thisValue, const JSC::ArgList &args)
1047{
1048 if (!callee->inherits(&QtPropertyFunction::info))
1049 return throwError(exec, JSC::TypeError, "callee is not a QtPropertyFunction object");
1050 QtPropertyFunction *qfun = static_cast<QtPropertyFunction*>(callee);
1051 QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec);
1052 JSC::ExecState *previousFrame = eng_p->currentFrame;
1053 eng_p->currentFrame = exec;
1054 eng_p->pushContext(exec, thisValue, args, callee);
1055 JSC::JSValue result = qfun->execute(eng_p->currentFrame, thisValue, args);
1056 eng_p->popContext();
1057 eng_p->currentFrame = previousFrame;
1058 return result;
1059}
1060
1061JSC::JSValue QtPropertyFunction::execute(JSC::ExecState *exec,
1062 JSC::JSValue thisValue,
1063 const JSC::ArgList &args)
1064{
1065 JSC::JSValue result = JSC::jsUndefined();
1066
1067 // ### don't go via QScriptValue
1068 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
1069 thisValue = engine->toUsableValue(thisValue);
1070 QScriptValue object = engine->scriptValueFromJSCValue(thisValue);
1071 QObject *qobject = object.toQObject();
1072 while ((!qobject || (qobject->metaObject() != data->meta))
1073 && object.prototype().isObject()) {
1074 object = object.prototype();
1075 qobject = object.toQObject();
1076 }
1077 Q_ASSERT_X(qobject, Q_FUNC_INFO, "this-object must be a QObject");
1078
1079 QMetaProperty prop = data->meta->property(data->index);
1080 Q_ASSERT(prop.isScriptable());
1081 if (args.size() == 0) {
1082 // get
1083 if (prop.isValid()) {
1084 QScriptable *scriptable = scriptableFromQObject(qobject);
1085 QScriptEngine *oldEngine = 0;
1086 if (scriptable) {
1087 oldEngine = QScriptablePrivate::get(scriptable)->engine;
1088 QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine);
1089 }
1090
1091 QVariant v = prop.read(qobject);
1092
1093 if (scriptable)
1094 QScriptablePrivate::get(scriptable)->engine = oldEngine;
1095
1096 result = engine->jscValueFromVariant(v);
1097 }
1098 } else {
1099 // set
1100 JSC::JSValue arg = args.at(0);
1101 QVariant v;
1102 if (prop.isEnumType() && arg.isString()
1103 && !engine->hasDemarshalFunction(prop.userType())) {
1104 // give QMetaProperty::write() a chance to convert from
1105 // string to enum value
1106 v = (QString)arg.toString(exec);
1107 } else {
1108 // ### don't go via QScriptValue
1109 QScriptValue tmp = engine->scriptValueFromJSCValue(arg);
1110 v = variantFromValue(engine, prop.userType(), tmp);
1111 }
1112
1113 QScriptable *scriptable = scriptableFromQObject(qobject);
1114 QScriptEngine *oldEngine = 0;
1115 if (scriptable) {
1116 oldEngine = QScriptablePrivate::get(scriptable)->engine;
1117 QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine);
1118 }
1119
1120 prop.write(qobject, v);
1121
1122 if (scriptable)
1123 QScriptablePrivate::get(scriptable)->engine = oldEngine;
1124
1125 result = arg;
1126 }
1127 return result;
1128}
1129
1130const QMetaObject *QtPropertyFunction::metaObject() const
1131{
1132 return data->meta;
1133}
1134
1135int QtPropertyFunction::propertyIndex() const
1136{
1137 return data->index;
1138}
1139
1140
1141QObjectDelegate::QObjectDelegate(
1142 QObject *object, QScriptEngine::ValueOwnership ownership,
1143 const QScriptEngine::QObjectWrapOptions &options)
1144 : data(new Data(object, ownership, options))
1145{
1146}
1147
1148QObjectDelegate::~QObjectDelegate()
1149{
1150 switch (data->ownership) {
1151 case QScriptEngine::QtOwnership:
1152 break;
1153 case QScriptEngine::ScriptOwnership:
1154 if (data->value)
1155 delete data->value; // ### fixme
1156// eng->disposeQObject(value);
1157 break;
1158 case QScriptEngine::AutoOwnership:
1159 if (data->value && !data->value->parent())
1160 delete data->value; // ### fixme
1161// eng->disposeQObject(value);
1162 break;
1163 }
1164 delete data;
1165}
1166
1167QScriptObjectDelegate::Type QObjectDelegate::type() const
1168{
1169 return QtObject;
1170}
1171
1172bool QObjectDelegate::getOwnPropertySlot(QScriptObject *object, JSC::ExecState *exec,
1173 const JSC::Identifier &propertyName,
1174 JSC::PropertySlot &slot)
1175{
1176 //Note: this has to be kept in sync with getOwnPropertyDescriptor
1177#ifndef QT_NO_PROPERTIES
1178 QByteArray name = QString(propertyName.ustring()).toLatin1();
1179 QObject *qobject = data->value;
1180 if (!qobject) {
1181 QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject")
1182 .arg(QString::fromLatin1(name));
1183 slot.setValue(JSC::throwError(exec, JSC::GeneralError, message));
1184 return true;
1185 }
1186
1187 const QMetaObject *meta = qobject->metaObject();
1188 {
1189 QHash<QByteArray, JSC::JSValue>::const_iterator it = data->cachedMembers.constFind(name);
1190 if (it != data->cachedMembers.constEnd()) {
1191 if (GeneratePropertyFunctions && (meta->indexOfProperty(name) != -1))
1192 slot.setGetterSlot(JSC::asObject(it.value()));
1193 else
1194 slot.setValue(it.value());
1195 return true;
1196 }
1197 }
1198
1199 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1200 QScriptEnginePrivate *eng = scriptEngineFromExec(exec);
1201 int index = -1;
1202 if (name.contains('(')) {
1203 QByteArray normalized = QMetaObject::normalizedSignature(name);
1204 if (-1 != (index = meta->indexOfMethod(normalized))) {
1205 QMetaMethod method = meta->method(index);
1206 if (hasMethodAccess(method, index, opt)) {
1207 if (!(opt & QScriptEngine::ExcludeSuperClassMethods)
1208 || (index >= meta->methodOffset())) {
1209 QtFunction *fun = new (exec)QtFunction(
1210 object, index, /*maybeOverloaded=*/false,
1211 &exec->globalData(), eng->originalGlobalObject()->functionStructure(),
1212 propertyName);
1213 slot.setValue(fun);
1214 data->cachedMembers.insert(name, fun);
1215 return true;
1216 }
1217 }
1218 }
1219 }
1220
1221 index = meta->indexOfProperty(name);
1222 if (index != -1) {
1223 QMetaProperty prop = meta->property(index);
1224 if (prop.isScriptable()) {
1225 if (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1226 || (index >= meta->propertyOffset())) {
1227 if (GeneratePropertyFunctions) {
1228 QtPropertyFunction *fun = new (exec)QtPropertyFunction(
1229 meta, index, &exec->globalData(),
1230 eng->originalGlobalObject()->functionStructure(),
1231 propertyName);
1232 data->cachedMembers.insert(name, fun);
1233 slot.setGetterSlot(fun);
1234 } else {
1235 JSC::JSValue val;
1236 if (!prop.isValid())
1237 val = JSC::jsUndefined();
1238 else
1239 val = eng->jscValueFromVariant(prop.read(qobject));
1240 slot.setValue(val);
1241 }
1242 return true;
1243 }
1244 }
1245 }
1246
1247 index = qobject->dynamicPropertyNames().indexOf(name);
1248 if (index != -1) {
1249 JSC::JSValue val = eng->jscValueFromVariant(qobject->property(name));
1250 slot.setValue(val);
1251 return true;
1252 }
1253
1254 const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods)
1255 ? meta->methodOffset() : 0;
1256 for (index = meta->methodCount() - 1; index >= offset; --index) {
1257 QMetaMethod method = meta->method(index);
1258 if (hasMethodAccess(method, index, opt)
1259 && (methodName(method) == name)) {
1260 QtFunction *fun = new (exec)QtFunction(
1261 object, index, /*maybeOverloaded=*/true,
1262 &exec->globalData(), eng->originalGlobalObject()->functionStructure(),
1263 propertyName);
1264 slot.setValue(fun);
1265 data->cachedMembers.insert(name, fun);
1266 return true;
1267 }
1268 }
1269
1270 if (!(opt & QScriptEngine::ExcludeChildObjects)) {
1271 QList<QObject*> children = qobject->children();
1272 for (index = 0; index < children.count(); ++index) {
1273 QObject *child = children.at(index);
1274 if (child->objectName() == QString(propertyName.ustring())) {
1275 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
1276 QScriptValue tmp = QScriptEnginePrivate::get(eng)->newQObject(child, QScriptEngine::QtOwnership, opt);
1277 slot.setValue(eng->scriptValueToJSCValue(tmp));
1278 return true;
1279 }
1280 }
1281 }
1282
1283 return QScriptObjectDelegate::getOwnPropertySlot(object, exec, propertyName, slot);
1284#else //QT_NO_PROPERTIES
1285 return false;
1286#endif //QT_NO_PROPERTIES
1287}
1288
1289
1290bool QObjectDelegate::getOwnPropertyDescriptor(QScriptObject *object, JSC::ExecState *exec,
1291 const JSC::Identifier &propertyName,
1292 JSC::PropertyDescriptor &descriptor)
1293{
1294 //Note: this has to be kept in sync with getOwnPropertySlot abd getPropertyAttributes
1295#ifndef QT_NO_PROPERTIES
1296 QByteArray name = QString(propertyName.ustring()).toLatin1();
1297 QObject *qobject = data->value;
1298 if (!qobject) {
1299 QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject")
1300 .arg(QString::fromLatin1(name));
1301 descriptor.setValue(JSC::throwError(exec, JSC::GeneralError, message));
1302 return true;
1303 }
1304
1305 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1306
1307 const QMetaObject *meta = qobject->metaObject();
1308 {
1309 QHash<QByteArray, JSC::JSValue>::const_iterator it = data->cachedMembers.constFind(name);
1310 if (it != data->cachedMembers.constEnd()) {
1311 int index;
1312 if (GeneratePropertyFunctions && ((index = meta->indexOfProperty(name)) != -1)) {
1313 QMetaProperty prop = meta->property(index);
1314 descriptor.setAccessorDescriptor(it.value(), it.value(), flagsForMetaProperty(prop));
1315 if (!prop.isWritable())
1316 descriptor.setWritable(false);
1317 } else {
1318 unsigned attributes = QObjectMemberAttribute;
1319 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1320 attributes |= JSC::DontEnum;
1321 descriptor.setDescriptor(it.value(), attributes);
1322 }
1323 return true;
1324 }
1325 }
1326
1327 QScriptEnginePrivate *eng = scriptEngineFromExec(exec);
1328 int index = -1;
1329 if (name.contains('(')) {
1330 QByteArray normalized = QMetaObject::normalizedSignature(name);
1331 if (-1 != (index = meta->indexOfMethod(normalized))) {
1332 QMetaMethod method = meta->method(index);
1333 if (hasMethodAccess(method, index, opt)) {
1334 if (!(opt & QScriptEngine::ExcludeSuperClassMethods)
1335 || (index >= meta->methodOffset())) {
1336 QtFunction *fun = new (exec)QtFunction(
1337 object, index, /*maybeOverloaded=*/false,
1338 &exec->globalData(), eng->originalGlobalObject()->functionStructure(),
1339 propertyName);
1340 data->cachedMembers.insert(name, fun);
1341 unsigned attributes = QObjectMemberAttribute;
1342 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1343 attributes |= JSC::DontEnum;
1344 descriptor.setDescriptor(fun, attributes);
1345 return true;
1346 }
1347 }
1348 }
1349 }
1350
1351 index = meta->indexOfProperty(name);
1352 if (index != -1) {
1353 QMetaProperty prop = meta->property(index);
1354 if (prop.isScriptable()) {
1355 if (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1356 || (index >= meta->propertyOffset())) {
1357 unsigned attributes = flagsForMetaProperty(prop);
1358 if (GeneratePropertyFunctions) {
1359 QtPropertyFunction *fun = new (exec)QtPropertyFunction(
1360 meta, index, &exec->globalData(),
1361 eng->originalGlobalObject()->functionStructure(),
1362 propertyName);
1363 data->cachedMembers.insert(name, fun);
1364 descriptor.setAccessorDescriptor(fun, fun, attributes);
1365 if (attributes & JSC::ReadOnly)
1366 descriptor.setWritable(false);
1367 } else {
1368 JSC::JSValue val;
1369 if (!prop.isValid())
1370 val = JSC::jsUndefined();
1371 else
1372 val = eng->jscValueFromVariant(prop.read(qobject));
1373 descriptor.setDescriptor(val, attributes);
1374 }
1375 return true;
1376 }
1377 }
1378 }
1379
1380 index = qobject->dynamicPropertyNames().indexOf(name);
1381 if (index != -1) {
1382 JSC::JSValue val = eng->jscValueFromVariant(qobject->property(name));
1383 descriptor.setDescriptor(val, QObjectMemberAttribute);
1384 return true;
1385 }
1386
1387 const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods)
1388 ? meta->methodOffset() : 0;
1389 for (index = meta->methodCount() - 1; index >= offset; --index) {
1390 QMetaMethod method = meta->method(index);
1391 if (hasMethodAccess(method, index, opt)
1392 && (methodName(method) == name)) {
1393 QtFunction *fun = new (exec)QtFunction(
1394 object, index, /*maybeOverloaded=*/true,
1395 &exec->globalData(), eng->originalGlobalObject()->functionStructure(),
1396 propertyName);
1397 unsigned attributes = QObjectMemberAttribute;
1398 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1399 attributes |= JSC::DontEnum;
1400 descriptor.setDescriptor(fun, attributes);
1401 data->cachedMembers.insert(name, fun);
1402 return true;
1403 }
1404 }
1405
1406 if (!(opt & QScriptEngine::ExcludeChildObjects)) {
1407 QList<QObject*> children = qobject->children();
1408 for (index = 0; index < children.count(); ++index) {
1409 QObject *child = children.at(index);
1410 if (child->objectName() == QString(propertyName.ustring())) {
1411 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
1412 QScriptValue tmp = QScriptEnginePrivate::get(eng)->newQObject(child, QScriptEngine::QtOwnership, opt);
1413 descriptor.setDescriptor(eng->scriptValueToJSCValue(tmp), JSC::ReadOnly | JSC::DontDelete | JSC::DontEnum);
1414 return true;
1415 }
1416 }
1417 }
1418
1419 return QScriptObjectDelegate::getOwnPropertyDescriptor(object, exec, propertyName, descriptor);
1420#else //QT_NO_PROPERTIES
1421 return false;
1422#endif //QT_NO_PROPERTIES
1423}
1424
1425void QObjectDelegate::put(QScriptObject *object, JSC::ExecState* exec,
1426 const JSC::Identifier& propertyName,
1427 JSC::JSValue value, JSC::PutPropertySlot &slot)
1428{
1429#ifndef QT_NO_PROPERTIES
1430 QByteArray name = ((QString)propertyName.ustring()).toLatin1();
1431 QObject *qobject = data->value;
1432 if (!qobject) {
1433 QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject")
1434 .arg(QString::fromLatin1(name));
1435 JSC::throwError(exec, JSC::GeneralError, message);
1436 return;
1437 }
1438
1439 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1440 const QMetaObject *meta = qobject->metaObject();
1441 QScriptEnginePrivate *eng = scriptEngineFromExec(exec);
1442 int index = -1;
1443 if (name.contains('(')) {
1444 QByteArray normalized = QMetaObject::normalizedSignature(name);
1445 if (-1 != (index = meta->indexOfMethod(normalized))) {
1446 QMetaMethod method = meta->method(index);
1447 if (hasMethodAccess(method, index, opt)) {
1448 if (!(opt & QScriptEngine::ExcludeSuperClassMethods)
1449 || (index >= meta->methodOffset())) {
1450 data->cachedMembers.insert(name, value);
1451 return;
1452 }
1453 }
1454 }
1455 }
1456
1457 index = meta->indexOfProperty(name);
1458 if (index != -1) {
1459 QMetaProperty prop = meta->property(index);
1460 if (prop.isScriptable()) {
1461 if (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1462 || (index >= meta->propertyOffset())) {
1463 if (GeneratePropertyFunctions) {
1464 // ### ideally JSC would do this for us already, i.e. find out
1465 // that the property is a setter and call the setter.
1466 // Maybe QtPropertyFunction needs to inherit JSC::GetterSetter.
1467 JSC::JSValue fun;
1468 QHash<QByteArray, JSC::JSValue>::const_iterator it;
1469 it = data->cachedMembers.constFind(name);
1470 if (it != data->cachedMembers.constEnd()) {
1471 fun = it.value();
1472 } else {
1473 fun = new (exec)QtPropertyFunction(
1474 meta, index, &exec->globalData(),
1475 eng->originalGlobalObject()->functionStructure(),
1476 propertyName);
1477 data->cachedMembers.insert(name, fun);
1478 }
1479 JSC::CallData callData;
1480 JSC::CallType callType = fun.getCallData(callData);
1481 JSC::JSValue argv[1] = { value };
1482 JSC::ArgList args(argv, 1);
1483 (void)JSC::call(exec, fun, callType, callData, object, args);
1484 } else {
1485 QVariant v;
1486 if (prop.isEnumType() && value.isString()
1487 && !eng->hasDemarshalFunction(prop.userType())) {
1488 // give QMetaProperty::write() a chance to convert from
1489 // string to enum value
1490 v = (QString)value.toString(exec);
1491 } else {
1492 v = eng->jscValueToVariant(value, prop.userType());
1493 }
1494 (void)prop.write(qobject, v);
1495 }
1496 return;
1497 }
1498 }
1499 }
1500
1501 const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods)
1502 ? meta->methodOffset() : 0;
1503 for (index = meta->methodCount() - 1; index >= offset; --index) {
1504 QMetaMethod method = meta->method(index);
1505 if (hasMethodAccess(method, index, opt)
1506 && (methodName(method) == name)) {
1507 data->cachedMembers.insert(name, value);
1508 return;
1509 }
1510 }
1511
1512 index = qobject->dynamicPropertyNames().indexOf(name);
1513 if ((index != -1) || (opt & QScriptEngine::AutoCreateDynamicProperties)) {
1514 QVariant v = eng->scriptValueFromJSCValue(value).toVariant();
1515 (void)qobject->setProperty(name, v);
1516 return;
1517 }
1518
1519 QScriptObjectDelegate::put(object, exec, propertyName, value, slot);
1520#endif //QT_NO_PROPERTIES
1521}
1522
1523bool QObjectDelegate::deleteProperty(QScriptObject *object, JSC::ExecState *exec,
1524 const JSC::Identifier& propertyName,
1525 bool checkDontDelete)
1526{
1527#ifndef QT_NO_PROPERTIES
1528 QByteArray name = ((QString)propertyName.ustring()).toLatin1();
1529 QObject *qobject = data->value;
1530 if (!qobject) {
1531 QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject")
1532 .arg(QString::fromLatin1(name));
1533 JSC::throwError(exec, JSC::GeneralError, message);
1534 return false;
1535 }
1536
1537 const QMetaObject *meta = qobject->metaObject();
1538 {
1539 QHash<QByteArray, JSC::JSValue>::iterator it = data->cachedMembers.find(name);
1540 if (it != data->cachedMembers.end()) {
1541 if (GeneratePropertyFunctions && (meta->indexOfProperty(name) != -1))
1542 return false;
1543 data->cachedMembers.erase(it);
1544 return true;
1545 }
1546 }
1547
1548 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1549 int index = meta->indexOfProperty(name);
1550 if (index != -1) {
1551 QMetaProperty prop = meta->property(index);
1552 if (prop.isScriptable() &&
1553 (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1554 || (index >= meta->propertyOffset()))) {
1555 return false;
1556 }
1557 }
1558
1559 index = qobject->dynamicPropertyNames().indexOf(name);
1560 if (index != -1) {
1561 (void)qobject->setProperty(name, QVariant());
1562 return true;
1563 }
1564
1565 return QScriptObjectDelegate::deleteProperty(object, exec, propertyName, checkDontDelete);
1566#else //QT_NO_PROPERTIES
1567 return false;
1568#endif //QT_NO_PROPERTIES
1569}
1570
1571bool QObjectDelegate::getPropertyAttributes(const QScriptObject *object,
1572 JSC::ExecState *exec,
1573 const JSC::Identifier &propertyName,
1574 unsigned &attributes) const
1575{
1576#ifndef QT_NO_PROPERTIES
1577 //Note: this has to be kept in sync with getOwnPropertyDescriptor and getOwnPropertySlot
1578 QByteArray name = ((QString)propertyName.ustring()).toLatin1();
1579 QObject *qobject = data->value;
1580 if (!qobject)
1581 return false;
1582
1583 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1584 const QMetaObject *meta = qobject->metaObject();
1585 int index = -1;
1586 if (name.contains('(')) {
1587 QByteArray normalized = QMetaObject::normalizedSignature(name);
1588 if (-1 != (index = meta->indexOfMethod(normalized))) {
1589 QMetaMethod method = meta->method(index);
1590 if (hasMethodAccess(method, index, opt)) {
1591 if (!(opt & QScriptEngine::ExcludeSuperClassMethods)
1592 || (index >= meta->methodOffset())) {
1593 attributes = QObjectMemberAttribute;
1594 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1595 attributes |= JSC::DontEnum;
1596 return true;
1597 }
1598 }
1599 }
1600 }
1601
1602 index = meta->indexOfProperty(name);
1603 if (index != -1) {
1604 QMetaProperty prop = meta->property(index);
1605 if (prop.isScriptable()) {
1606 if (!(opt & QScriptEngine::ExcludeSuperClassProperties)
1607 || (index >= meta->propertyOffset())) {
1608 attributes = flagsForMetaProperty(prop);
1609 return true;
1610 }
1611 }
1612 }
1613
1614 index = qobject->dynamicPropertyNames().indexOf(name);
1615 if (index != -1) {
1616 attributes = QObjectMemberAttribute;
1617 return true;
1618 }
1619
1620 const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods)
1621 ? meta->methodOffset() : 0;
1622 for (index = meta->methodCount() - 1; index >= offset; --index) {
1623 QMetaMethod method = meta->method(index);
1624 if (hasMethodAccess(method, index, opt)
1625 && (methodName(method) == name)) {
1626 attributes = QObjectMemberAttribute;
1627 if (opt & QScriptEngine::SkipMethodsInEnumeration)
1628 attributes |= JSC::DontEnum;
1629 return true;
1630 }
1631 }
1632
1633 if (!(opt & QScriptEngine::ExcludeChildObjects)) {
1634 QList<QObject*> children = qobject->children();
1635 for (index = 0; index < children.count(); ++index) {
1636 QObject *child = children.at(index);
1637 if (child->objectName() == (QString)(propertyName.ustring())) {
1638 attributes = JSC::ReadOnly | JSC::DontDelete | JSC::DontEnum;
1639 return true;
1640 }
1641 }
1642 }
1643
1644 return QScriptObjectDelegate::getPropertyAttributes(object, exec, propertyName, attributes);
1645#else //QT_NO_PROPERTIES
1646 return false;
1647#endif //QT_NO_PROPERTIES
1648}
1649
1650void QObjectDelegate::getOwnPropertyNames(QScriptObject *object, JSC::ExecState *exec,
1651 JSC::PropertyNameArray &propertyNames,
1652 bool includeNonEnumerable)
1653{
1654#ifndef QT_NO_PROPERTIES
1655 QObject *qobject = data->value;
1656 if (!qobject) {
1657 QString message = QString::fromLatin1("cannot get property names of deleted QObject");
1658 JSC::throwError(exec, JSC::GeneralError, message);
1659 return;
1660 }
1661
1662 const QScriptEngine::QObjectWrapOptions &opt = data->options;
1663 const QMetaObject *meta = qobject->metaObject();
1664 {
1665 int i = (opt & QScriptEngine::ExcludeSuperClassProperties)
1666 ? meta->propertyOffset() : 0;
1667 for ( ; i < meta->propertyCount(); ++i) {
1668 QMetaProperty prop = meta->property(i);
1669 if (isEnumerableMetaProperty(prop, meta, i)) {
1670 QString name = QString::fromLatin1(prop.name());
1671 propertyNames.add(JSC::Identifier(exec, name));
1672 }
1673 }
1674 }
1675
1676 {
1677 QList<QByteArray> dpNames = qobject->dynamicPropertyNames();
1678 for (int i = 0; i < dpNames.size(); ++i) {
1679 QString name = QString::fromLatin1(dpNames.at(i));
1680 propertyNames.add(JSC::Identifier(exec, name));
1681 }
1682 }
1683
1684 if (!(opt & QScriptEngine::SkipMethodsInEnumeration)) {
1685 int i = (opt & QScriptEngine::ExcludeSuperClassMethods)
1686 ? meta->methodOffset() : 0;
1687 for ( ; i < meta->methodCount(); ++i) {
1688 QMetaMethod method = meta->method(i);
1689 if (hasMethodAccess(method, i, opt)) {
1690 QMetaMethod method = meta->method(i);
1691 QString sig = QString::fromLatin1(method.signature());
1692 propertyNames.add(JSC::Identifier(exec, sig));
1693 }
1694 }
1695 }
1696
1697 QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, includeNonEnumerable);
1698#endif //QT_NO_PROPERTIES
1699}
1700
1701void QObjectDelegate::markChildren(QScriptObject *object, JSC::MarkStack& markStack)
1702{
1703 QHash<QByteArray, JSC::JSValue>::const_iterator it;
1704 for (it = data->cachedMembers.constBegin(); it != data->cachedMembers.constEnd(); ++it) {
1705 JSC::JSValue val = it.value();
1706 if (val)
1707 markStack.append(val);
1708 }
1709
1710 QScriptObjectDelegate::markChildren(object, markStack);
1711}
1712
1713bool QObjectDelegate::compareToObject(QScriptObject *, JSC::ExecState *exec, JSC::JSObject *o2)
1714{
1715 if (!o2->inherits(&QScriptObject::info))
1716 return false;
1717 QScriptObject *object = static_cast<QScriptObject*>(o2);
1718 QScriptObjectDelegate *delegate = object->delegate();
1719 if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject))
1720 return false;
1721 return value() == static_cast<QObjectDelegate *>(delegate)->value();
1722}
1723
1724static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncFindChild(JSC::ExecState *exec, JSC::JSObject*,
1725 JSC::JSValue thisValue, const JSC::ArgList &args)
1726{
1727 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
1728 thisValue = engine->toUsableValue(thisValue);
1729 if (!thisValue.inherits(&QScriptObject::info))
1730 return throwError(exec, JSC::TypeError, "this object is not a QObject");
1731 QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(thisValue));
1732 QScriptObjectDelegate *delegate = scriptObject->delegate();
1733 if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject))
1734 return throwError(exec, JSC::TypeError, "this object is not a QObject");
1735 QObject *obj = static_cast<QObjectDelegate*>(delegate)->value();
1736 QString name;
1737 if (args.size() != 0)
1738 name = args.at(0).toString(exec);
1739 QObject *child = qFindChild<QObject*>(obj, name);
1740 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
1741 return engine->newQObject(child, QScriptEngine::QtOwnership, opt);
1742}
1743
1744static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncFindChildren(JSC::ExecState *exec, JSC::JSObject*,
1745 JSC::JSValue thisValue, const JSC::ArgList &args)
1746{
1747 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
1748 thisValue = engine->toUsableValue(thisValue);
1749 // extract the QObject
1750 if (!thisValue.inherits(&QScriptObject::info))
1751 return throwError(exec, JSC::TypeError, "this object is not a QObject");
1752 QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(thisValue));
1753 QScriptObjectDelegate *delegate = scriptObject->delegate();
1754 if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject))
1755 return throwError(exec, JSC::TypeError, "this object is not a QObject");
1756 const QObject *const obj = static_cast<QObjectDelegate*>(delegate)->value();
1757
1758 // find the children
1759 QList<QObject *> children;
1760 if (args.size() != 0) {
1761 const JSC::JSValue arg = args.at(0);
1762 if (arg.inherits(&JSC::RegExpObject::info)) {
1763 const QObjectList allChildren= obj->children();
1764
1765 JSC::RegExpObject *const regexp = JSC::asRegExpObject(arg);
1766
1767 const int allChildrenCount = allChildren.size();
1768 for (int i = 0; i < allChildrenCount; ++i) {
1769 QObject *const child = allChildren.at(i);
1770 const JSC::UString childName = child->objectName();
1771 JSC::RegExpConstructor* regExpConstructor = engine->originalGlobalObject()->regExpConstructor();
1772 int position;
1773 int length;
1774 regExpConstructor->performMatch(regexp->regExp(), childName, 0, position, length);
1775 if (position >= 0)
1776 children.append(child);
1777 }
1778 } else {
1779 const QString name(args.at(0).toString(exec));
1780 children = qFindChildren<QObject*>(obj, name);
1781 }
1782 } else {
1783 children = qFindChildren<QObject*>(obj, QString());
1784 }
1785 // create the result array with the children
1786 const int length = children.size();
1787 JSC::JSArray *const result = JSC::constructEmptyArray(exec, length);
1788
1789 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
1790 for (int i = 0; i < length; ++i) {
1791 QObject *const child = children.at(i);
1792 result->put(exec, i, engine->newQObject(child, QScriptEngine::QtOwnership, opt));
1793 }
1794 return JSC::JSValue(result);
1795}
1796
1797static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncToString(JSC::ExecState *exec, JSC::JSObject*,
1798 JSC::JSValue thisValue, const JSC::ArgList&)
1799{
1800 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
1801 thisValue = engine->toUsableValue(thisValue);
1802 if (!thisValue.inherits(&QScriptObject::info))
1803 return JSC::jsUndefined();
1804 QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(thisValue));
1805 QScriptObjectDelegate *delegate = scriptObject->delegate();
1806 if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject))
1807 return JSC::jsUndefined();
1808 QObject *obj = static_cast<QObjectDelegate*>(delegate)->value();
1809 const QMetaObject *meta = obj ? obj->metaObject() : &QObject::staticMetaObject;
1810 QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed");
1811 QString str = QString::fromUtf8("%0(name = \"%1\")")
1812 .arg(QLatin1String(meta->className())).arg(name);
1813 return JSC::jsString(exec, str);
1814}
1815
1816QObjectPrototype::QObjectPrototype(JSC::ExecState* exec, WTF::PassRefPtr<JSC::Structure> structure,
1817 JSC::Structure* prototypeFunctionStructure)
1818 : QScriptObject(structure)
1819{
1820 setDelegate(new QObjectDelegate(new QObjectPrototypeObject(), QScriptEngine::AutoOwnership,
1821 QScriptEngine::ExcludeSuperClassMethods
1822 | QScriptEngine::ExcludeSuperClassProperties
1823 | QScriptEngine::ExcludeChildObjects));
1824
1825 putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/0, exec->propertyNames().toString, qobjectProtoFuncToString), JSC::DontEnum);
1826 putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChild"), qobjectProtoFuncFindChild), JSC::DontEnum);
1827 putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChildren"), qobjectProtoFuncFindChildren), JSC::DontEnum);
1828 this->structure()->setHasGetterSetterProperties(true);
1829}
1830
1831const JSC::ClassInfo QMetaObjectWrapperObject::info = { "QMetaObject", 0, 0, 0 };
1832
1833QMetaObjectWrapperObject::QMetaObjectWrapperObject(
1834 JSC::ExecState *exec, const QMetaObject *metaObject, JSC::JSValue ctor,
1835 WTF::PassRefPtr<JSC::Structure> sid)
1836 : JSC::JSObject(sid),
1837 data(new Data(metaObject, ctor))
1838{
1839 if (!ctor)
1840 data->prototype = new (exec)JSC::JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
1841}
1842
1843QMetaObjectWrapperObject::~QMetaObjectWrapperObject()
1844{
1845 delete data;
1846}
1847
1848bool QMetaObjectWrapperObject::getOwnPropertySlot(
1849 JSC::ExecState *exec, const JSC::Identifier& propertyName,
1850 JSC::PropertySlot &slot)
1851{
1852 const QMetaObject *meta = data->value;
1853 if (!meta)
1854 return false;
1855
1856 if (propertyName == exec->propertyNames().prototype) {
1857 if (data->ctor)
1858 slot.setValue(data->ctor.get(exec, propertyName));
1859 else
1860 slot.setValue(data->prototype);
1861 return true;
1862 }
1863
1864 QByteArray name = QString(propertyName.ustring()).toLatin1();
1865
1866 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1867 QMetaEnum e = meta->enumerator(i);
1868 for (int j = 0; j < e.keyCount(); ++j) {
1869 const char *key = e.key(j);
1870 if (!qstrcmp(key, name.constData())) {
1871 slot.setValue(JSC::JSValue(exec, e.value(j)));
1872 return true;
1873 }
1874 }
1875 }
1876
1877 return JSC::JSObject::getOwnPropertySlot(exec, propertyName, slot);
1878}
1879
1880void QMetaObjectWrapperObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName,
1881 JSC::JSValue value, JSC::PutPropertySlot &slot)
1882{
1883 if (propertyName == exec->propertyNames().prototype) {
1884 if (data->ctor)
1885 data->ctor.put(exec, propertyName, value, slot);
1886 else
1887 data->prototype = value;
1888 return;
1889 }
1890 const QMetaObject *meta = data->value;
1891 if (meta) {
1892 QByteArray name = QString(propertyName.ustring()).toLatin1();
1893 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1894 QMetaEnum e = meta->enumerator(i);
1895 for (int j = 0; j < e.keyCount(); ++j) {
1896 if (!qstrcmp(e.key(j), name.constData()))
1897 return;
1898 }
1899 }
1900 }
1901 JSC::JSObject::put(exec, propertyName, value, slot);
1902}
1903
1904bool QMetaObjectWrapperObject::deleteProperty(
1905 JSC::ExecState *exec, const JSC::Identifier& propertyName,
1906 bool checkDontDelete)
1907{
1908 if (propertyName == exec->propertyNames().prototype)
1909 return false;
1910 const QMetaObject *meta = data->value;
1911 if (meta) {
1912 QByteArray name = QString(propertyName.ustring()).toLatin1();
1913 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1914 QMetaEnum e = meta->enumerator(i);
1915 for (int j = 0; j < e.keyCount(); ++j) {
1916 if (!qstrcmp(e.key(j), name.constData()))
1917 return false;
1918 }
1919 }
1920 }
1921 return JSC::JSObject::deleteProperty(exec, propertyName, checkDontDelete);
1922}
1923
1924bool QMetaObjectWrapperObject::getPropertyAttributes(JSC::ExecState *exec,
1925 const JSC::Identifier &propertyName,
1926 unsigned &attributes) const
1927{
1928 if (propertyName == exec->propertyNames().prototype) {
1929 attributes = JSC::DontDelete;
1930 return true;
1931 }
1932 const QMetaObject *meta = data->value;
1933 if (meta) {
1934 QByteArray name = QString(propertyName.ustring()).toLatin1();
1935 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1936 QMetaEnum e = meta->enumerator(i);
1937 for (int j = 0; j < e.keyCount(); ++j) {
1938 if (!qstrcmp(e.key(j), name.constData())) {
1939 attributes = JSC::ReadOnly | JSC::DontDelete;
1940 return true;
1941 }
1942 }
1943 }
1944 }
1945 return JSC::JSObject::getPropertyAttributes(exec, propertyName, attributes);
1946}
1947
1948void QMetaObjectWrapperObject::getOwnPropertyNames(JSC::ExecState *exec,
1949 JSC::PropertyNameArray &propertyNames,
1950 bool includeNonEnumerable)
1951{
1952 const QMetaObject *meta = data->value;
1953 if (!meta)
1954 return;
1955 for (int i = 0; i < meta->enumeratorCount(); ++i) {
1956 QMetaEnum e = meta->enumerator(i);
1957 for (int j = 0; j < e.keyCount(); ++j)
1958 propertyNames.add(JSC::Identifier(exec, e.key(j)));
1959 }
1960 JSC::JSObject::getOwnPropertyNames(exec, propertyNames, includeNonEnumerable);
1961}
1962
1963void QMetaObjectWrapperObject::markChildren(JSC::MarkStack& markStack)
1964{
1965 if (data->ctor)
1966 markStack.append(data->ctor);
1967 if (data->prototype)
1968 markStack.append(data->prototype);
1969 JSC::JSObject::markChildren(markStack);
1970}
1971
1972JSC::CallType QMetaObjectWrapperObject::getCallData(JSC::CallData& callData)
1973{
1974 callData.native.function = call;
1975 return JSC::CallTypeHost;
1976}
1977
1978JSC::ConstructType QMetaObjectWrapperObject::getConstructData(JSC::ConstructData& constructData)
1979{
1980 constructData.native.function = construct;
1981 return JSC::ConstructTypeHost;
1982}
1983
1984JSC::JSValue JSC_HOST_CALL QMetaObjectWrapperObject::call(
1985 JSC::ExecState *exec, JSC::JSObject *callee,
1986 JSC::JSValue thisValue, const JSC::ArgList &args)
1987{
1988 QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec);
1989 thisValue = eng_p->toUsableValue(thisValue);
1990 if (!callee->inherits(&QMetaObjectWrapperObject::info))
1991 return throwError(exec, JSC::TypeError, "callee is not a QMetaObject");
1992 QMetaObjectWrapperObject *self = static_cast<QMetaObjectWrapperObject*>(callee);
1993 JSC::ExecState *previousFrame = eng_p->currentFrame;
1994 eng_p->pushContext(exec, thisValue, args, callee);
1995 JSC::JSValue result = self->execute(eng_p->currentFrame, args);
1996 eng_p->popContext();
1997 eng_p->currentFrame = previousFrame;
1998 return result;
1999}
2000
2001JSC::JSObject* QMetaObjectWrapperObject::construct(JSC::ExecState *exec, JSC::JSObject *callee, const JSC::ArgList &args)
2002{
2003 QMetaObjectWrapperObject *self = static_cast<QMetaObjectWrapperObject*>(callee);
2004 QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec);
2005 JSC::ExecState *previousFrame = eng_p->currentFrame;
2006 eng_p->pushContext(exec, JSC::JSValue(), args, callee, true);
2007 JSC::JSValue result = self->execute(eng_p->currentFrame, args);
2008 eng_p->popContext();
2009 eng_p->currentFrame = previousFrame;
2010 if (!result || !result.isObject())
2011 return 0;
2012 return JSC::asObject(result);
2013}
2014
2015JSC::JSValue QMetaObjectWrapperObject::execute(JSC::ExecState *exec,
2016 const JSC::ArgList &args)
2017{
2018 if (data->ctor) {
2019 QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec);
2020 QScriptContext *ctx = eng_p->contextForFrame(exec);
2021 JSC::CallData callData;
2022 JSC::CallType callType = data->ctor.getCallData(callData);
2023 Q_UNUSED(callType);
2024 Q_ASSERT_X(callType == JSC::CallTypeHost, Q_FUNC_INFO, "script constructors not supported");
2025 if (data->ctor.inherits(&FunctionWithArgWrapper::info)) {
2026 FunctionWithArgWrapper *wrapper = static_cast<FunctionWithArgWrapper*>(JSC::asObject(data->ctor));
2027 QScriptValue result = wrapper->function()(ctx, QScriptEnginePrivate::get(eng_p), wrapper->arg());
2028 return eng_p->scriptValueToJSCValue(result);
2029 } else {
2030 Q_ASSERT(data->ctor.inherits(&FunctionWrapper::info));
2031 FunctionWrapper *wrapper = static_cast<FunctionWrapper*>(JSC::asObject(data->ctor));
2032 QScriptValue result = wrapper->function()(ctx, QScriptEnginePrivate::get(eng_p));
2033 return eng_p->scriptValueToJSCValue(result);
2034 }
2035 } else {
2036 const QMetaObject *meta = data->value;
2037 if (meta->constructorCount() > 0) {
2038 JSC::JSValue result = callQtMethod(exec, QMetaMethod::Constructor, /*thisQObject=*/0,
2039 args, meta, meta->constructorCount()-1, /*maybeOverloaded=*/true);
2040 if (!exec->hadException()) {
2041 Q_ASSERT(result && result.inherits(&QScriptObject::info));
2042 QScriptObject *object = static_cast<QScriptObject*>(JSC::asObject(result));
2043 QScript::QObjectDelegate *delegate = static_cast<QScript::QObjectDelegate*>(object->delegate());
2044 delegate->setOwnership(QScriptEngine::AutoOwnership);
2045 if (data->prototype)
2046 object->setPrototype(data->prototype);
2047 }
2048 return result;
2049 } else {
2050 QString message = QString::fromLatin1("no constructor for %0")
2051 .arg(QLatin1String(meta->className()));
2052 return JSC::throwError(exec, JSC::TypeError, message);
2053 }
2054 }
2055}
2056
2057struct StaticQtMetaObject : public QObject
2058{
2059 static const QMetaObject *get()
2060 { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
2061};
2062
2063static JSC::JSValue JSC_HOST_CALL qmetaobjectProtoFuncClassName(
2064 JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisValue, const JSC::ArgList&)
2065{
2066 QScriptEnginePrivate *engine = scriptEngineFromExec(exec);
2067 thisValue = engine->toUsableValue(thisValue);
2068 if (!thisValue.inherits(&QMetaObjectWrapperObject::info))
2069 return throwError(exec, JSC::TypeError, "this object is not a QMetaObject");
2070 const QMetaObject *meta = static_cast<QMetaObjectWrapperObject*>(JSC::asObject(thisValue))->value();
2071 return JSC::jsString(exec, meta->className());
2072}
2073
2074QMetaObjectPrototype::QMetaObjectPrototype(
2075 JSC::ExecState *exec, WTF::PassRefPtr<JSC::Structure> structure,
2076 JSC::Structure* prototypeFunctionStructure)
2077 : QMetaObjectWrapperObject(exec, StaticQtMetaObject::get(), /*ctor=*/JSC::JSValue(), structure)
2078{
2079 putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/0, JSC::Identifier(exec, "className"), qmetaobjectProtoFuncClassName), JSC::DontEnum);
2080}
2081
2082static const uint qt_meta_data_QObjectConnectionManager[] = {
2083
2084 // content:
2085 1, // revision
2086 0, // classname
2087 0, 0, // classinfo
2088 1, 10, // methods
2089 0, 0, // properties
2090 0, 0, // enums/sets
2091
2092 // slots: signature, parameters, type, tag, flags
2093 35, 34, 34, 34, 0x0a,
2094
2095 0 // eod
2096};
2097
2098static const char qt_meta_stringdata_QObjectConnectionManager[] = {
2099 "QScript::QObjectConnectionManager\0\0execute()\0"
2100};
2101
2102const QMetaObject QObjectConnectionManager::staticMetaObject = {
2103 { &QObject::staticMetaObject, qt_meta_stringdata_QObjectConnectionManager,
2104 qt_meta_data_QObjectConnectionManager, 0 }
2105};
2106
2107const QMetaObject *QObjectConnectionManager::metaObject() const
2108{
2109 return &staticMetaObject;
2110}
2111
2112void *QObjectConnectionManager::qt_metacast(const char *_clname)
2113{
2114 if (!_clname) return 0;
2115 if (!strcmp(_clname, qt_meta_stringdata_QObjectConnectionManager))
2116 return static_cast<void*>(const_cast<QObjectConnectionManager*>(this));
2117 return QObject::qt_metacast(_clname);
2118}
2119
2120int QObjectConnectionManager::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
2121{
2122 _id = QObject::qt_metacall(_c, _id, _a);
2123 if (_id < 0)
2124 return _id;
2125 if (_c == QMetaObject::InvokeMetaMethod) {
2126 execute(_id, _a);
2127 _id -= slotCounter;
2128 }
2129 return _id;
2130}
2131
2132void QObjectConnectionManager::execute(int slotIndex, void **argv)
2133{
2134 JSC::JSValue receiver;
2135 JSC::JSValue slot;
2136 JSC::JSValue senderWrapper;
2137 int signalIndex = -1;
2138 for (int i = 0; i < connections.size(); ++i) {
2139 const QVector<QObjectConnection> &cs = connections.at(i);
2140 for (int j = 0; j < cs.size(); ++j) {
2141 const QObjectConnection &c = cs.at(j);
2142 if (c.slotIndex == slotIndex) {
2143 receiver = c.receiver;
2144 slot = c.slot;
2145 senderWrapper = c.senderWrapper;
2146 signalIndex = i;
2147 break;
2148 }
2149 }
2150 }
2151 Q_ASSERT(slot && slot.isObject());
2152
2153 if (engine->isCollecting()) {
2154 qWarning("QtScript: can't execute signal handler during GC");
2155 // we can't do a script function call during GC,
2156 // so we're forced to ignore this signal
2157 return;
2158 }
2159
2160#if 0
2161 QScriptFunction *fun = engine->convertToNativeFunction(slot);
2162 if (fun == 0) {
2163 // the signal handler has been GC'ed. This can only happen when
2164 // a QObject is owned by the engine, the engine is destroyed, and
2165 // there is a script function connected to the destroyed() signal
2166 Q_ASSERT(signalIndex <= 1); // destroyed(QObject*)
2167 return;
2168 }
2169#endif
2170
2171 const QMetaObject *meta = sender()->metaObject();
2172 const QMetaMethod method = meta->method(signalIndex);
2173
2174 QList<QByteArray> parameterTypes = method.parameterTypes();
2175 int argc = parameterTypes.count();
2176
2177 JSC::ExecState *exec = engine->currentFrame;
2178 QVarLengthArray<JSC::JSValue, 8> argsVector(argc);
2179 for (int i = 0; i < argc; ++i) {
2180 // ### optimize -- no need to convert via QScriptValue
2181 QScriptValue actual;
2182 void *arg = argv[i + 1];
2183 QByteArray typeName = parameterTypes.at(i);
2184 int argType = QMetaType::type(parameterTypes.at(i));
2185 if (!argType) {
2186 if (typeName == "QVariant") {
2187 actual = engine->scriptValueFromVariant(*reinterpret_cast<QVariant*>(arg));
2188 } else {
2189 qWarning("QScriptEngine: Unable to handle unregistered datatype '%s' "
2190 "when invoking handler of signal %s::%s",
2191 typeName.constData(), meta->className(), method.signature());
2192 actual = QScriptValue(QScriptValue::UndefinedValue);
2193 }
2194 } else {
2195 actual = engine->create(argType, arg);
2196 }
2197 argsVector[i] = engine->scriptValueToJSCValue(actual);
2198 }
2199 JSC::ArgList jscArgs(argsVector.data(), argsVector.size());
2200
2201 JSC::JSValue senderObject;
2202 if (senderWrapper && senderWrapper.inherits(&QScriptObject::info)) // ### check if it's actually a QObject wrapper
2203 senderObject = senderWrapper;
2204 else {
2205 QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject;
2206 senderObject = engine->newQObject(sender(), QScriptEngine::QtOwnership, opt);
2207 }
2208
2209 JSC::JSValue thisObject;
2210 if (receiver && receiver.isObject())
2211 thisObject = receiver;
2212 else
2213 thisObject = engine->globalObject();
2214
2215 JSC::CallData callData;
2216 JSC::CallType callType = slot.getCallData(callData);
2217 if (exec->hadException())
2218 exec->clearException(); // ### otherwise JSC asserts
2219 JSC::call(exec, slot, callType, callData, thisObject, jscArgs);
2220
2221 if (exec->hadException()) {
2222 engine->emitSignalHandlerException();
2223 }
2224}
2225
2226QObjectConnectionManager::QObjectConnectionManager(QScriptEnginePrivate *eng)
2227 : engine(eng), slotCounter(0)
2228{
2229}
2230
2231QObjectConnectionManager::~QObjectConnectionManager()
2232{
2233}
2234
2235void QObjectConnectionManager::mark(JSC::MarkStack& markStack)
2236{
2237 for (int i = 0; i < connections.size(); ++i) {
2238 QVector<QObjectConnection> &cs = connections[i];
2239 for (int j = 0; j < cs.size(); ++j)
2240 cs[j].mark(markStack);
2241 }
2242}
2243
2244bool QObjectConnectionManager::addSignalHandler(
2245 QObject *sender, int signalIndex, JSC::JSValue receiver,
2246 JSC::JSValue function, JSC::JSValue senderWrapper,
2247 Qt::ConnectionType type)
2248{
2249 if (connections.size() <= signalIndex)
2250 connections.resize(signalIndex+1);
2251 QVector<QObjectConnection> &cs = connections[signalIndex];
2252 int absSlotIndex = slotCounter + metaObject()->methodOffset();
2253 bool ok = QMetaObject::connect(sender, signalIndex, this, absSlotIndex, type);
2254 if (ok) {
2255 cs.append(QObjectConnection(slotCounter++, receiver, function, senderWrapper));
2256 QMetaMethod signal = sender->metaObject()->method(signalIndex);
2257 QByteArray signalString;
2258 signalString.append('2'); // signal code
2259 signalString.append(signal.signature());
2260 static_cast<QObjectNotifyCaller*>(sender)->callConnectNotify(signalString);
2261 }
2262 return ok;
2263}
2264
2265bool QObjectConnectionManager::removeSignalHandler(
2266 QObject *sender, int signalIndex,
2267 JSC::JSValue receiver, JSC::JSValue slot)
2268{
2269 if (connections.size() <= signalIndex)
2270 return false;
2271 QVector<QObjectConnection> &cs = connections[signalIndex];
2272 for (int i = 0; i < cs.size(); ++i) {
2273 const QObjectConnection &c = cs.at(i);
2274 if (c.hasTarget(receiver, slot)) {
2275 int absSlotIndex = c.slotIndex + metaObject()->methodOffset();
2276 bool ok = QMetaObject::disconnect(sender, signalIndex, this, absSlotIndex);
2277 if (ok) {
2278 cs.remove(i);
2279 QMetaMethod signal = sender->metaObject()->method(signalIndex);
2280 QByteArray signalString;
2281 signalString.append('2'); // signal code
2282 signalString.append(signal.signature());
2283 static_cast<QScript::QObjectNotifyCaller*>(sender)->callDisconnectNotify(signalString);
2284 }
2285 return ok;
2286 }
2287 }
2288 return false;
2289}
2290
2291QObjectData::QObjectData(QScriptEnginePrivate *eng)
2292 : engine(eng), connectionManager(0)
2293{
2294}
2295
2296QObjectData::~QObjectData()
2297{
2298 if (connectionManager) {
2299 delete connectionManager;
2300 connectionManager = 0;
2301 }
2302}
2303
2304void QObjectData::mark(JSC::MarkStack& markStack)
2305{
2306 if (connectionManager)
2307 connectionManager->mark(markStack);
2308 {
2309 QList<QScript::QObjectWrapperInfo>::iterator it;
2310 for (it = wrappers.begin(); it != wrappers.end(); ) {
2311 const QScript::QObjectWrapperInfo &info = *it;
2312 // ### don't mark if there are no other references.
2313 // we need something like isMarked()
2314 markStack.append(info.object);
2315 ++it;
2316 }
2317 }
2318}
2319
2320bool QObjectData::addSignalHandler(QObject *sender,
2321 int signalIndex,
2322 JSC::JSValue receiver,
2323 JSC::JSValue slot,
2324 JSC::JSValue senderWrapper,
2325 Qt::ConnectionType type)
2326{
2327 if (!connectionManager)
2328 connectionManager = new QObjectConnectionManager(engine);
2329 return connectionManager->addSignalHandler(
2330 sender, signalIndex, receiver, slot, senderWrapper, type);
2331}
2332
2333bool QObjectData::removeSignalHandler(QObject *sender,
2334 int signalIndex,
2335 JSC::JSValue receiver,
2336 JSC::JSValue slot)
2337{
2338 if (!connectionManager)
2339 return false;
2340 return connectionManager->removeSignalHandler(
2341 sender, signalIndex, receiver, slot);
2342}
2343
2344QScriptObject *QObjectData::findWrapper(QScriptEngine::ValueOwnership ownership,
2345 const QScriptEngine::QObjectWrapOptions &options) const
2346{
2347 for (int i = 0; i < wrappers.size(); ++i) {
2348 const QObjectWrapperInfo &info = wrappers.at(i);
2349 if ((info.ownership == ownership) && (info.options == options))
2350 return info.object;
2351 }
2352 return 0;
2353}
2354
2355void QObjectData::registerWrapper(QScriptObject *wrapper,
2356 QScriptEngine::ValueOwnership ownership,
2357 const QScriptEngine::QObjectWrapOptions &options)
2358{
2359 wrappers.append(QObjectWrapperInfo(wrapper, ownership, options));
2360}
2361
2362} // namespace QScript
2363
2364QT_END_NAMESPACE
2365
2366namespace JSC
2367{
2368 ASSERT_CLASS_FITS_IN_CELL(QScript::QtFunction);
2369}
2370
2371#include "moc_qscriptqobject_p.cpp"
2372
Note: See TracBrowser for help on using the repository browser.