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

Last change on this file since 1023 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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