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