1/****************************************************************************
2**
3** Copyright (C) 2017 Crimson AS <info@crimson.no>
4** Copyright (C) 2016 The Qt Company Ltd.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the test suite of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29#include <QtTest/QtTest>
30#include <QtQml/qqmlcomponent.h>
31#include <QtQml/qqmlengine.h>
32#include <QtQml/qqmlexpression.h>
33#include <QtQml/qqmlcontext.h>
34#include <QtCore/qfileinfo.h>
35#include <QtCore/qdebug.h>
36#include <QtCore/qdir.h>
37#include <QtCore/qnumeric.h>
38#include <private/qqmlengine_p.h>
39#include <private/qqmlvmemetaobject_p.h>
40#include <private/qv4qmlcontext_p.h>
41#include "testtypes.h"
42#include "testhttpserver.h"
43#include "../../shared/util.h"
44#include <private/qv4functionobject_p.h>
45#include <private/qv4scopedvalue_p.h>
46#include <private/qv4jscall_p.h>
47#include <private/qv4alloca_p.h>
48#include <private/qv4runtime_p.h>
49#include <private/qv4object_p.h>
50#include <private/qv4script_p.h>
51#include <private/qqmlcomponentattached_p.h>
52#include <private/qv4objectiterator_p.h>
53#include <private/qqmlabstractbinding_p.h>
54#include <private/qqmlvaluetypeproxybinding_p.h>
55
56#ifdef Q_CC_MSVC
57#define NO_INLINE __declspec(noinline)
58#else
59#define NO_INLINE __attribute__((noinline))
60#endif
61
62/*
63This test covers evaluation of ECMAScript expressions and bindings from within
64QML. This does not include static QML language issues.
65
66Static QML language issues are covered in qmllanguage
67*/
68
69class tst_qqmlecmascript : public QQmlDataTest
70{
71 Q_OBJECT
72public:
73 tst_qqmlecmascript() {}
74
75private slots:
76 void initTestCase();
77 void arrayIncludesValueType();
78 void assignBasicTypes();
79 void assignDate_data();
80 void assignDate();
81 void exportDate_data();
82 void exportDate();
83 void checkDate_data();
84 void checkDate();
85 void checkDateTime_data();
86 void checkDateTime();
87 void idShortcutInvalidates();
88 void boolPropertiesEvaluateAsBool();
89 void methods();
90 void signalAssignment();
91 void signalArguments();
92 void bindingLoop();
93 void basicExpressions();
94 void basicExpressions_data();
95 void arrayExpressions();
96 void contextPropertiesTriggerReeval();
97 void objectPropertiesTriggerReeval();
98 void dependenciesWithFunctions();
99 void deferredProperties();
100 void deferredPropertiesErrors();
101 void deferredPropertiesInComponents();
102 void deferredPropertiesInDestruction();
103 void extensionObjects();
104 void overrideExtensionProperties();
105 void attachedProperties();
106 void enums();
107 void valueTypeFunctions();
108 void constantsOverrideBindings();
109 void outerBindingOverridesInnerBinding();
110 void aliasPropertyAndBinding();
111 void aliasPropertyReset();
112 void nonExistentAttachedObject();
113 void scope();
114 void importScope();
115 void signalParameterTypes();
116 void objectsCompareAsEqual();
117 void componentCreation_data();
118 void componentCreation();
119 void dynamicCreation_data();
120 void dynamicCreation();
121 void dynamicDestruction();
122 void objectToString();
123 void objectHasOwnProperty();
124 void selfDeletingBinding();
125 void extendedObjectPropertyLookup();
126 void extendedObjectPropertyLookup2();
127 void uncreatableExtendedObjectFailureCheck();
128 void extendedObjectPropertyLookup3();
129 void scriptErrors();
130 void functionErrors();
131 void propertyAssignmentErrors();
132 void signalTriggeredBindings();
133 void listProperties();
134 void exceptionClearsOnReeval();
135 void exceptionSlotProducesWarning();
136 void exceptionBindingProducesWarning();
137 void compileInvalidBinding();
138 void transientErrors();
139 void shutdownErrors();
140 void compositePropertyType();
141 void jsObject();
142 void undefinedResetsProperty();
143 void listToVariant();
144 void listAssignment();
145 void multiEngineObject();
146 void deletedObject();
147 void attachedPropertyScope();
148 void scriptConnect();
149 void scriptDisconnect();
150 void ownership();
151 void cppOwnershipReturnValue();
152 void ownershipCustomReturnValue();
153 void ownershipRootObject();
154 void ownershipConsistency();
155 void ownershipQmlIncubated();
156 void qlistqobjectMethods();
157 void strictlyEquals();
158 void compiled();
159 void numberAssignment();
160 void propertySplicing();
161 void signalWithUnknownTypes();
162 void signalWithJSValueInVariant_data();
163 void signalWithJSValueInVariant();
164 void signalWithJSValueInVariant_twoEngines_data();
165 void signalWithJSValueInVariant_twoEngines();
166 void signalWithQJSValue_data();
167 void signalWithQJSValue();
168 void singletonType_data();
169 void singletonType();
170 void singletonTypeCaching_data();
171 void singletonTypeCaching();
172 void singletonTypeImportOrder();
173 void singletonTypeResolution();
174 void importScripts_data();
175 void importScripts();
176 void importCreationContext();
177 void scarceResources();
178 void scarceResources_data();
179 void scarceResources_other();
180 void propertyChangeSlots();
181 void propertyVar_data();
182 void propertyVar();
183 void propertyQJSValue_data();
184 void propertyQJSValue();
185 void propertyVarCpp();
186 void propertyVarOwnership();
187 void propertyVarImplicitOwnership();
188 void propertyVarReparent();
189 void propertyVarReparentNullContext();
190 void propertyVarCircular();
191 void propertyVarCircular2();
192 void propertyVarInheritance();
193 void propertyVarInheritance2();
194 void elementAssign();
195 void objectPassThroughSignals();
196 void objectConversion();
197 void booleanConversion();
198 void handleReferenceManagement();
199 void stringArg();
200 void readonlyDeclaration();
201 void sequenceConversionRead();
202 void sequenceConversionWrite();
203 void sequenceConversionArray();
204 void sequenceConversionIndexes();
205 void sequenceConversionThreads();
206 void sequenceConversionBindings();
207 void sequenceConversionCopy();
208 void assignSequenceTypes();
209 void sequenceSort_data();
210 void sequenceSort();
211 void dateParse();
212 void utcDate();
213 void negativeYear();
214 void qtbug_22464();
215 void qtbug_21580();
216 void singleV8BindingDestroyedDuringEvaluation();
217 void bug1();
218#ifndef QT_NO_WIDGETS
219 void bug2();
220#endif
221 void dynamicCreationCrash();
222 void dynamicCreationOwnership();
223 void regExpBug();
224 void nullObjectBinding();
225 void nullObjectInitializer();
226 void deletedEngine();
227 void libraryScriptAssert();
228 void variantsAssignedUndefined();
229 void variants();
230 void qtbug_9792();
231 void qtcreatorbug_1289();
232 void noSpuriousWarningsAtShutdown();
233 void canAssignNullToQObject();
234 void functionAssignment_fromBinding();
235 void functionAssignment_fromJS();
236 void functionAssignment_fromJS_data();
237 void functionAssignmentfromJS_invalid();
238 void functionAssignment_afterBinding();
239 void eval();
240 void function();
241 void topLevelGeneratorFunction();
242 void generatorCrashNewProperty();
243 void generatorCallsGC();
244 void qtbug_10696();
245 void qtbug_11606();
246 void qtbug_11600();
247 void qtbug_21864();
248 void qobjectConnectionListExceptionHandling();
249 void nonscriptable();
250 void deleteLater();
251 void objectNameChangedSignal();
252 void destroyedSignal();
253 void in();
254 void typeOf();
255 void qtbug_24448();
256 void sharedAttachedObject();
257 void objectName();
258 void writeRemovesBinding();
259 void aliasBindingsAssignCorrectly();
260 void aliasBindingsOverrideTarget();
261 void aliasWritesOverrideBindings();
262 void aliasToCompositeElement();
263 void realToInt();
264 void urlProperty();
265 void urlPropertyWithEncoding();
266 void urlListPropertyWithEncoding();
267 void dynamicString();
268 void include();
269 void includeRemoteSuccess();
270 void signalHandlers();
271 void qtbug_37351();
272 void doubleEvaluate();
273 void forInLoop();
274 void nonNotifyable();
275 void nonNotifyableConstant();
276 void deleteWhileBindingRunning();
277 void callQtInvokables();
278 void resolveClashingProperties();
279 void invokableObjectArg();
280 void invokableObjectRet();
281 void invokableEnumRet();
282 void qtbug_20344();
283 void qtbug_22679();
284 void qtbug_22843_data();
285 void qtbug_22843();
286 void rewriteMultiLineStrings();
287 void revisionErrors();
288 void revision();
289 void invokableWithQObjectDerived();
290 void realTypePrecision();
291 void registeredFlagMethod();
292 void deleteLaterObjectMethodCall();
293 void automaticSemicolon();
294 void compatibilitySemicolon();
295 void incrDecrSemicolon1();
296 void incrDecrSemicolon2();
297 void incrDecrSemicolon_error1();
298 void unaryExpression();
299 void switchStatement();
300 void withStatement();
301 void tryStatement();
302 void replaceBinding();
303 void bindingBoundFunctions();
304 void deleteRootObjectInCreation();
305 void onDestruction();
306 void onDestructionViaGC();
307 void bindingSuppression();
308 void signalEmitted();
309 void threadSignal();
310 void qqmldataDestroyed();
311 void secondAlias();
312 void varAlias();
313 void overrideDataAssert();
314 void fallbackBindings_data();
315 void fallbackBindings();
316 void propertyOverride();
317 void concatenatedStringPropertyAccess();
318 void jsOwnedObjectsDeletedOnEngineDestroy();
319 void updateCall();
320 void numberParsing();
321 void stringParsing();
322 void push_and_shift();
323 void qtbug_32801();
324 void thisObject();
325 void qtbug_33754();
326 void qtbug_34493();
327 void singletonFromQMLToCpp();
328 void singletonFromQMLAndBackAndCompare();
329 void setPropertyOnInvalid();
330 void miscTypeTest();
331 void stackLimits();
332 void idsAsLValues();
333 void qtbug_34792();
334 void noCaptureWhenWritingProperty();
335 void singletonWithEnum();
336 void lazyBindingEvaluation();
337 void varPropertyAccessOnObjectWithInvalidContext();
338 void importedScriptsAccessOnObjectWithInvalidContext();
339 void importedScriptsWithoutQmlMode();
340 void contextObjectOnLazyBindings();
341 void garbageCollectionDuringCreation();
342 void qtbug_39520();
343 void readUnregisteredQObjectProperty();
344 void writeUnregisteredQObjectProperty();
345 void switchExpression();
346 void qtbug_46022();
347 void qtbug_52340();
348 void qtbug_54589();
349 void qtbug_54687();
350 void stringify_qtbug_50592();
351 void instanceof_data();
352 void instanceof();
353 void constkw_data();
354 void constkw();
355 void redefineGlobalProp();
356 void freeze_empty_object();
357 void singleBlockLoops();
358 void qtbug_60547();
359 void delayLoadingArgs();
360 void manyArguments();
361 void forInIterator();
362 void localForInIterator();
363 void shadowedFunctionName();
364 void anotherNaN();
365 void callPropertyOnUndefined();
366 void jumpStrictNotEqualUndefined();
367 void removeBindingsWithNoDependencies();
368 void preserveBindingWithUnresolvedNames();
369 void temporaryDeadZone();
370 void importLexicalVariables_data();
371 void importLexicalVariables();
372 void hugeObject();
373 void templateStringTerminator();
374 void arrayAndException();
375 void numberToStringWithRadix();
376 void tailCallWithArguments();
377 void deleteSparseInIteration();
378 void saveAccumulatorBeforeToInt32();
379 void intMinDividedByMinusOne();
380 void undefinedPropertiesInObjectWrapper();
381 void hugeRegexpQuantifiers();
382 void singletonTypeWrapperLookup();
383 void getThisObject();
384 void semicolonAfterProperty();
385 void hugeStack();
386 void variantConversionMethod();
387 void proxyIteration();
388 void proxyHandlerTraps();
389 void gcCrashRegressionTest();
390 void functionAsDefaultArgument();
391
392private:
393// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
394 static void verifyContextLifetime(QQmlContextData *ctxt);
395
396 // When calling into JavaScript, the specific type of the return value can differ if that return
397 // value is a number. This is not only the case for non-integral numbers, or numbers that do not
398 // fit into the (signed) integer range, but it also depends on which optimizations are run. So,
399 // to check if the return value is of a number type, use this method instead of checking against
400 // a specific userType.
401 static bool isJSNumberType(int userType)
402 {
403 return userType == (int) QVariant::Int
404 || userType == (int) QVariant::UInt
405 || userType == (int) QVariant::Double;
406 }
407};
408
409static void gc(QQmlEngine &engine)
410{
411 engine.collectGarbage();
412 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
413 QCoreApplication::processEvents();
414}
415
416
417void tst_qqmlecmascript::initTestCase()
418{
419 QQmlDataTest::initTestCase();
420 registerTypes();
421}
422
423void tst_qqmlecmascript::arrayIncludesValueType()
424{
425 QQmlEngine engine;
426 QQmlComponent component(&engine);
427 // It is vital that QtQuick is imported below else we get a warning about
428 // QQml_colorProvider and tst_qqmlecmascript::signalParameterTypes fails due
429 // to some static variable being initialized with the wrong value
430 component.setData(R"(
431 import QtQuick 2.15
432 import QtQml 2.15
433 QtObject {
434 id: root
435 property color r: Qt.rgba(1, 0, 0)
436 property color g: Qt.rgba(0, 1, 0)
437 property color b: Qt.rgba(0, 0, 1)
438 property var colors: [r, g, b]
439 property bool success: false
440
441 Component.onCompleted: {
442 root.success = root.colors.includes(root.g)
443 }
444 }
445 )", baseUrl: QUrl("testData"));
446 QScopedPointer<QObject> o(component.create());
447 QVERIFY(o);
448 auto success = o->property(name: "success");
449 QVERIFY(success.isValid());
450 QVERIFY(success.toBool());
451}
452
453void tst_qqmlecmascript::assignBasicTypes()
454{
455 QQmlEngine engine;
456 {
457 QQmlComponent component(&engine, testFileUrl(fileName: "assignBasicTypes.qml"));
458 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
459 QVERIFY(object != nullptr);
460 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
461 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
462 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
463 QCOMPARE(object->stringProperty(), QString("Hello World!"));
464 QCOMPARE(object->uintProperty(), uint(10));
465 QCOMPARE(object->intProperty(), -19);
466 QCOMPARE((float)object->realProperty(), float(23.2));
467 QCOMPARE((float)object->doubleProperty(), float(-19.75));
468 QCOMPARE((float)object->floatProperty(), float(8.5));
469 QCOMPARE(object->colorProperty(), QColor("red"));
470 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
471 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
472 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
473 QCOMPARE(object->pointProperty(), QPoint(99,13));
474 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
475 QCOMPARE(object->sizeProperty(), QSize(99, 13));
476 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
477 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
478 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
479 QCOMPARE(object->boolProperty(), true);
480 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
481 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2f));
482 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
483 delete object;
484 }
485 {
486 QQmlComponent component(&engine, testFileUrl(fileName: "assignBasicTypes.2.qml"));
487 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: component.create());
488 QVERIFY(object != nullptr);
489 QCOMPARE(object->flagProperty(), MyTypeObject::FlagVal1 | MyTypeObject::FlagVal3);
490 QCOMPARE(object->enumProperty(), MyTypeObject::EnumVal2);
491 QCOMPARE(object->relatedEnumProperty(), MyEnumContainer::RelatedValue);
492 QCOMPARE(object->stringProperty(), QString("Hello World!"));
493 QCOMPARE(object->uintProperty(), uint(10));
494 QCOMPARE(object->intProperty(), -19);
495 QCOMPARE((float)object->realProperty(), float(23.2));
496 QCOMPARE((float)object->doubleProperty(), float(-19.75));
497 QCOMPARE((float)object->floatProperty(), float(8.5));
498 QCOMPARE(object->colorProperty(), QColor("red"));
499 QCOMPARE(object->dateProperty(), QDate(1982, 11, 25));
500 QCOMPARE(object->timeProperty(), QTime(11, 11, 32));
501 QCOMPARE(object->dateTimeProperty(), QDateTime(QDate(2009, 5, 12), QTime(13, 22, 1), Qt::UTC));
502 QCOMPARE(object->pointProperty(), QPoint(99,13));
503 QCOMPARE(object->pointFProperty(), QPointF(-10.1, 12.3));
504 QCOMPARE(object->sizeProperty(), QSize(99, 13));
505 QCOMPARE(object->sizeFProperty(), QSizeF(0.1, 0.2));
506 QCOMPARE(object->rectProperty(), QRect(9, 7, 100, 200));
507 QCOMPARE(object->rectFProperty(), QRectF(1000.1, -10.9, 400, 90.99));
508 QCOMPARE(object->boolProperty(), true);
509 QCOMPARE(object->variantProperty(), QVariant("Hello World!"));
510 QCOMPARE(object->vectorProperty(), QVector3D(10, 1, 2.2f));
511 QCOMPARE(object->urlProperty(), component.url().resolved(QUrl("main.qml")));
512 delete object;
513 }
514}
515
516void tst_qqmlecmascript::assignDate_data()
517{
518 QTest::addColumn<QUrl>(name: "source");
519 QTest::addColumn<int>(name: "timeOffset"); // -1 for local-time, else minutes from UTC
520
521 QTest::newRow(dataTag: "Component.onComplete JS Parse") << testFileUrl(fileName: "assignDate.qml") << -1;
522 QTest::newRow(dataTag: "Component.onComplete JS") << testFileUrl(fileName: "assignDate.1.qml") << 0;
523 QTest::newRow(dataTag: "Binding JS") << testFileUrl(fileName: "assignDate.2.qml") << -1;
524 QTest::newRow(dataTag: "Binding UTC") << testFileUrl(fileName: "assignDate.3.qml") << 0;
525 QTest::newRow(dataTag: "Binding JS UTC") << testFileUrl(fileName: "assignDate.4.qml") << 0;
526 QTest::newRow(dataTag: "Binding UTC+2") << testFileUrl(fileName: "assignDate.5.qml") << 120;
527 QTest::newRow(dataTag: "Binding JS UTC+2 ") << testFileUrl(fileName: "assignDate.6.qml") << 120;
528}
529
530void tst_qqmlecmascript::assignDate()
531{
532 QFETCH(QUrl, source);
533 QFETCH(int, timeOffset);
534
535 QQmlEngine engine;
536 QQmlComponent component(&engine, source);
537 QScopedPointer<QObject> obj(component.create());
538 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: obj.data());
539 QVERIFY(object != nullptr);
540
541 QDate expectedDate(2009, 5, 12);
542 QDateTime expectedDateTime;
543 QDateTime expectedDateTime2;
544 if (timeOffset == -1) {
545 expectedDateTime = QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::LocalTime);
546 expectedDateTime2 = QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::LocalTime);
547 } else {
548 expectedDateTime = QDateTime(QDate(2009, 5, 12), QTime(0, 0, 1), Qt::OffsetFromUTC, timeOffset * 60);
549 expectedDateTime2 = QDateTime(QDate(2009, 5, 12), QTime(23, 59, 59), Qt::OffsetFromUTC, timeOffset * 60);
550 }
551
552 QCOMPARE(object->dateProperty(), expectedDate);
553 QCOMPARE(object->dateTimeProperty(), expectedDateTime);
554 QCOMPARE(object->dateTimeProperty2(), expectedDateTime2);
555 QCOMPARE(object->boolProperty(), true);
556}
557
558void tst_qqmlecmascript::exportDate_data()
559{
560 QTest::addColumn<QUrl>(name: "source");
561 QTest::addColumn<QDateTime>(name: "datetime");
562
563 // Verify that we can export datetime information to QML and that consumers can access
564 // the data correctly provided they know the TZ info associated with the value
565
566 const QDate date(2009, 5, 12);
567 const QTime early(0, 0, 1);
568 const QTime late(23, 59, 59);
569 const int offset(((11 * 60) + 30) * 60);
570
571 QTest::newRow(dataTag: "Localtime early") << testFileUrl(fileName: "exportDate.qml") << QDateTime(date, early, Qt::LocalTime);
572 QTest::newRow(dataTag: "Localtime late") << testFileUrl(fileName: "exportDate.2.qml") << QDateTime(date, late, Qt::LocalTime);
573 QTest::newRow(dataTag: "UTC early") << testFileUrl(fileName: "exportDate.3.qml") << QDateTime(date, early, Qt::UTC);
574 QTest::newRow(dataTag: "UTC late") << testFileUrl(fileName: "exportDate.4.qml") << QDateTime(date, late, Qt::UTC);
575 {
576 QDateTime dt(date, early, Qt::OffsetFromUTC);
577 dt.setOffsetFromUtc(offset);
578 QTest::newRow(dataTag: "+11:30 early") << testFileUrl(fileName: "exportDate.5.qml") << dt;
579 }
580 {
581 QDateTime dt(date, late, Qt::OffsetFromUTC);
582 dt.setOffsetFromUtc(offset);
583 QTest::newRow(dataTag: "+11:30 late") << testFileUrl(fileName: "exportDate.6.qml") << dt;
584 }
585 {
586 QDateTime dt(date, early, Qt::OffsetFromUTC);
587 dt.setOffsetFromUtc(-offset);
588 QTest::newRow(dataTag: "-11:30 early") << testFileUrl(fileName: "exportDate.7.qml") << dt;
589 }
590 {
591 QDateTime dt(date, late, Qt::OffsetFromUTC);
592 dt.setOffsetFromUtc(-offset);
593 QTest::newRow(dataTag: "-11:30 late") << testFileUrl(fileName: "exportDate.8.qml") << dt;
594 }
595}
596
597void tst_qqmlecmascript::exportDate()
598{
599 QFETCH(QUrl, source);
600 QFETCH(QDateTime, datetime);
601
602 DateTimeExporter exporter(datetime);
603
604 QQmlEngine e;
605 e.rootContext()->setContextProperty("datetimeExporter", &exporter);
606
607 QQmlComponent component(&e, source);
608 QScopedPointer<QObject> obj(component.create());
609 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: obj.data());
610 QVERIFY(object != nullptr);
611 QCOMPARE(object->boolProperty(), true);
612}
613
614void tst_qqmlecmascript::checkDate_data()
615{
616 QTest::addColumn<QUrl>(name: "source");
617 QTest::addColumn<QDate>(name: "date");
618 // NB: JavaScript month-indices are Jan = 0 to Dec = 11; QDate's are Jan = 1 to Dec = 12.
619 QTest::newRow(dataTag: "denormal-March")
620 << testFileUrl(fileName: "checkDate-denormal-March.qml")
621 << QDate(2019, 3, 1);
622 QTest::newRow(dataTag: "denormal-leap")
623 << testFileUrl(fileName: "checkDate-denormal-leap.qml")
624 << QDate(2020, 2, 29);
625 QTest::newRow(dataTag: "denormal-Feb")
626 << testFileUrl(fileName: "checkDate-denormal-Feb.qml")
627 << QDate(2019, 2, 28);
628 QTest::newRow(dataTag: "denormal-year")
629 << testFileUrl(fileName: "checkDate-denormal-year.qml")
630 << QDate(2019, 12, 31);
631 QTest::newRow(dataTag: "denormal-wrap")
632 << testFileUrl(fileName: "checkDate-denormal-wrap.qml")
633 << QDate(2020, 2, 29);
634 QTest::newRow(dataTag: "October")
635 << testFileUrl(fileName: "checkDate-October.qml")
636 << QDate(2019, 10, 3);
637}
638
639void tst_qqmlecmascript::checkDate()
640{
641 QFETCH(const QUrl, source);
642 QFETCH(const QDate, date);
643 QQmlEngine e;
644 QQmlComponent component(&e, source);
645 QScopedPointer<QObject> obj(component.create());
646 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: obj.data());
647 QVERIFY(object != nullptr);
648 QCOMPARE(object->dateProperty(), date);
649 QVERIFY(object->boolProperty());
650}
651
652void tst_qqmlecmascript::checkDateTime_data()
653{
654 QTest::addColumn<QUrl>(name: "source");
655 QTest::addColumn<QDateTime>(name: "when");
656 // NB: JavaScript month-indices are Jan = 0 to Dec = 11; QDate's are Jan = 1 to Dec = 12.
657 QTest::newRow(dataTag: "denormal-March")
658 << testFileUrl(fileName: "checkDateTime-denormal-March.qml")
659 << QDateTime(QDate(2019, 3, 1), QTime(0, 0, 0, 1), Qt::LocalTime);
660 QTest::newRow(dataTag: "denormal-leap")
661 << testFileUrl(fileName: "checkDateTime-denormal-leap.qml")
662 << QDateTime(QDate(2020, 2, 29), QTime(23, 59, 59, 999), Qt::LocalTime);
663 QTest::newRow(dataTag: "denormal-hours")
664 << testFileUrl(fileName: "checkDateTime-denormal-hours.qml")
665 << QDateTime(QDate(2020, 2, 29), QTime(0, 0), Qt::LocalTime);
666 QTest::newRow(dataTag: "denormal-minutes")
667 << testFileUrl(fileName: "checkDateTime-denormal-minutes.qml")
668 << QDateTime(QDate(2020, 2, 29), QTime(0, 0), Qt::LocalTime);
669 QTest::newRow(dataTag: "denormal-seconds")
670 << testFileUrl(fileName: "checkDateTime-denormal-seconds.qml")
671 << QDateTime(QDate(2020, 2, 29), QTime(0, 0), Qt::LocalTime);
672 QTest::newRow(dataTag: "October")
673 << testFileUrl(fileName: "checkDateTime-October.qml")
674 << QDateTime(QDate(2019, 10, 3), QTime(12, 0), Qt::LocalTime);
675}
676
677void tst_qqmlecmascript::checkDateTime()
678{
679 QFETCH(const QUrl, source);
680 QFETCH(const QDateTime, when);
681 QQmlEngine e;
682 QQmlComponent component(&e, source);
683 QScopedPointer<QObject> obj(component.create());
684 MyTypeObject *object = qobject_cast<MyTypeObject *>(object: obj.data());
685 QVERIFY(object != nullptr);
686 QCOMPARE(object->dateTimeProperty(), when);
687 QVERIFY(object->boolProperty());
688}
689
690void tst_qqmlecmascript::idShortcutInvalidates()
691{
692 QQmlEngine engine;
693 {
694 QQmlComponent component(&engine, testFileUrl(fileName: "idShortcutInvalidates.qml"));
695 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
696 QVERIFY(object != nullptr);
697 QVERIFY(object->objectProperty() != nullptr);
698 delete object->objectProperty();
699 QVERIFY(!object->objectProperty());
700 delete object;
701 }
702
703 {
704 QQmlComponent component(&engine, testFileUrl(fileName: "idShortcutInvalidates.1.qml"));
705 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
706 QVERIFY(object != nullptr);
707 QVERIFY(object->objectProperty() != nullptr);
708 delete object->objectProperty();
709 QVERIFY(!object->objectProperty());
710 delete object;
711 }
712}
713
714void tst_qqmlecmascript::boolPropertiesEvaluateAsBool()
715{
716 QQmlEngine engine;
717 {
718 QQmlComponent component(&engine, testFileUrl(fileName: "boolPropertiesEvaluateAsBool.1.qml"));
719 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
720 QVERIFY(object != nullptr);
721 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
722 delete object;
723 }
724 {
725 QQmlComponent component(&engine, testFileUrl(fileName: "boolPropertiesEvaluateAsBool.2.qml"));
726 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
727 QVERIFY(object != nullptr);
728 QCOMPARE(object->stringProperty(), QLatin1String("pass"));
729 delete object;
730 }
731}
732
733void tst_qqmlecmascript::signalAssignment()
734{
735 QQmlEngine engine;
736 {
737 QQmlComponent component(&engine, testFileUrl(fileName: "signalAssignment.1.qml"));
738 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
739 QVERIFY(object != nullptr);
740 QCOMPARE(object->string(), QString());
741 emit object->basicSignal();
742 QCOMPARE(object->string(), QString("pass"));
743 delete object;
744 }
745
746 {
747 QQmlComponent component(&engine, testFileUrl(fileName: "signalAssignment.2.qml"));
748 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
749 QVERIFY(object != nullptr);
750 QCOMPARE(object->string(), QString());
751 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
752 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
753 delete object;
754 }
755
756 {
757 QQmlComponent component(&engine, testFileUrl(fileName: "signalAssignment.3.qml"));
758 QVERIFY(component.isError());
759 QString expectedErrorString = component.url().toString() + QLatin1String(":4 Signal uses unnamed parameter followed by named parameter.\n");
760 QCOMPARE(component.errorString(), expectedErrorString);
761 }
762
763 {
764 QQmlComponent component(&engine, testFileUrl(fileName: "signalAssignment.4.qml"));
765 QVERIFY(component.isError());
766 QString expectedErrorString = component.url().toString() + QLatin1String(":5 Signal parameter \"parseInt\" hides global variable.\n");
767 QCOMPARE(component.errorString(), expectedErrorString);
768 }
769}
770
771void tst_qqmlecmascript::signalArguments()
772{
773 QQmlEngine engine;
774 {
775 QQmlComponent component(&engine, testFileUrl(fileName: "signalArguments.1.qml"));
776 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
777 QVERIFY(object != nullptr);
778 QCOMPARE(object->string(), QString());
779 emit object->basicSignal();
780 QCOMPARE(object->string(), QString("pass"));
781 QCOMPARE(object->property("argumentCount").toInt(), 0);
782 delete object;
783 }
784
785 {
786 QQmlComponent component(&engine, testFileUrl(fileName: "signalArguments.2.qml"));
787 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
788 QVERIFY(object != nullptr);
789 QCOMPARE(object->string(), QString());
790 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
791 QCOMPARE(object->string(), QString("pass 19 Hello world! 10.25 3 2"));
792 QCOMPARE(object->property("argumentCount").toInt(), 5);
793 delete object;
794 }
795}
796
797void tst_qqmlecmascript::methods()
798{
799 QQmlEngine engine;
800 {
801 QQmlComponent component(&engine, testFileUrl(fileName: "methods.1.qml"));
802 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
803 QVERIFY(object != nullptr);
804 QCOMPARE(object->methodCalled(), false);
805 QCOMPARE(object->methodIntCalled(), false);
806 emit object->basicSignal();
807 QCOMPARE(object->methodCalled(), true);
808 QCOMPARE(object->methodIntCalled(), false);
809 delete object;
810 }
811
812 {
813 QQmlComponent component(&engine, testFileUrl(fileName: "methods.2.qml"));
814 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
815 QVERIFY(object != nullptr);
816 QCOMPARE(object->methodCalled(), false);
817 QCOMPARE(object->methodIntCalled(), false);
818 emit object->basicSignal();
819 QCOMPARE(object->methodCalled(), false);
820 QCOMPARE(object->methodIntCalled(), true);
821 delete object;
822 }
823
824 {
825 QQmlComponent component(&engine, testFileUrl(fileName: "methods.3.qml"));
826 QObject *object = component.create();
827 QVERIFY(object != nullptr);
828 QCOMPARE(object->property("test").toInt(), 19);
829 delete object;
830 }
831
832 {
833 QQmlComponent component(&engine, testFileUrl(fileName: "methods.4.qml"));
834 QObject *object = component.create();
835 QVERIFY(object != nullptr);
836 QCOMPARE(object->property("test").toInt(), 19);
837 QCOMPARE(object->property("test2").toInt(), 17);
838 QCOMPARE(object->property("test3").toInt(), 16);
839 delete object;
840 }
841
842 {
843 QQmlComponent component(&engine, testFileUrl(fileName: "methods.5.qml"));
844 QObject *object = component.create();
845 QVERIFY(object != nullptr);
846 QCOMPARE(object->property("test").toInt(), 9);
847 delete object;
848 }
849}
850
851void tst_qqmlecmascript::bindingLoop()
852{
853 QQmlEngine engine;
854 QQmlComponent component(&engine, testFileUrl(fileName: "bindingLoop.qml"));
855 QString warning = component.url().toString() + ":9:9: QML MyQmlObject: Binding loop detected for property \"stringProperty\"";
856 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
857 QObject *object = component.create();
858 QVERIFY(object != nullptr);
859 delete object;
860}
861
862void tst_qqmlecmascript::basicExpressions_data()
863{
864 QTest::addColumn<QString>(name: "expression");
865 QTest::addColumn<QVariant>(name: "result");
866 QTest::addColumn<bool>(name: "nest");
867
868 QTest::newRow(dataTag: "Syntax error (self test)") << "{console.log({'a':1'}.a)}" << QVariant() << false;
869 QTest::newRow(dataTag: "Context property") << "a" << QVariant(1944) << false;
870 QTest::newRow(dataTag: "Context property") << "a" << QVariant(1944) << true;
871 QTest::newRow(dataTag: "Context property expression") << "a * 2" << QVariant(3888) << false;
872 QTest::newRow(dataTag: "Context property expression") << "a * 2" << QVariant(3888) << true;
873 QTest::newRow(dataTag: "Overridden context property") << "b" << QVariant("Milk") << false;
874 QTest::newRow(dataTag: "Overridden context property") << "b" << QVariant("Cow") << true;
875 QTest::newRow(dataTag: "Object property") << "object.stringProperty" << QVariant("Object1") << false;
876 QTest::newRow(dataTag: "Object property") << "object.stringProperty" << QVariant("Object1") << true;
877 QTest::newRow(dataTag: "Overridden object property") << "objectOverride.stringProperty" << QVariant("Object2") << false;
878 QTest::newRow(dataTag: "Overridden object property") << "objectOverride.stringProperty" << QVariant("Object3") << true;
879 QTest::newRow(dataTag: "Default object property") << "horseLegs" << QVariant(4) << false;
880 QTest::newRow(dataTag: "Default object property") << "antLegs" << QVariant(6) << false;
881 QTest::newRow(dataTag: "Default object property") << "emuLegs" << QVariant(2) << false;
882 QTest::newRow(dataTag: "Nested default object property") << "horseLegs" << QVariant(4) << true;
883 QTest::newRow(dataTag: "Nested default object property") << "antLegs" << QVariant(7) << true;
884 QTest::newRow(dataTag: "Nested default object property") << "emuLegs" << QVariant(2) << true;
885 QTest::newRow(dataTag: "Nested default object property") << "humanLegs" << QVariant(2) << true;
886 QTest::newRow(dataTag: "Context property override default object property") << "millipedeLegs" << QVariant(100) << true;
887}
888
889void tst_qqmlecmascript::basicExpressions()
890{
891 QFETCH(QString, expression);
892 QFETCH(QVariant, result);
893 QFETCH(bool, nest);
894
895 QQmlEngine engine;
896
897 MyQmlObject object1;
898 MyQmlObject object2;
899 MyQmlObject object3;
900 MyDefaultObject1 default1;
901 MyDefaultObject3 default3;
902 object1.setStringProperty("Object1");
903 object2.setStringProperty("Object2");
904 object3.setStringProperty("Object3");
905
906 QQmlContext context(engine.rootContext());
907 QQmlContext nestedContext(&context);
908
909 context.setContextObject(&default1);
910 context.setContextProperty("a", QVariant(1944));
911 context.setContextProperty("b", QVariant("Milk"));
912 context.setContextProperty("object", &object1);
913 context.setContextProperty("objectOverride", &object2);
914 nestedContext.setContextObject(&default3);
915 nestedContext.setContextProperty("b", QVariant("Cow"));
916 nestedContext.setContextProperty("objectOverride", &object3);
917 nestedContext.setContextProperty("millipedeLegs", QVariant(100));
918
919 MyExpression expr(nest?&nestedContext:&context, expression);
920 QCOMPARE(expr.evaluate(), result);
921}
922
923void tst_qqmlecmascript::arrayExpressions()
924{
925 QObject obj1;
926 QObject obj2;
927 QObject obj3;
928
929 QQmlEngine engine;
930 QQmlContext context(engine.rootContext());
931 context.setContextProperty("a", &obj1);
932 context.setContextProperty("b", &obj2);
933 context.setContextProperty("c", &obj3);
934
935 MyExpression expr(&context, "[a, b, c, 10]");
936 QVariant result = expr.evaluate();
937 QCOMPARE(result.userType(), qMetaTypeId<QJSValue>());
938 QJSValue list = qvariant_cast<QJSValue>(v: result);
939 QCOMPARE(list.property("length").toInt(), 4);
940 QCOMPARE(list.property(0).toQObject(), &obj1);
941 QCOMPARE(list.property(1).toQObject(), &obj2);
942 QCOMPARE(list.property(2).toQObject(), &obj3);
943 QCOMPARE(list.property(3).toInt(), 10);
944}
945
946// Tests that modifying a context property will reevaluate expressions
947void tst_qqmlecmascript::contextPropertiesTriggerReeval()
948{
949 QQmlEngine engine;
950 QQmlContext context(engine.rootContext());
951 MyQmlObject object1;
952 MyQmlObject object2;
953 MyQmlObject *object3 = new MyQmlObject;
954
955 object1.setStringProperty("Hello");
956 object2.setStringProperty("World");
957
958 context.setContextProperty("testProp", QVariant(1));
959 context.setContextProperty("testObj", &object1);
960 context.setContextProperty("testObj2", object3);
961
962 {
963 MyExpression expr(&context, "testProp + 1");
964 QCOMPARE(expr.changed, false);
965 QCOMPARE(expr.evaluate(), QVariant(2));
966
967 context.setContextProperty("testProp", QVariant(2));
968 QCOMPARE(expr.changed, true);
969 QCOMPARE(expr.evaluate(), QVariant(3));
970 }
971
972 {
973 MyExpression expr(&context, "testProp + testProp + testProp");
974 QCOMPARE(expr.changed, false);
975 QCOMPARE(expr.evaluate(), QVariant(6));
976
977 context.setContextProperty("testProp", QVariant(4));
978 QCOMPARE(expr.changed, true);
979 QCOMPARE(expr.evaluate(), QVariant(12));
980 }
981
982 {
983 MyExpression expr(&context, "testObj.stringProperty");
984 QCOMPARE(expr.changed, false);
985 QCOMPARE(expr.evaluate(), QVariant("Hello"));
986
987 context.setContextProperty("testObj", &object2);
988 QCOMPARE(expr.changed, true);
989 QCOMPARE(expr.evaluate(), QVariant("World"));
990 }
991
992 {
993 MyExpression expr(&context, "testObj.stringProperty /**/");
994 QCOMPARE(expr.changed, false);
995 QCOMPARE(expr.evaluate(), QVariant("World"));
996
997 context.setContextProperty("testObj", &object1);
998 QCOMPARE(expr.changed, true);
999 QCOMPARE(expr.evaluate(), QVariant("Hello"));
1000 }
1001
1002 {
1003 MyExpression expr(&context, "testObj2");
1004 QCOMPARE(expr.changed, false);
1005 QCOMPARE(expr.evaluate(), QVariant::fromValue((QObject *)object3));
1006 }
1007
1008 delete object3;
1009}
1010
1011void tst_qqmlecmascript::objectPropertiesTriggerReeval()
1012{
1013 QQmlEngine engine;
1014 QQmlContext context(engine.rootContext());
1015 MyQmlObject object1;
1016 MyQmlObject object2;
1017 MyQmlObject object3;
1018 context.setContextProperty("testObj", &object1);
1019
1020 object1.setStringProperty(QLatin1String("Hello"));
1021 object2.setStringProperty(QLatin1String("Dog"));
1022 object3.setStringProperty(QLatin1String("Cat"));
1023
1024 {
1025 MyExpression expr(&context, "testObj.stringProperty");
1026 QCOMPARE(expr.changed, false);
1027 QCOMPARE(expr.evaluate(), QVariant("Hello"));
1028
1029 object1.setStringProperty(QLatin1String("World"));
1030 QCOMPARE(expr.changed, true);
1031 QCOMPARE(expr.evaluate(), QVariant("World"));
1032 }
1033
1034 {
1035 MyExpression expr(&context, "testObj.objectProperty.stringProperty");
1036 QCOMPARE(expr.changed, false);
1037 QCOMPARE(expr.evaluate(), QVariant());
1038
1039 object1.setObjectProperty(&object2);
1040 QCOMPARE(expr.changed, true);
1041 expr.changed = false;
1042 QCOMPARE(expr.evaluate(), QVariant("Dog"));
1043
1044 object1.setObjectProperty(&object3);
1045 QCOMPARE(expr.changed, true);
1046 expr.changed = false;
1047 QCOMPARE(expr.evaluate(), QVariant("Cat"));
1048
1049 object1.setObjectProperty(nullptr);
1050 QCOMPARE(expr.changed, true);
1051 expr.changed = false;
1052 QCOMPARE(expr.evaluate(), QVariant());
1053
1054 object1.setObjectProperty(&object3);
1055 QCOMPARE(expr.changed, true);
1056 expr.changed = false;
1057 QCOMPARE(expr.evaluate(), QVariant("Cat"));
1058
1059 object3.setStringProperty("Donkey");
1060 QCOMPARE(expr.changed, true);
1061 expr.changed = false;
1062 QCOMPARE(expr.evaluate(), QVariant("Donkey"));
1063 }
1064}
1065
1066void tst_qqmlecmascript::dependenciesWithFunctions()
1067{
1068 QQmlEngine engine;
1069 QQmlComponent component(&engine, testFileUrl(fileName: "dependenciesWithFunctions.qml"));
1070
1071 QScopedPointer<QObject> object(component.create());
1072 QVERIFY2(object, qPrintable(component.errorString()));
1073 QVERIFY(!object->property("success").toBool());
1074 object->setProperty(name: "value", value: 42);
1075 QVERIFY(object->property("success").toBool());
1076}
1077
1078void tst_qqmlecmascript::deferredProperties()
1079{
1080 QQmlEngine engine;
1081 QQmlComponent component(&engine, testFileUrl(fileName: "deferredProperties.qml"));
1082 MyDeferredObject *object =
1083 qobject_cast<MyDeferredObject *>(object: component.create());
1084 QVERIFY(object != nullptr);
1085 QCOMPARE(object->value(), 0);
1086 QVERIFY(!object->objectProperty());
1087 QVERIFY(object->objectProperty2() != nullptr);
1088 qmlExecuteDeferred(object);
1089 QCOMPARE(object->value(), 10);
1090 QVERIFY(object->objectProperty() != nullptr);
1091 MyQmlObject *qmlObject =
1092 qobject_cast<MyQmlObject *>(object: object->objectProperty());
1093 QVERIFY(qmlObject != nullptr);
1094 QCOMPARE(qmlObject->value(), 10);
1095 object->setValue(19);
1096 QCOMPARE(qmlObject->value(), 19);
1097
1098 delete object;
1099}
1100
1101// Check errors on deferred properties are correctly emitted
1102void tst_qqmlecmascript::deferredPropertiesErrors()
1103{
1104 QQmlEngine engine;
1105 QQmlComponent component(&engine, testFileUrl(fileName: "deferredPropertiesErrors.qml"));
1106 MyDeferredObject *object =
1107 qobject_cast<MyDeferredObject *>(object: component.create());
1108 QVERIFY(object != nullptr);
1109 QCOMPARE(object->value(), 0);
1110 QVERIFY(!object->objectProperty());
1111 QVERIFY(!object->objectProperty2());
1112
1113 QString warning = component.url().toString() + ":6:5: Unable to assign [undefined] to QObject*";
1114 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
1115
1116 qmlExecuteDeferred(object);
1117
1118 delete object;
1119}
1120
1121void tst_qqmlecmascript::deferredPropertiesInComponents()
1122{
1123 // Test that it works when the property is set inside and outside component
1124 QQmlEngine engine;
1125 QQmlComponent component(&engine, testFileUrl(fileName: "deferredPropertiesInComponents.qml"));
1126 QObject *object = component.create();
1127 if (!object)
1128 qDebug() << component.errorString();
1129 QVERIFY(object != nullptr);
1130 QCOMPARE(object->property("value").value<int>(), 10);
1131
1132 MyDeferredObject *defObjectA =
1133 qobject_cast<MyDeferredObject *>(object: object->property(name: "deferredInside").value<QObject*>());
1134 QVERIFY(defObjectA != nullptr);
1135 QVERIFY(!defObjectA->objectProperty());
1136
1137 qmlExecuteDeferred(defObjectA);
1138 QVERIFY(defObjectA->objectProperty() != nullptr);
1139 QCOMPARE(defObjectA->objectProperty()->property("value").value<int>(), 10);
1140
1141 MyDeferredObject *defObjectB =
1142 qobject_cast<MyDeferredObject *>(object: object->property(name: "deferredOutside").value<QObject*>());
1143 QVERIFY(defObjectB != nullptr);
1144 QVERIFY(!defObjectB->objectProperty());
1145
1146 qmlExecuteDeferred(defObjectB);
1147 QVERIFY(defObjectB->objectProperty() != nullptr);
1148 QCOMPARE(defObjectB->objectProperty()->property("value").value<int>(), 10);
1149
1150 delete object;
1151}
1152
1153void tst_qqmlecmascript::deferredPropertiesInDestruction()
1154{
1155 //Test that the component does not get created at all if creation is deferred until the containing context is destroyed
1156 //Very specific operation ordering is needed for this to occur, currently accessing object from object destructor.
1157 //
1158 QQmlEngine engine;
1159 QQmlComponent component(&engine, testFileUrl(fileName: "deferredPropertiesInDestruction.qml"));
1160 QObject *object = component.create();
1161 if (!object)
1162 qDebug() << component.errorString();
1163 QVERIFY(object != nullptr);
1164 delete object; //QTBUG-33112 was that this used to cause a crash
1165}
1166
1167void tst_qqmlecmascript::extensionObjects()
1168{
1169 QQmlEngine engine;
1170 QQmlComponent component(&engine, testFileUrl(fileName: "extensionObjects.qml"));
1171 MyExtendedObject *object =
1172 qobject_cast<MyExtendedObject *>(object: component.create());
1173 QVERIFY(object != nullptr);
1174 QCOMPARE(object->baseProperty(), 13);
1175 QCOMPARE(object->coreProperty(), 9);
1176 object->setProperty(name: "extendedProperty", value: QVariant(11));
1177 object->setProperty(name: "baseExtendedProperty", value: QVariant(92));
1178 QCOMPARE(object->coreProperty(), 11);
1179 QCOMPARE(object->baseProperty(), 92);
1180
1181 MyExtendedObject *nested = qobject_cast<MyExtendedObject*>(object: qvariant_cast<QObject *>(v: object->property(name: "nested")));
1182 QVERIFY(nested);
1183 QCOMPARE(nested->baseProperty(), 13);
1184 QCOMPARE(nested->coreProperty(), 9);
1185 nested->setProperty(name: "extendedProperty", value: QVariant(11));
1186 nested->setProperty(name: "baseExtendedProperty", value: QVariant(92));
1187 QCOMPARE(nested->coreProperty(), 11);
1188 QCOMPARE(nested->baseProperty(), 92);
1189
1190 delete object;
1191}
1192
1193void tst_qqmlecmascript::overrideExtensionProperties()
1194{
1195 QQmlEngine engine;
1196 QQmlComponent component(&engine, testFileUrl(fileName: "extensionObjectsPropertyOverride.qml"));
1197 OverrideDefaultPropertyObject *object =
1198 qobject_cast<OverrideDefaultPropertyObject *>(object: component.create());
1199 QVERIFY(object != nullptr);
1200 QVERIFY(object->secondProperty() != nullptr);
1201 QVERIFY(!object->firstProperty());
1202
1203 delete object;
1204}
1205
1206void tst_qqmlecmascript::attachedProperties()
1207{
1208 QQmlEngine engine;
1209
1210 {
1211 QQmlComponent component(&engine, testFileUrl(fileName: "attachedProperty.qml"));
1212 QObject *object = component.create();
1213 QVERIFY(object != nullptr);
1214 QCOMPARE(object->property("a").toInt(), 19);
1215 QCOMPARE(object->property("b").toInt(), 19);
1216 QCOMPARE(object->property("c").toInt(), 19);
1217 QCOMPARE(object->property("d").toInt(), 19);
1218 delete object;
1219 }
1220
1221 {
1222 QQmlComponent component(&engine, testFileUrl(fileName: "attachedProperty.2.qml"));
1223 QObject *object = component.create();
1224 QVERIFY(object != nullptr);
1225 QCOMPARE(object->property("a").toInt(), 26);
1226 QCOMPARE(object->property("b").toInt(), 26);
1227 QCOMPARE(object->property("c").toInt(), 26);
1228 QCOMPARE(object->property("d").toInt(), 26);
1229
1230 delete object;
1231 }
1232
1233 {
1234 QQmlComponent component(&engine, testFileUrl(fileName: "writeAttachedProperty.qml"));
1235 QObject *object = component.create();
1236 QVERIFY(object != nullptr);
1237
1238 QMetaObject::invokeMethod(obj: object, member: "writeValue2");
1239
1240 MyQmlAttachedObject *attached =
1241 qobject_cast<MyQmlAttachedObject *>(object: qmlAttachedPropertiesObject<MyQmlObject>(obj: object));
1242 QVERIFY(attached != nullptr);
1243
1244 QCOMPARE(attached->value2(), 9);
1245 delete object;
1246 }
1247}
1248
1249void tst_qqmlecmascript::enums()
1250{
1251 QQmlEngine engine;
1252
1253 // Existent enums
1254 {
1255 QQmlComponent component(&engine, testFileUrl(fileName: "enums.1.qml"));
1256 QObject *object = component.create();
1257 QVERIFY(object != nullptr);
1258
1259 QCOMPARE(object->property("enumProperty").toInt(), (int)MyQmlObject::EnumValue2);
1260 QCOMPARE(object->property("relatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
1261 QCOMPARE(object->property("unrelatedEnumProperty").toInt(), (int)MyEnumContainer::RelatedValue);
1262 QCOMPARE(object->property("qtEnumProperty").toInt(), (int)Qt::CaseInsensitive);
1263 QCOMPARE(object->property("a").toInt(), 0);
1264 QCOMPARE(object->property("b").toInt(), 1);
1265 QCOMPARE(object->property("c").toInt(), 2);
1266 QCOMPARE(object->property("d").toInt(), 3);
1267 QCOMPARE(object->property("e").toInt(), 0);
1268 QCOMPARE(object->property("f").toInt(), 1);
1269 QCOMPARE(object->property("g").toInt(), 2);
1270 QCOMPARE(object->property("h").toInt(), 3);
1271 QCOMPARE(object->property("i").toInt(), 19);
1272 QCOMPARE(object->property("j").toInt(), 19);
1273 QCOMPARE(object->property("k").toInt(), 42);
1274 QCOMPARE(object->property("l").toInt(), 333);
1275
1276 delete object;
1277 }
1278 // Non-existent enums
1279 {
1280 QUrl file = testFileUrl(fileName: "enums.2.qml");
1281 QString w1 = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'MyEnum' for property 'MyUnregisteredEnumTypeObject::enumProperty'");
1282 QString w2 = QLatin1String("QQmlExpression: Expression ") + testFileUrl(fileName: "enums.2.qml").toString() + QLatin1String(":9:5 depends on non-NOTIFYable properties:");
1283 QString w3 = QLatin1String(" MyUnregisteredEnumTypeObject::enumProperty");
1284 QString w4 = file.toString() + ":7:5: Unable to assign [undefined] to int";
1285 QString w5 = file.toString() + ":8:5: Unable to assign [undefined] to int";
1286 QString w6 = file.toString() + ":9:5: Unable to assign [undefined] to int";
1287 QString w7 = file.toString() + ":13:9: Unable to assign [undefined] to [unknown property type]";
1288 QString w8 = file.toString() + ":31:9: Unable to assign int to [unknown property type]";
1289 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w1));
1290 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w2));
1291 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w3));
1292 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w4));
1293 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w5));
1294 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w6));
1295 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w7));
1296 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w8));
1297
1298 QQmlComponent component(&engine, testFileUrl(fileName: "enums.2.qml"));
1299 QObject *object = component.create();
1300 QVERIFY(object != nullptr);
1301 QCOMPARE(object->property("a").toInt(), 0);
1302 QCOMPARE(object->property("b").toInt(), 0);
1303 QCOMPARE(object->property("c").toInt(), 0);
1304
1305 QString w9 = file.toString() + ":18: Error: Cannot assign JavaScript function to [unknown property type]";
1306 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w9));
1307 QMetaObject::invokeMethod(obj: object, member: "testAssignmentOne");
1308
1309 QString w10 = file.toString() + ":21: Error: Cannot assign [undefined] to [unknown property type]";
1310 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w10));
1311 QMetaObject::invokeMethod(obj: object, member: "testAssignmentTwo");
1312
1313 QString w11 = file.toString() + ":24: Error: Cannot assign [undefined] to [unknown property type]";
1314 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w11));
1315 QMetaObject::invokeMethod(obj: object, member: "testAssignmentThree");
1316
1317 QString w12 = file.toString() + ":34: Error: Cannot assign int to an unregistered type";
1318 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w12));
1319 QMetaObject::invokeMethod(obj: object, member: "testAssignmentFour");
1320
1321 delete object;
1322 }
1323 // Enums as literals
1324 {
1325 QQmlComponent component(&engine, testFileUrl(fileName: "enums.3.qml"));
1326 QObject *object = component.create();
1327 QVERIFY(object != nullptr);
1328
1329 // check the values are what we expect
1330 QCOMPARE(object->property("a").toInt(), 4);
1331 QCOMPARE(object->property("b").toInt(), 5);
1332 QCOMPARE(object->property("c").toInt(), 9);
1333 QCOMPARE(object->property("d").toInt(), 13);
1334 QCOMPARE(object->property("e").toInt(), 2);
1335 QCOMPARE(object->property("f").toInt(), 3);
1336 QCOMPARE(object->property("h").toInt(), 2);
1337 QCOMPARE(object->property("i").toInt(), 3);
1338 QCOMPARE(object->property("j").toInt(), -1);
1339 QCOMPARE(object->property("k").toInt(), 42);
1340
1341 // count of change signals
1342 QCOMPARE(object->property("ac").toInt(), 0);
1343 QCOMPARE(object->property("bc").toInt(), 0);
1344 QCOMPARE(object->property("cc").toInt(), 0);
1345 QCOMPARE(object->property("dc").toInt(), 0);
1346 QCOMPARE(object->property("ec").toInt(), 0);
1347 QCOMPARE(object->property("fc").toInt(), 0);
1348 QCOMPARE(object->property("hc").toInt(), 1); // namespace -> binding
1349 QCOMPARE(object->property("ic").toInt(), 1); // namespace -> binding
1350 QCOMPARE(object->property("jc").toInt(), 0);
1351 QCOMPARE(object->property("kc").toInt(), 0);
1352
1353 delete object;
1354 }
1355}
1356
1357void tst_qqmlecmascript::valueTypeFunctions()
1358{
1359 QQmlEngine engine;
1360 QQmlComponent component(&engine, testFileUrl(fileName: "valueTypeFunctions.qml"));
1361 MyTypeObject *obj = qobject_cast<MyTypeObject*>(object: component.create());
1362 QVERIFY(obj != nullptr);
1363 QCOMPARE(obj->rectProperty(), QRect(0,0,100,100));
1364 QCOMPARE(obj->rectFProperty(), QRectF(0,0.5,100,99.5));
1365
1366 delete obj;
1367}
1368
1369/*
1370Tests that writing a constant to a property with a binding on it disables the
1371binding.
1372*/
1373void tst_qqmlecmascript::constantsOverrideBindings()
1374{
1375 QQmlEngine engine;
1376
1377 // From ECMAScript
1378 {
1379 QQmlComponent component(&engine, testFileUrl(fileName: "constantsOverrideBindings.1.qml"));
1380 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
1381 QVERIFY(object != nullptr);
1382
1383 QCOMPARE(object->property("c2").toInt(), 0);
1384 object->setProperty(name: "c1", value: QVariant(9));
1385 QCOMPARE(object->property("c2").toInt(), 9);
1386
1387 emit object->basicSignal();
1388
1389 QCOMPARE(object->property("c2").toInt(), 13);
1390 object->setProperty(name: "c1", value: QVariant(8));
1391 QCOMPARE(object->property("c2").toInt(), 13);
1392
1393 delete object;
1394 }
1395
1396 // During construction
1397 {
1398 QQmlComponent component(&engine, testFileUrl(fileName: "constantsOverrideBindings.2.qml"));
1399 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
1400 QVERIFY(object != nullptr);
1401
1402 QCOMPARE(object->property("c1").toInt(), 0);
1403 QCOMPARE(object->property("c2").toInt(), 10);
1404 object->setProperty(name: "c1", value: QVariant(9));
1405 QCOMPARE(object->property("c1").toInt(), 9);
1406 QCOMPARE(object->property("c2").toInt(), 10);
1407
1408 delete object;
1409 }
1410
1411#if 0
1412 // From C++
1413 {
1414 QQmlComponent component(&engine, testFileUrl("constantsOverrideBindings.3.qml"));
1415 MyQmlObject *object = qobject_cast<MyQmlObject *>(component.create());
1416 QVERIFY(object != 0);
1417
1418 QCOMPARE(object->property("c2").toInt(), 0);
1419 object->setProperty("c1", QVariant(9));
1420 QCOMPARE(object->property("c2").toInt(), 9);
1421
1422 object->setProperty("c2", QVariant(13));
1423 QCOMPARE(object->property("c2").toInt(), 13);
1424 object->setProperty("c1", QVariant(7));
1425 QCOMPARE(object->property("c1").toInt(), 7);
1426 QCOMPARE(object->property("c2").toInt(), 13);
1427
1428 delete object;
1429 }
1430#endif
1431
1432 // Using an alias
1433 {
1434 QQmlComponent component(&engine, testFileUrl(fileName: "constantsOverrideBindings.4.qml"));
1435 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
1436 QVERIFY(object != nullptr);
1437
1438 QCOMPARE(object->property("c1").toInt(), 0);
1439 QCOMPARE(object->property("c3").toInt(), 10);
1440 object->setProperty(name: "c1", value: QVariant(9));
1441 QCOMPARE(object->property("c1").toInt(), 9);
1442 QCOMPARE(object->property("c3").toInt(), 10);
1443
1444 delete object;
1445 }
1446}
1447
1448/*
1449Tests that assigning a binding to a property that already has a binding causes
1450the original binding to be disabled.
1451*/
1452void tst_qqmlecmascript::outerBindingOverridesInnerBinding()
1453{
1454 QQmlEngine engine;
1455 QQmlComponent component(&engine,
1456 testFileUrl(fileName: "outerBindingOverridesInnerBinding.qml"));
1457 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
1458 QVERIFY(object != nullptr);
1459
1460 QCOMPARE(object->property("c1").toInt(), 0);
1461 QCOMPARE(object->property("c2").toInt(), 0);
1462 QCOMPARE(object->property("c3").toInt(), 0);
1463
1464 object->setProperty(name: "c1", value: QVariant(9));
1465 QCOMPARE(object->property("c1").toInt(), 9);
1466 QCOMPARE(object->property("c2").toInt(), 0);
1467 QCOMPARE(object->property("c3").toInt(), 0);
1468
1469 object->setProperty(name: "c3", value: QVariant(8));
1470 QCOMPARE(object->property("c1").toInt(), 9);
1471 QCOMPARE(object->property("c2").toInt(), 8);
1472 QCOMPARE(object->property("c3").toInt(), 8);
1473
1474 delete object;
1475}
1476
1477/*
1478Access a non-existent attached object.
1479
1480Tests for a regression where this used to crash.
1481*/
1482void tst_qqmlecmascript::nonExistentAttachedObject()
1483{
1484 QQmlEngine engine;
1485 QQmlComponent component(&engine, testFileUrl(fileName: "nonExistentAttachedObject.qml"));
1486
1487 QString warning = component.url().toString() + ":4:5: Unable to assign [undefined] to QString";
1488 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
1489
1490 QObject *object = component.create();
1491 QVERIFY(object != nullptr);
1492
1493 delete object;
1494}
1495
1496void tst_qqmlecmascript::scope()
1497{
1498 QQmlEngine engine;
1499
1500 {
1501 QQmlComponent component(&engine, testFileUrl(fileName: "scope.qml"));
1502 QObject *object = component.create();
1503 QVERIFY(object != nullptr);
1504
1505 QCOMPARE(object->property("test1").toInt(), 1);
1506 QCOMPARE(object->property("test2").toInt(), 2);
1507 QCOMPARE(object->property("test3").toString(), QString("1Test"));
1508 QCOMPARE(object->property("test4").toString(), QString("2Test"));
1509 QCOMPARE(object->property("test5").toInt(), 1);
1510 QCOMPARE(object->property("test6").toInt(), 1);
1511 QCOMPARE(object->property("test7").toInt(), 2);
1512 QCOMPARE(object->property("test8").toInt(), 2);
1513 QCOMPARE(object->property("test9").toInt(), 1);
1514 QCOMPARE(object->property("test10").toInt(), 3);
1515
1516 delete object;
1517 }
1518
1519 {
1520 QQmlComponent component(&engine, testFileUrl(fileName: "scope.2.qml"));
1521 QObject *object = component.create();
1522 QVERIFY(object != nullptr);
1523
1524 QCOMPARE(object->property("test1").toInt(), 19);
1525 QCOMPARE(object->property("test2").toInt(), 19);
1526 QCOMPARE(object->property("test3").toInt(), 14);
1527 QCOMPARE(object->property("test4").toInt(), 14);
1528 QCOMPARE(object->property("test5").toInt(), 24);
1529 QCOMPARE(object->property("test6").toInt(), 24);
1530
1531 delete object;
1532 }
1533
1534 {
1535 QQmlComponent component(&engine, testFileUrl(fileName: "scope.3.qml"));
1536 QObject *object = component.create();
1537 QVERIFY(object != nullptr);
1538
1539 QCOMPARE(object->property("test1").toBool(), true);
1540 QEXPECT_FAIL("", "Properties resolvable at compile time come before the global object, which is not 100% compatible with older QML versions", Continue);
1541 QCOMPARE(object->property("test2").toBool(), true);
1542 QCOMPARE(object->property("test3").toBool(), true);
1543
1544 delete object;
1545 }
1546
1547 // Signal argument scope
1548 {
1549 QQmlComponent component(&engine, testFileUrl(fileName: "scope.4.qml"));
1550 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
1551 QVERIFY(object != nullptr);
1552
1553 QCOMPARE(object->property("test").toInt(), 0);
1554 QCOMPARE(object->property("test2").toString(), QString());
1555
1556 emit object->argumentSignal(a: 13, b: "Argument Scope", c: 9, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
1557
1558 QCOMPARE(object->property("test").toInt(), 13);
1559 QCOMPARE(object->property("test2").toString(), QString("Argument Scope"));
1560
1561 delete object;
1562 }
1563
1564 {
1565 QQmlComponent component(&engine, testFileUrl(fileName: "scope.5.qml"));
1566 QObject *object = component.create();
1567 QVERIFY(object != nullptr);
1568
1569 QCOMPARE(object->property("test1").toBool(), true);
1570 QCOMPARE(object->property("test2").toBool(), true);
1571
1572 delete object;
1573 }
1574
1575 {
1576 QQmlComponent component(&engine, testFileUrl(fileName: "scope.6.qml"));
1577 QObject *object = component.create();
1578 QVERIFY(object != nullptr);
1579
1580 QCOMPARE(object->property("test").toBool(), true);
1581
1582 delete object;
1583 }
1584}
1585
1586// In 4.7, non-library javascript files that had no imports shared the imports of their
1587// importing context
1588void tst_qqmlecmascript::importScope()
1589{
1590 QQmlEngine engine;
1591 QQmlComponent component(&engine, testFileUrl(fileName: "importScope.qml"));
1592 QObject *o = component.create();
1593 QVERIFY(o != nullptr);
1594
1595 QCOMPARE(o->property("test").toInt(), 240);
1596
1597 delete o;
1598}
1599
1600/*
1601Tests that "any" type passes through a synthesized signal parameter. This
1602is essentially a test of QQmlMetaType::copy()
1603*/
1604void tst_qqmlecmascript::signalParameterTypes()
1605{
1606 QQmlEngine engine;
1607 QQmlComponent component(&engine, testFileUrl(fileName: "signalParameterTypes.qml"));
1608 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
1609 QVERIFY(object != nullptr);
1610
1611 emit object->basicSignal();
1612
1613 QCOMPARE(object->property("intProperty").toInt(), 10);
1614 QCOMPARE(object->property("realProperty").toReal(), 19.2);
1615 QVERIFY(object->property("colorProperty").value<QColor>() == QColor(255, 255, 0, 255));
1616 QVERIFY(object->property("variantProperty") == QVariant::fromValue(QColor(255, 0, 255, 255)));
1617 QVERIFY(object->property("enumProperty") == MyQmlObject::EnumValue3);
1618 QVERIFY(object->property("qtEnumProperty") == Qt::LeftButton);
1619
1620 emit object->qjsValueEmittingSignal(value: QJSValue());
1621 QVERIFY(object->property("emittedQjsValueWasUndefined").toBool());
1622 emit object->qjsValueEmittingSignal(value: QJSValue(42));
1623 QVERIFY(!object->property("emittedQjsValueWasUndefined").toBool());
1624 QCOMPARE(object->property("emittedQjsValueAsInt").value<int>(), 42);
1625
1626 delete object;
1627}
1628
1629/*
1630Test that two JS objects for the same QObject compare as equal.
1631*/
1632void tst_qqmlecmascript::objectsCompareAsEqual()
1633{
1634 QQmlEngine engine;
1635 QQmlComponent component(&engine, testFileUrl(fileName: "objectsCompareAsEqual.qml"));
1636 QObject *object = component.create();
1637 QVERIFY(object != nullptr);
1638
1639 QCOMPARE(object->property("test1").toBool(), true);
1640 QCOMPARE(object->property("test2").toBool(), true);
1641 QCOMPARE(object->property("test3").toBool(), true);
1642 QCOMPARE(object->property("test4").toBool(), true);
1643 QCOMPARE(object->property("test5").toBool(), true);
1644
1645 delete object;
1646}
1647
1648/*
1649Confirm bindings and alias properties can coexist.
1650
1651Tests for a regression where the binding would not reevaluate.
1652*/
1653void tst_qqmlecmascript::aliasPropertyAndBinding()
1654{
1655 QQmlEngine engine;
1656 QQmlComponent component(&engine, testFileUrl(fileName: "aliasPropertyAndBinding.qml"));
1657 QObject *object = component.create();
1658 QVERIFY(object != nullptr);
1659
1660 QCOMPARE(object->property("c2").toInt(), 3);
1661 QCOMPARE(object->property("c3").toInt(), 3);
1662
1663 object->setProperty(name: "c2", value: QVariant(19));
1664
1665 QCOMPARE(object->property("c2").toInt(), 19);
1666 QCOMPARE(object->property("c3").toInt(), 19);
1667
1668 delete object;
1669}
1670
1671/*
1672Ensure that we can write undefined value to an alias property,
1673and that the aliased property is reset correctly if possible.
1674*/
1675void tst_qqmlecmascript::aliasPropertyReset()
1676{
1677 QQmlEngine engine;
1678 QObject *object = nullptr;
1679
1680 // test that a manual write (of undefined) to a resettable aliased property succeeds
1681 QQmlComponent c1(&engine, testFileUrl(fileName: "aliasreset/aliasPropertyReset.1.qml"));
1682 object = c1.create();
1683 QVERIFY(object != nullptr);
1684 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1685 QCOMPARE(object->property("aliasIsUndefined"), QVariant(false));
1686 QMetaObject::invokeMethod(obj: object, member: "resetAliased");
1687 QVERIFY(!object->property("sourceComponentAlias").value<QQmlComponent*>());
1688 QCOMPARE(object->property("aliasIsUndefined"), QVariant(true));
1689 delete object;
1690
1691 // test that a manual write (of undefined) to a resettable alias property succeeds
1692 QQmlComponent c2(&engine, testFileUrl(fileName: "aliasreset/aliasPropertyReset.2.qml"));
1693 object = c2.create();
1694 QVERIFY(object != nullptr);
1695 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1696 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(false));
1697 QMetaObject::invokeMethod(obj: object, member: "resetAlias");
1698 QVERIFY(!object->property("sourceComponentAlias").value<QQmlComponent*>());
1699 QCOMPARE(object->property("loaderSourceComponentIsUndefined"), QVariant(true));
1700 delete object;
1701
1702 // test that an alias to a bound property works correctly
1703 QQmlComponent c3(&engine, testFileUrl(fileName: "aliasreset/aliasPropertyReset.3.qml"));
1704 object = c3.create();
1705 QVERIFY(object != nullptr);
1706 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1707 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(false));
1708 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1709 QMetaObject::invokeMethod(obj: object, member: "resetAlias");
1710 QVERIFY(!object->property("sourceComponentAlias").value<QQmlComponent*>());
1711 QCOMPARE(object->property("loaderOneSourceComponentIsUndefined"), QVariant(true));
1712 QCOMPARE(object->property("loaderTwoSourceComponentIsUndefined"), QVariant(false));
1713 delete object;
1714
1715 // test that a manual write (of undefined) to a resettable alias property
1716 // whose aliased property's object has been deleted, does not crash.
1717 QQmlComponent c4(&engine, testFileUrl(fileName: "aliasreset/aliasPropertyReset.4.qml"));
1718 object = c4.create();
1719 QVERIFY(object != nullptr);
1720 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() != 0);
1721 QObject *loader = object->findChild<QObject*>(aName: "loader");
1722 QVERIFY(loader != nullptr);
1723 delete loader;
1724 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // deletion should have caused value unset.
1725 QMetaObject::invokeMethod(obj: object, member: "resetAlias"); // shouldn't crash.
1726 QVERIFY(!object->property("sourceComponentAlias").value<QQmlComponent*>());
1727 QMetaObject::invokeMethod(obj: object, member: "setAlias"); // shouldn't crash, and shouldn't change value (since it's no longer referencing anything).
1728 QVERIFY(!object->property("sourceComponentAlias").value<QQmlComponent*>());
1729 delete object;
1730
1731 // test that binding an alias property to an undefined value works correctly
1732 QQmlComponent c5(&engine, testFileUrl(fileName: "aliasreset/aliasPropertyReset.5.qml"));
1733 object = c5.create();
1734 QVERIFY(object != nullptr);
1735 QVERIFY(object->property("sourceComponentAlias").value<QQmlComponent*>() == 0); // bound to undefined value.
1736 delete object;
1737
1738 // test that a manual write (of undefined) to a non-resettable property fails properly
1739 QUrl url = testFileUrl(fileName: "aliasreset/aliasPropertyReset.error.1.qml");
1740 QString warning1 = url.toString() + QLatin1String(":15: Error: Cannot assign [undefined] to int");
1741 QQmlComponent e1(&engine, url);
1742 object = e1.create();
1743 QVERIFY(object != nullptr);
1744 QCOMPARE(object->property("intAlias").value<int>(), 12);
1745 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1746 QTest::ignoreMessage(type: QtWarningMsg, message: warning1.toLatin1().constData());
1747 QMetaObject::invokeMethod(obj: object, member: "resetAlias");
1748 QCOMPARE(object->property("intAlias").value<int>(), 12);
1749 QCOMPARE(object->property("aliasedIntIsUndefined"), QVariant(false));
1750 delete object;
1751}
1752
1753void tst_qqmlecmascript::componentCreation_data()
1754{
1755 QTest::addColumn<QString>(name: "method");
1756 QTest::addColumn<QString>(name: "creationError");
1757 QTest::addColumn<QString>(name: "createdParent");
1758
1759 QTest::newRow(dataTag: "url")
1760 << "url"
1761 << ""
1762 << "";
1763 QTest::newRow(dataTag: "urlMode")
1764 << "urlMode"
1765 << ""
1766 << "";
1767 QTest::newRow(dataTag: "urlParent")
1768 << "urlParent"
1769 << ""
1770 << "obj";
1771 QTest::newRow(dataTag: "urlNullParent")
1772 << "urlNullParent"
1773 << ""
1774 << "null";
1775 QTest::newRow(dataTag: "urlModeParent")
1776 << "urlModeParent"
1777 << ""
1778 << "obj";
1779 QTest::newRow(dataTag: "urlModeNullParent")
1780 << "urlModeNullParent"
1781 << ""
1782 << "null";
1783 QTest::newRow(dataTag: "invalidSecondArg")
1784 << "invalidSecondArg"
1785 << ":40: Error: Qt.createComponent(): Invalid arguments"
1786 << "";
1787 QTest::newRow(dataTag: "invalidThirdArg")
1788 << "invalidThirdArg"
1789 << ":45: Error: Qt.createComponent(): Invalid parent object"
1790 << "";
1791 QTest::newRow(dataTag: "invalidMode")
1792 << "invalidMode"
1793 << ":50: Error: Qt.createComponent(): Invalid arguments"
1794 << "";
1795}
1796
1797/*
1798Test using createComponent to dynamically generate a component.
1799*/
1800void tst_qqmlecmascript::componentCreation()
1801{
1802 QFETCH(QString, method);
1803 QFETCH(QString, creationError);
1804 QFETCH(QString, createdParent);
1805
1806 QQmlEngine engine;
1807 QUrl testUrl(testFileUrl(fileName: "componentCreation.qml"));
1808
1809 if (!creationError.isEmpty()) {
1810 QString warning = testUrl.toString() + creationError;
1811 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
1812 }
1813
1814 QQmlComponent component(&engine, testUrl);
1815 QScopedPointer<MyTypeObject> object(qobject_cast<MyTypeObject*>(object: component.create()));
1816 QVERIFY(object != nullptr);
1817
1818 QMetaObject::invokeMethod(obj: object.get(), member: method.toUtf8());
1819 QQmlComponent *created = object->componentProperty();
1820
1821 if (creationError.isEmpty()) {
1822 QVERIFY(created);
1823
1824 QObject *expectedParent = reinterpret_cast<QObject *>(quintptr(-1));
1825 if (createdParent == QLatin1String("obj")) {
1826 expectedParent = object.get();
1827 } else if ((createdParent == QLatin1String("null")) || createdParent.isEmpty()) {
1828 expectedParent = nullptr;
1829 }
1830 QCOMPARE(created->parent(), expectedParent);
1831 }
1832}
1833
1834void tst_qqmlecmascript::dynamicCreation_data()
1835{
1836 QTest::addColumn<QString>(name: "method");
1837 QTest::addColumn<QString>(name: "createdName");
1838
1839 QTest::newRow(dataTag: "One") << "createOne" << "objectOne";
1840 QTest::newRow(dataTag: "Two") << "createTwo" << "objectTwo";
1841 QTest::newRow(dataTag: "Three") << "createThree" << "objectThree";
1842}
1843
1844/*
1845Test using createQmlObject to dynamically generate an item
1846Also using createComponent is tested.
1847*/
1848void tst_qqmlecmascript::dynamicCreation()
1849{
1850 QFETCH(QString, method);
1851 QFETCH(QString, createdName);
1852
1853 QQmlEngine engine;
1854 QQmlComponent component(&engine, testFileUrl(fileName: "dynamicCreation.qml"));
1855 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
1856 QVERIFY(object != nullptr);
1857
1858 QMetaObject::invokeMethod(obj: object, member: method.toUtf8());
1859 QObject *created = object->objectProperty();
1860 QVERIFY(created);
1861 QCOMPARE(created->objectName(), createdName);
1862
1863 delete object;
1864}
1865
1866/*
1867 Tests the destroy function
1868*/
1869void tst_qqmlecmascript::dynamicDestruction()
1870{
1871 QQmlEngine engine;
1872
1873 {
1874 QQmlComponent component(&engine, testFileUrl(fileName: "dynamicDeletion.qml"));
1875 QPointer<MyQmlObject> object = qobject_cast<MyQmlObject*>(object: component.create());
1876 QVERIFY(object != nullptr);
1877 QPointer<QObject> createdQmlObject = nullptr;
1878
1879 QMetaObject::invokeMethod(obj: object, member: "create");
1880 createdQmlObject = object->objectProperty();
1881 QVERIFY(createdQmlObject);
1882 QCOMPARE(createdQmlObject->objectName(), QString("emptyObject"));
1883
1884 QMetaObject::invokeMethod(obj: object, member: "killOther");
1885 QVERIFY(createdQmlObject);
1886
1887 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
1888 QCoreApplication::processEvents();
1889 QVERIFY(createdQmlObject);
1890 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1891 if (createdQmlObject) {
1892 QTest::qWait(ms: 100);
1893 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
1894 QCoreApplication::processEvents();
1895 }
1896 }
1897 QVERIFY(!createdQmlObject);
1898
1899 QQmlEngine::setObjectOwnership(object, QQmlEngine::JavaScriptOwnership);
1900 QMetaObject::invokeMethod(obj: object, member: "killMe");
1901 QVERIFY(object);
1902 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
1903 QCoreApplication::processEvents();
1904 QVERIFY(!object);
1905 }
1906
1907 {
1908 QQmlComponent component(&engine, testFileUrl(fileName: "dynamicDeletion.2.qml"));
1909 QObject *o = component.create();
1910 QVERIFY(o != nullptr);
1911
1912 QVERIFY(!qvariant_cast<QObject*>(o->property("objectProperty")));
1913
1914 QMetaObject::invokeMethod(obj: o, member: "create");
1915
1916 QVERIFY(qvariant_cast<QObject*>(o->property("objectProperty")) != 0);
1917
1918 QMetaObject::invokeMethod(obj: o, member: "destroy");
1919
1920 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
1921 QCoreApplication::processEvents();
1922
1923 QVERIFY(!qvariant_cast<QObject*>(o->property("objectProperty")));
1924
1925 delete o;
1926 }
1927
1928 {
1929 // QTBUG-23451
1930 QPointer<QObject> createdQmlObject = nullptr;
1931 QQmlComponent component(&engine, testFileUrl(fileName: "dynamicDeletion.3.qml"));
1932 QObject *o = component.create();
1933 QVERIFY(o != nullptr);
1934 QVERIFY(!qvariant_cast<QObject*>(o->property("objectProperty")));
1935 QMetaObject::invokeMethod(obj: o, member: "create");
1936 createdQmlObject = qvariant_cast<QObject*>(v: o->property(name: "objectProperty"));
1937 QVERIFY(createdQmlObject);
1938 QMetaObject::invokeMethod(obj: o, member: "destroy");
1939 QCOMPARE(qvariant_cast<bool>(o->property("test")), false);
1940 for (int ii = 0; createdQmlObject && ii < 50; ++ii) { // After 5 seconds we should give up
1941 QTest::qWait(ms: 100);
1942 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
1943 QCoreApplication::processEvents();
1944 }
1945 QVERIFY(!qvariant_cast<QObject*>(o->property("objectProperty")));
1946 QCOMPARE(qvariant_cast<bool>(o->property("test")), true);
1947 delete o;
1948 }
1949}
1950
1951/*
1952 tests that id.toString() works
1953*/
1954void tst_qqmlecmascript::objectToString()
1955{
1956 QQmlEngine engine;
1957 QQmlComponent component(&engine, testFileUrl(fileName: "qmlToString.qml"));
1958 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
1959 QVERIFY(object != nullptr);
1960 QMetaObject::invokeMethod(obj: object, member: "testToString");
1961 QVERIFY(object->stringProperty().startsWith("MyQmlObject_QML_"));
1962 QVERIFY(object->stringProperty().endsWith(", \"objName\")"));
1963
1964 delete object;
1965}
1966
1967/*
1968 tests that id.hasOwnProperty() works
1969*/
1970void tst_qqmlecmascript::objectHasOwnProperty()
1971{
1972 QUrl url = testFileUrl(fileName: "qmlHasOwnProperty.qml");
1973 QString warning1 = url.toString() + ":59: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1974 QString warning2 = url.toString() + ":64: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1975 QString warning3 = url.toString() + ":69: TypeError: Cannot call method 'hasOwnProperty' of undefined";
1976
1977 QQmlEngine engine;
1978 QQmlComponent component(&engine, url);
1979 QObject *object = component.create();
1980 QVERIFY(object != nullptr);
1981
1982 // test QObjects in QML
1983 QMetaObject::invokeMethod(obj: object, member: "testHasOwnPropertySuccess");
1984 QVERIFY(object->property("result").value<bool>());
1985 QMetaObject::invokeMethod(obj: object, member: "testHasOwnPropertyFailure");
1986 QVERIFY(!object->property("result").value<bool>());
1987
1988 // now test other types in QML
1989 QObject *child = object->findChild<QObject*>(aName: "typeObj");
1990 QVERIFY(child != nullptr);
1991 QMetaObject::invokeMethod(obj: child, member: "testHasOwnPropertySuccess");
1992 QCOMPARE(child->property("valueTypeHasOwnProperty").toBool(), true);
1993 QCOMPARE(child->property("valueTypeHasOwnProperty2").toBool(), true);
1994 QCOMPARE(child->property("variantTypeHasOwnProperty").toBool(), true);
1995 QCOMPARE(child->property("stringTypeHasOwnProperty").toBool(), true);
1996 QCOMPARE(child->property("listTypeHasOwnProperty").toBool(), true);
1997 QCOMPARE(child->property("emptyListTypeHasOwnProperty").toBool(), true);
1998 QCOMPARE(child->property("enumTypeHasOwnProperty").toBool(), true);
1999 QCOMPARE(child->property("typenameHasOwnProperty").toBool(), true);
2000 QCOMPARE(child->property("typenameHasOwnProperty2").toBool(), true);
2001 QCOMPARE(child->property("singletonTypeTypeHasOwnProperty").toBool(), true);
2002 QCOMPARE(child->property("singletonTypePropertyTypeHasOwnProperty").toBool(), true);
2003
2004 QTest::ignoreMessage(type: QtWarningMsg, message: warning1.toLatin1().constData());
2005 QMetaObject::invokeMethod(obj: child, member: "testHasOwnPropertyFailureOne");
2006 QCOMPARE(child->property("enumNonValueHasOwnProperty").toBool(), false);
2007 QTest::ignoreMessage(type: QtWarningMsg, message: warning2.toLatin1().constData());
2008 QMetaObject::invokeMethod(obj: child, member: "testHasOwnPropertyFailureTwo");
2009 QCOMPARE(child->property("singletonTypeNonPropertyHasOwnProperty").toBool(), false);
2010 QTest::ignoreMessage(type: QtWarningMsg, message: warning3.toLatin1().constData());
2011 QMetaObject::invokeMethod(obj: child, member: "testHasOwnPropertyFailureThree");
2012 QCOMPARE(child->property("listAtInvalidHasOwnProperty").toBool(), false);
2013
2014 delete object;
2015}
2016
2017/*
2018Tests bindings that indirectly cause their own deletion work.
2019
2020This test is best run under valgrind to ensure no invalid memory access occur.
2021*/
2022void tst_qqmlecmascript::selfDeletingBinding()
2023{
2024 QQmlEngine engine;
2025
2026 {
2027 QQmlComponent component(&engine, testFileUrl(fileName: "selfDeletingBinding.qml"));
2028 QObject *object = component.create();
2029 QVERIFY(object != nullptr);
2030 object->setProperty(name: "triggerDelete", value: true);
2031 delete object;
2032 }
2033
2034 {
2035 QQmlComponent component(&engine, testFileUrl(fileName: "selfDeletingBinding.2.qml"));
2036 QObject *object = component.create();
2037 QVERIFY(object != nullptr);
2038 object->setProperty(name: "triggerDelete", value: true);
2039 delete object;
2040 }
2041}
2042
2043/*
2044Test that extended object properties can be accessed.
2045
2046This test a regression where this used to crash. The issue was specificially
2047for extended objects that did not include a synthesized meta object (so non-root
2048and no synthesiszed properties).
2049*/
2050void tst_qqmlecmascript::extendedObjectPropertyLookup()
2051{
2052 QQmlEngine engine;
2053 QQmlComponent component(&engine, testFileUrl(fileName: "extendedObjectPropertyLookup.qml"));
2054 QObject *object = component.create();
2055 QVERIFY(object != nullptr);
2056 delete object;
2057}
2058
2059/*
2060Test that extended object properties can be accessed correctly.
2061*/
2062void tst_qqmlecmascript::extendedObjectPropertyLookup2()
2063{
2064 QQmlEngine engine;
2065 QQmlComponent component(&engine, testFileUrl(fileName: "extendedObjectPropertyLookup2.qml"));
2066 QObject *object = component.create();
2067 QVERIFY(object != nullptr);
2068
2069 QVariant returnValue;
2070 QVERIFY(QMetaObject::invokeMethod(object, "getValue", Q_RETURN_ARG(QVariant, returnValue)));
2071 QCOMPARE(returnValue.toInt(), 42);
2072
2073 delete object;
2074}
2075
2076/*
2077Test failure when trying to create and uncreatable extended type object.
2078 */
2079void tst_qqmlecmascript::uncreatableExtendedObjectFailureCheck()
2080{
2081 QQmlEngine engine;
2082 QQmlComponent component(&engine, testFileUrl(fileName: "uncreatableExtendedObjectFailureCheck.qml"));
2083
2084 QObject *object = component.create();
2085 QVERIFY(!object);
2086}
2087
2088/*
2089Test that an subclass of an uncreatable extended object contains all the required properties.
2090 */
2091void tst_qqmlecmascript::extendedObjectPropertyLookup3()
2092{
2093 QQmlEngine engine;
2094 QQmlComponent component(&engine, testFileUrl(fileName: "extendedObjectPropertyLookup3.qml"));
2095
2096 QObject *object = component.create();
2097 QVERIFY(object != nullptr);
2098
2099 QVariant returnValue;
2100 QVERIFY(QMetaObject::invokeMethod(object, "getAbstractProperty", Q_RETURN_ARG(QVariant, returnValue)));
2101 QCOMPARE(returnValue.toInt(), -1);
2102 QVERIFY(QMetaObject::invokeMethod(object, "getImplementedProperty", Q_RETURN_ARG(QVariant, returnValue)));
2103 QCOMPARE(returnValue.toInt(), 883);
2104 QVERIFY(QMetaObject::invokeMethod(object, "getExtendedProperty", Q_RETURN_ARG(QVariant, returnValue)));
2105 QCOMPARE(returnValue.toInt(), 42);
2106
2107 delete object;
2108}
2109/*
2110Test file/lineNumbers for binding/Script errors.
2111*/
2112void tst_qqmlecmascript::scriptErrors()
2113{
2114 QQmlEngine engine;
2115 QQmlComponent component(&engine, testFileUrl(fileName: "scriptErrors.qml"));
2116 QString url = component.url().toString();
2117
2118 QString warning1 = url.left(n: url.length() - 3) + "js:2: Error: Invalid write to global property \"a\"";
2119 QString warning2 = url + ":5: ReferenceError: a is not defined";
2120 QString warning3 = url.left(n: url.length() - 3) + "js:4: Error: Invalid write to global property \"a\"";
2121 QString warning4 = url + ":13: ReferenceError: a is not defined";
2122 QString warning5 = url + ":11: ReferenceError: a is not defined";
2123 QString warning6 = url + ":10:5: Unable to assign [undefined] to int";
2124 QString warning7 = url + ":15: TypeError: Cannot assign to read-only property \"trueProperty\"";
2125 QString warning8 = url + ":16: Error: Cannot assign to non-existent property \"fakeProperty\"";
2126
2127 QTest::ignoreMessage(type: QtWarningMsg, message: warning1.toLatin1().constData());
2128 QTest::ignoreMessage(type: QtWarningMsg, message: warning2.toLatin1().constData());
2129 QTest::ignoreMessage(type: QtWarningMsg, message: warning3.toLatin1().constData());
2130 QTest::ignoreMessage(type: QtWarningMsg, message: warning5.toLatin1().constData());
2131 QTest::ignoreMessage(type: QtWarningMsg, message: warning6.toLatin1().constData());
2132 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
2133 QVERIFY(object != nullptr);
2134
2135 QTest::ignoreMessage(type: QtWarningMsg, message: warning4.toLatin1().constData());
2136 emit object->basicSignal();
2137
2138 QTest::ignoreMessage(type: QtWarningMsg, message: warning7.toLatin1().constData());
2139 emit object->anotherBasicSignal();
2140
2141 QTest::ignoreMessage(type: QtWarningMsg, message: warning8.toLatin1().constData());
2142 emit object->thirdBasicSignal();
2143
2144 delete object;
2145}
2146
2147/*
2148Test file/lineNumbers for inline functions.
2149*/
2150void tst_qqmlecmascript::functionErrors()
2151{
2152 QQmlEngine engine;
2153 QQmlComponent component(&engine, testFileUrl(fileName: "functionErrors.qml"));
2154 QString url = component.url().toString();
2155
2156 QString warning = url + ":5: Error: Invalid write to global property \"a\"";
2157
2158 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
2159
2160 QObject *object = component.create();
2161 QVERIFY(object != nullptr);
2162 delete object;
2163
2164 // test that if an exception occurs while invoking js function from cpp, it is reported as expected.
2165 QQmlComponent componentTwo(&engine, testFileUrl(fileName: "scarceResourceFunctionFail.var.qml"));
2166 url = componentTwo.url().toString();
2167 object = componentTwo.create();
2168 QVERIFY(object != nullptr);
2169
2170 QObject *resource = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
2171 warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function");
2172 warning = warning.arg(a: QString::number(quintptr(resource), base: 16));
2173 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData()); // we expect a meaningful warning to be printed.
2174 QMetaObject::invokeMethod(obj: object, member: "retrieveScarceResource");
2175 delete object;
2176}
2177
2178/*
2179Test various errors that can occur when assigning a property from script
2180*/
2181void tst_qqmlecmascript::propertyAssignmentErrors()
2182{
2183 QQmlEngine engine;
2184 QQmlComponent component(&engine, testFileUrl(fileName: "propertyAssignmentErrors.qml"));
2185
2186 QString url = component.url().toString();
2187
2188 QObject *object = component.create();
2189 QVERIFY(object != nullptr);
2190
2191 QCOMPARE(object->property("test1").toBool(), true);
2192 QCOMPARE(object->property("test2").toBool(), true);
2193
2194 delete object;
2195}
2196
2197/*
2198Test bindings still work when the reeval is triggered from within
2199a signal script.
2200*/
2201void tst_qqmlecmascript::signalTriggeredBindings()
2202{
2203 QQmlEngine engine;
2204 QQmlComponent component(&engine, testFileUrl(fileName: "signalTriggeredBindings.qml"));
2205 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
2206 QVERIFY(object != nullptr);
2207
2208 QCOMPARE(object->property("base").toReal(), 50.);
2209 QCOMPARE(object->property("test1").toReal(), 50.);
2210 QCOMPARE(object->property("test2").toReal(), 50.);
2211
2212 object->basicSignal();
2213
2214 QCOMPARE(object->property("base").toReal(), 200.);
2215 QCOMPARE(object->property("test1").toReal(), 200.);
2216 QCOMPARE(object->property("test2").toReal(), 200.);
2217
2218 object->argumentSignal(a: 10, b: QString(), c: 10, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
2219
2220 QCOMPARE(object->property("base").toReal(), 400.);
2221 QCOMPARE(object->property("test1").toReal(), 400.);
2222 QCOMPARE(object->property("test2").toReal(), 400.);
2223
2224 delete object;
2225}
2226
2227/*
2228Test that list properties can be iterated from ECMAScript
2229*/
2230void tst_qqmlecmascript::listProperties()
2231{
2232 QQmlEngine engine;
2233 QQmlComponent component(&engine, testFileUrl(fileName: "listProperties.qml"));
2234 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
2235 QVERIFY(object != nullptr);
2236
2237 QCOMPARE(object->property("test1").toInt(), 21);
2238 QCOMPARE(object->property("test2").toInt(), 2);
2239 QCOMPARE(object->property("test3").toBool(), true);
2240 QCOMPARE(object->property("test4").toBool(), true);
2241
2242 delete object;
2243}
2244
2245void tst_qqmlecmascript::exceptionClearsOnReeval()
2246{
2247 QQmlEngine engine;
2248 QQmlComponent component(&engine, testFileUrl(fileName: "exceptionClearsOnReeval.qml"));
2249 QString url = component.url().toString();
2250
2251 QString warning = url + ":4: TypeError: Cannot read property 'objectProperty' of null";
2252
2253 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
2254 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
2255 QVERIFY(object != nullptr);
2256
2257 QCOMPARE(object->property("test").toBool(), false);
2258
2259 MyQmlObject object2;
2260 MyQmlObject object3;
2261 object2.setObjectProperty(&object3);
2262 object->setObjectProperty(&object2);
2263
2264 QCOMPARE(object->property("test").toBool(), true);
2265
2266 delete object;
2267}
2268
2269void tst_qqmlecmascript::exceptionSlotProducesWarning()
2270{
2271 QQmlEngine engine;
2272 QQmlComponent component(&engine, testFileUrl(fileName: "exceptionProducesWarning.qml"));
2273 QString url = component.url().toString();
2274
2275 QString warning = component.url().toString() + ":6: Error: JS exception";
2276
2277 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
2278 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
2279 QVERIFY(object != nullptr);
2280 delete object;
2281}
2282
2283void tst_qqmlecmascript::exceptionBindingProducesWarning()
2284{
2285 QQmlEngine engine;
2286 QQmlComponent component(&engine, testFileUrl(fileName: "exceptionProducesWarning2.qml"));
2287 QString url = component.url().toString();
2288
2289 QString warning = component.url().toString() + ":5: Error: JS exception";
2290
2291 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
2292 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
2293 QVERIFY(object != nullptr);
2294 delete object;
2295}
2296
2297void tst_qqmlecmascript::compileInvalidBinding()
2298{
2299 // QTBUG-23387: ensure that invalid bindings don't cause a crash.
2300 QQmlEngine engine;
2301 QQmlComponent component(&engine, testFileUrl(fileName: "v8bindingException.qml"));
2302 QObject *object = component.create();
2303 QVERIFY(object != nullptr);
2304 delete object;
2305}
2306
2307// Check that transient binding errors are not displayed
2308void tst_qqmlecmascript::transientErrors()
2309{
2310 QQmlEngine engine;
2311
2312 {
2313 QQmlComponent component(&engine, testFileUrl(fileName: "transientErrors.qml"));
2314
2315 QQmlTestMessageHandler messageHandler;
2316
2317 QObject *object = component.create();
2318 QVERIFY(object != nullptr);
2319
2320 QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
2321
2322 delete object;
2323 }
2324
2325 // One binding erroring multiple times, but then resolving
2326 {
2327 QQmlComponent component(&engine, testFileUrl(fileName: "transientErrors.2.qml"));
2328
2329 QQmlTestMessageHandler messageHandler;
2330
2331 QObject *object = component.create();
2332 QVERIFY(object != nullptr);
2333
2334 QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
2335
2336 delete object;
2337 }
2338}
2339
2340// Check that errors during shutdown are minimized
2341void tst_qqmlecmascript::shutdownErrors()
2342{
2343 QQmlEngine engine;
2344 QQmlComponent component(&engine, testFileUrl(fileName: "shutdownErrors.qml"));
2345 QObject *object = component.create();
2346 QVERIFY(object != nullptr);
2347
2348 QQmlTestMessageHandler messageHandler;
2349
2350 delete object;
2351
2352 QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
2353}
2354
2355void tst_qqmlecmascript::compositePropertyType()
2356{
2357 QQmlEngine engine;
2358 QQmlComponent component(&engine, testFileUrl(fileName: "compositePropertyType.qml"));
2359
2360 QTest::ignoreMessage(type: QtDebugMsg, message: "hello world");
2361 QObject *object = qobject_cast<QObject *>(object: component.create());
2362 delete object;
2363}
2364
2365// QTBUG-5759
2366void tst_qqmlecmascript::jsObject()
2367{
2368 QQmlEngine engine;
2369 QQmlComponent component(&engine, testFileUrl(fileName: "jsObject.qml"));
2370 QObject *object = component.create();
2371 QVERIFY(object != nullptr);
2372
2373 QCOMPARE(object->property("test").toInt(), 92);
2374
2375 delete object;
2376}
2377
2378void tst_qqmlecmascript::undefinedResetsProperty()
2379{
2380 QQmlEngine engine;
2381
2382 {
2383 QQmlComponent component(&engine, testFileUrl(fileName: "undefinedResetsProperty.qml"));
2384 QObject *object = component.create();
2385 QVERIFY(object != nullptr);
2386
2387 QCOMPARE(object->property("resettableProperty").toInt(), 92);
2388
2389 object->setProperty(name: "setUndefined", value: true);
2390
2391 QCOMPARE(object->property("resettableProperty").toInt(), 13);
2392
2393 object->setProperty(name: "setUndefined", value: false);
2394
2395 QCOMPARE(object->property("resettableProperty").toInt(), 92);
2396
2397 delete object;
2398 }
2399 {
2400 QQmlComponent component(&engine, testFileUrl(fileName: "undefinedResetsProperty.2.qml"));
2401 QObject *object = component.create();
2402 QVERIFY(object != nullptr);
2403
2404 QCOMPARE(object->property("resettableProperty").toInt(), 19);
2405
2406 QMetaObject::invokeMethod(obj: object, member: "doReset");
2407
2408 QCOMPARE(object->property("resettableProperty").toInt(), 13);
2409
2410 delete object;
2411 }
2412}
2413
2414// Aliases to variant properties should work
2415void tst_qqmlecmascript::qtbug_22464()
2416{
2417 QQmlEngine engine;
2418 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_22464.qml"));
2419 QObject *object = component.create();
2420 QVERIFY(object != nullptr);
2421
2422 QCOMPARE(object->property("test").toBool(), true);
2423
2424 delete object;
2425}
2426
2427void tst_qqmlecmascript::qtbug_21580()
2428{
2429 QQmlEngine engine;
2430 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_21580.qml"));
2431
2432 QObject *object = component.create();
2433 QVERIFY(object != nullptr);
2434
2435 QCOMPARE(object->property("test").toBool(), true);
2436
2437 delete object;
2438}
2439
2440// Causes a v8 binding, but not all v8 bindings to be destroyed during evaluation
2441void tst_qqmlecmascript::singleV8BindingDestroyedDuringEvaluation()
2442{
2443 QQmlEngine engine;
2444 QQmlComponent component(&engine, testFileUrl(fileName: "singleV8BindingDestroyedDuringEvaluation.qml"));
2445
2446 QObject *object = component.create();
2447 QVERIFY(object != nullptr);
2448 delete object;
2449}
2450
2451// QTBUG-6781
2452void tst_qqmlecmascript::bug1()
2453{
2454 QQmlEngine engine;
2455 QQmlComponent component(&engine, testFileUrl(fileName: "bug.1.qml"));
2456 QObject *object = component.create();
2457 QVERIFY(object != nullptr);
2458
2459 QCOMPARE(object->property("test").toInt(), 14);
2460
2461 object->setProperty(name: "a", value: 11);
2462
2463 QCOMPARE(object->property("test").toInt(), 3);
2464
2465 object->setProperty(name: "b", value: true);
2466
2467 QCOMPARE(object->property("test").toInt(), 9);
2468
2469 delete object;
2470}
2471
2472#ifndef QT_NO_WIDGETS
2473void tst_qqmlecmascript::bug2()
2474{
2475 QQmlEngine engine;
2476 QQmlComponent component(&engine);
2477 component.setData("import Qt.test 1.0;\nQPlainTextEdit { width: 100 }", baseUrl: QUrl());
2478
2479 QObject *object = component.create();
2480 QVERIFY(object != nullptr);
2481
2482 delete object;
2483}
2484#endif
2485
2486// Don't crash in createObject when the component has errors.
2487void tst_qqmlecmascript::dynamicCreationCrash()
2488{
2489 QQmlEngine engine;
2490 QQmlComponent component(&engine, testFileUrl(fileName: "dynamicCreation.qml"));
2491 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
2492 QVERIFY(object != nullptr);
2493
2494 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready");
2495 QMetaObject::invokeMethod(obj: object, member: "dontCrash");
2496 QObject *created = object->objectProperty();
2497 QVERIFY(!created);
2498
2499 delete object;
2500}
2501
2502// ownership transferred to JS, ensure that GC runs the dtor
2503void tst_qqmlecmascript::dynamicCreationOwnership()
2504{
2505 int dtorCount = 0;
2506 int expectedDtorCount = 1; // start at 1 since we expect mdcdo to dtor too.
2507
2508 // allow the engine to go out of scope too.
2509 {
2510 QQmlEngine dcoEngine;
2511 QQmlComponent component(&dcoEngine, testFileUrl(fileName: "dynamicCreationOwnership.qml"));
2512 QObject *object = component.create();
2513 QVERIFY(object != nullptr);
2514 MyDynamicCreationDestructionObject *mdcdo = object->findChild<MyDynamicCreationDestructionObject*>(aName: "mdcdo");
2515 QVERIFY(mdcdo != nullptr);
2516 mdcdo->setDtorCount(&dtorCount);
2517
2518 for (int i = 1; i < 105; ++i, ++expectedDtorCount) {
2519 QMetaObject::invokeMethod(obj: object, member: "dynamicallyCreateJsOwnedObject");
2520 if (i % 90 == 0) {
2521 // we do this once manually, but it should be done automatically
2522 // when the engine goes out of scope (since it should gc in dtor)
2523 QMetaObject::invokeMethod(obj: object, member: "performGc");
2524 }
2525 if (i % 10 == 0) {
2526 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
2527 QCoreApplication::processEvents();
2528 }
2529 }
2530
2531 delete object;
2532 }
2533 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
2534 QCoreApplication::processEvents();
2535 QCOMPARE(dtorCount, expectedDtorCount);
2536}
2537
2538void tst_qqmlecmascript::regExpBug()
2539{
2540 QQmlEngine engine;
2541
2542 //QTBUG-9367
2543 {
2544 QQmlComponent component(&engine, testFileUrl(fileName: "regExp.qml"));
2545 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
2546 QVERIFY(object != nullptr);
2547 QCOMPARE(object->regExp().pattern(), QLatin1String("[a-zA-z]"));
2548 delete object;
2549 }
2550
2551 {
2552 QQmlComponent component(&engine, testFileUrl(fileName: "regularExpression.qml"));
2553 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
2554 QVERIFY(!object.isNull());
2555 QCOMPARE(object->regularExpression().pattern(), QLatin1String("[a-zA-z]"));
2556 }
2557
2558 //QTBUG-23068
2559 {
2560 QString err = QString(QLatin1String("%1:6 Invalid property assignment: regular expression expected; use /pattern/ syntax\n")).arg(a: testFileUrl(fileName: "regExp.2.qml").toString());
2561 QQmlComponent component(&engine, testFileUrl(fileName: "regExp.2.qml"));
2562 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready");
2563 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
2564 QVERIFY(!object);
2565 QCOMPARE(component.errorString(), err);
2566 }
2567
2568 {
2569 const QString err = QString::fromLatin1(str: "%1:6 Invalid property assignment: "
2570 "regular expression expected; "
2571 "use /pattern/ syntax\n")
2572 .arg(a: testFileUrl(fileName: "regularExpression.2.qml").toString());
2573 QQmlComponent component(&engine, testFileUrl(fileName: "regularExpression.2.qml"));
2574 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready");
2575 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create());
2576 QVERIFY(!object);
2577 QCOMPARE(component.errorString(), err);
2578 }
2579}
2580
2581static inline bool evaluate_error(QV4::ExecutionEngine *v4, const QV4::Value &o, const char *source)
2582{
2583 QString functionSource = QLatin1String("(function(object) { return ") +
2584 QLatin1String(source) + QLatin1String(" })");
2585
2586 QV4::Scope scope(v4);
2587 QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::ContextType::Eval, functionSource);
2588 program.inheritContext = true;
2589
2590 QV4::ScopedFunctionObject function(scope, program.run());
2591 if (scope.engine->hasException) {
2592 scope.engine->catchException();
2593 return true;
2594 }
2595 QV4::JSCallData jsCallData(scope, 1);
2596 jsCallData->args[0] = o;
2597 *jsCallData->thisObject = v4->global();
2598 function->call(data: jsCallData);
2599 if (scope.engine->hasException) {
2600 scope.engine->catchException();
2601 return true;
2602 }
2603 return false;
2604}
2605
2606static inline bool evaluate_value(QV4::ExecutionEngine *v4, const QV4::Value &o,
2607 const char *source, const QV4::Value &result)
2608{
2609 QString functionSource = QLatin1String("(function(object) { return ") +
2610 QLatin1String(source) + QLatin1String(" })");
2611
2612 QV4::Scope scope(v4);
2613 QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::ContextType::Eval, functionSource);
2614 program.inheritContext = true;
2615
2616 QV4::ScopedFunctionObject function(scope, program.run());
2617 if (scope.engine->hasException) {
2618 scope.engine->catchException();
2619 return false;
2620 }
2621 if (!function)
2622 return false;
2623
2624 QV4::ScopedValue value(scope);
2625 QV4::JSCallData jsCallData(scope, 1);
2626 jsCallData->args[0] = o;
2627 *jsCallData->thisObject = v4->global();
2628 value = function->call(data: jsCallData);
2629 if (scope.engine->hasException) {
2630 scope.engine->catchException();
2631 return false;
2632 }
2633 return QV4::Runtime::StrictEqual::call(value, result);
2634}
2635
2636static inline QV4::ReturnedValue evaluate(QV4::ExecutionEngine *v4, const QV4::Value &o,
2637 const char *source)
2638{
2639 QString functionSource = QLatin1String("(function(object) { return ") +
2640 QLatin1String(source) + QLatin1String(" })");
2641
2642 QV4::Scope scope(v4);
2643
2644 QV4::Script program(QV4::ScopedContext(scope, scope.engine->rootContext()), QV4::Compiler::ContextType::Eval, functionSource);
2645 program.inheritContext = true;
2646
2647 QV4::ScopedFunctionObject function(scope, program.run());
2648 if (scope.engine->hasException) {
2649 scope.engine->catchException();
2650 return QV4::Encode::undefined();
2651 }
2652 if (!function)
2653 return QV4::Encode::undefined();
2654 QV4::JSCallData jsCallData(scope, 1);
2655 jsCallData->args[0] = o;
2656 *jsCallData->thisObject = v4->global();
2657 QV4::ScopedValue result(scope, function->call(data: jsCallData));
2658 if (scope.engine->hasException) {
2659 scope.engine->catchException();
2660 return QV4::Encode::undefined();
2661 }
2662 return result->asReturnedValue();
2663}
2664
2665#define EVALUATE_ERROR(source) evaluate_error(engine, object, source)
2666#define EVALUATE_VALUE(source, result) evaluate_value(engine, object, source, result)
2667#define EVALUATE(source) evaluate(engine, object, source)
2668
2669void tst_qqmlecmascript::callQtInvokables()
2670{
2671 // This object has JS ownership, as the call to method_NoArgs_QObject() in this test will return
2672 // it, which will set the indestructible flag to false.
2673 MyInvokableObject *o = new MyInvokableObject();
2674
2675 QQmlEngine qmlengine;
2676
2677 QV4::ExecutionEngine *engine = qmlengine.handle();
2678 QV4::Scope scope(engine);
2679
2680 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, object: o));
2681
2682 // Non-existent methods
2683 o->reset();
2684 QVERIFY(EVALUATE_ERROR("object.method_nonexistent()"));
2685 QCOMPARE(o->error(), false);
2686 QCOMPARE(o->invoked(), -1);
2687 QCOMPARE(o->actuals().count(), 0);
2688
2689 o->reset();
2690 QVERIFY(EVALUATE_ERROR("object.method_nonexistent(10, 11)"));
2691 QCOMPARE(o->error(), false);
2692 QCOMPARE(o->invoked(), -1);
2693 QCOMPARE(o->actuals().count(), 0);
2694
2695 // Insufficient arguments
2696 o->reset();
2697 QVERIFY(EVALUATE_ERROR("object.method_int()"));
2698 QCOMPARE(o->error(), false);
2699 QCOMPARE(o->invoked(), -1);
2700 QCOMPARE(o->actuals().count(), 0);
2701
2702 o->reset();
2703 QVERIFY(EVALUATE_ERROR("object.method_intint(10)"));
2704 QCOMPARE(o->error(), false);
2705 QCOMPARE(o->invoked(), -1);
2706 QCOMPARE(o->actuals().count(), 0);
2707
2708 // Excessive arguments
2709 o->reset();
2710 QVERIFY(EVALUATE_VALUE("object.method_int(10, 11)", QV4::Primitive::undefinedValue()));
2711 QCOMPARE(o->error(), false);
2712 QCOMPARE(o->invoked(), 8);
2713 QCOMPARE(o->actuals().count(), 1);
2714 QCOMPARE(o->actuals().at(0), QVariant(10));
2715
2716 o->reset();
2717 QVERIFY(EVALUATE_VALUE("object.method_intint(10, 11, 12)", QV4::Primitive::undefinedValue()));
2718 QCOMPARE(o->error(), false);
2719 QCOMPARE(o->invoked(), 9);
2720 QCOMPARE(o->actuals().count(), 2);
2721 QCOMPARE(o->actuals().at(0), QVariant(10));
2722 QCOMPARE(o->actuals().at(1), QVariant(11));
2723
2724 // Test return types
2725 o->reset();
2726 QVERIFY(EVALUATE_VALUE("object.method_NoArgs()", QV4::Primitive::undefinedValue()));
2727 QCOMPARE(o->error(), false);
2728 QCOMPARE(o->invoked(), 0);
2729 QCOMPARE(o->actuals().count(), 0);
2730
2731 o->reset();
2732 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_int()", QV4::Primitive::fromInt32(6)));
2733 QCOMPARE(o->error(), false);
2734 QCOMPARE(o->invoked(), 1);
2735 QCOMPARE(o->actuals().count(), 0);
2736
2737 o->reset();
2738 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_real()", QV4::Primitive::fromDouble(19.75)));
2739 QCOMPARE(o->error(), false);
2740 QCOMPARE(o->invoked(), 2);
2741 QCOMPARE(o->actuals().count(), 0);
2742
2743 o->reset();
2744 {
2745 QV4::ScopedValue ret(scope, EVALUATE("object.method_NoArgs_QPointF()"));
2746 QVERIFY(!ret->isUndefined());
2747 QCOMPARE(scope.engine->toVariant(ret, -1), QVariant(QPointF(123, 4.5)));
2748 QCOMPARE(o->error(), false);
2749 QCOMPARE(o->invoked(), 3);
2750 QCOMPARE(o->actuals().count(), 0);
2751 }
2752
2753 o->reset();
2754 {
2755 QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, EVALUATE("object.method_NoArgs_QObject()"));
2756 QVERIFY(qobjectWrapper);
2757 QCOMPARE(qobjectWrapper->object(), (QObject *)o);
2758 QCOMPARE(o->error(), false);
2759 QCOMPARE(o->invoked(), 4);
2760 QCOMPARE(o->actuals().count(), 0);
2761 }
2762
2763 o->reset();
2764 QVERIFY(EVALUATE_ERROR("object.method_NoArgs_unknown()"));
2765 QCOMPARE(o->error(), false);
2766 QCOMPARE(o->invoked(), -1);
2767 QCOMPARE(o->actuals().count(), 0);
2768
2769 o->reset();
2770 {
2771 QV4::ScopedValue ret(scope, EVALUATE("object.method_NoArgs_QScriptValue()"));
2772 QVERIFY(ret->isString());
2773 QCOMPARE(ret->toQStringNoThrow(), QString("Hello world"));
2774 QCOMPARE(o->error(), false);
2775 QCOMPARE(o->invoked(), 6);
2776 QCOMPARE(o->actuals().count(), 0);
2777 }
2778
2779 o->reset();
2780 QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", QV4::ScopedValue(scope, scope.engine->newString("QML rocks"))));
2781 QCOMPARE(o->error(), false);
2782 QCOMPARE(o->invoked(), 7);
2783 QCOMPARE(o->actuals().count(), 0);
2784
2785 // Test arg types
2786 o->reset();
2787 QVERIFY(EVALUATE_VALUE("object.method_int(94)", QV4::Primitive::undefinedValue()));
2788 QCOMPARE(o->error(), false);
2789 QCOMPARE(o->invoked(), 8);
2790 QCOMPARE(o->actuals().count(), 1);
2791 QCOMPARE(o->actuals().at(0), QVariant(94));
2792
2793 o->reset();
2794 QVERIFY(EVALUATE_VALUE("object.method_int(\"94\")", QV4::Primitive::undefinedValue()));
2795 QCOMPARE(o->error(), false);
2796 QCOMPARE(o->invoked(), 8);
2797 QCOMPARE(o->actuals().count(), 1);
2798 QCOMPARE(o->actuals().at(0), QVariant(94));
2799
2800 o->reset();
2801 QVERIFY(EVALUATE_VALUE("object.method_int(\"not a number\")", QV4::Primitive::undefinedValue()));
2802 QCOMPARE(o->error(), false);
2803 QCOMPARE(o->invoked(), 8);
2804 QCOMPARE(o->actuals().count(), 1);
2805 QCOMPARE(o->actuals().at(0), QVariant(0));
2806
2807 o->reset();
2808 QVERIFY(EVALUATE_VALUE("object.method_int(null)", QV4::Primitive::undefinedValue()));
2809 QCOMPARE(o->error(), false);
2810 QCOMPARE(o->invoked(), 8);
2811 QCOMPARE(o->actuals().count(), 1);
2812 QCOMPARE(o->actuals().at(0), QVariant(0));
2813
2814 o->reset();
2815 QVERIFY(EVALUATE_VALUE("object.method_int(undefined)", QV4::Primitive::undefinedValue()));
2816 QCOMPARE(o->error(), false);
2817 QCOMPARE(o->invoked(), 8);
2818 QCOMPARE(o->actuals().count(), 1);
2819 QCOMPARE(o->actuals().at(0), QVariant(0));
2820
2821 o->reset();
2822 QVERIFY(EVALUATE_VALUE("object.method_int(object)", QV4::Primitive::undefinedValue()));
2823 QCOMPARE(o->error(), false);
2824 QCOMPARE(o->invoked(), 8);
2825 QCOMPARE(o->actuals().count(), 1);
2826 QCOMPARE(o->actuals().at(0), QVariant(0));
2827
2828 o->reset();
2829 QVERIFY(EVALUATE_VALUE("object.method_intint(122, 9)", QV4::Primitive::undefinedValue()));
2830 QCOMPARE(o->error(), false);
2831 QCOMPARE(o->invoked(), 9);
2832 QCOMPARE(o->actuals().count(), 2);
2833 QCOMPARE(o->actuals().at(0), QVariant(122));
2834 QCOMPARE(o->actuals().at(1), QVariant(9));
2835
2836 o->reset();
2837 QVERIFY(EVALUATE_VALUE("object.method_real(94.3)", QV4::Primitive::undefinedValue()));
2838 QCOMPARE(o->error(), false);
2839 QCOMPARE(o->invoked(), 10);
2840 QCOMPARE(o->actuals().count(), 1);
2841 QCOMPARE(o->actuals().at(0), QVariant(94.3));
2842
2843 o->reset();
2844 QVERIFY(EVALUATE_VALUE("object.method_real(\"94.3\")", QV4::Primitive::undefinedValue()));
2845 QCOMPARE(o->error(), false);
2846 QCOMPARE(o->invoked(), 10);
2847 QCOMPARE(o->actuals().count(), 1);
2848 QCOMPARE(o->actuals().at(0), QVariant(94.3));
2849
2850 o->reset();
2851 QVERIFY(EVALUATE_VALUE("object.method_real(\"not a number\")", QV4::Primitive::undefinedValue()));
2852 QCOMPARE(o->error(), false);
2853 QCOMPARE(o->invoked(), 10);
2854 QCOMPARE(o->actuals().count(), 1);
2855 QVERIFY(qIsNaN(o->actuals().at(0).toDouble()));
2856
2857 o->reset();
2858 QVERIFY(EVALUATE_VALUE("object.method_real(null)", QV4::Primitive::undefinedValue()));
2859 QCOMPARE(o->error(), false);
2860 QCOMPARE(o->invoked(), 10);
2861 QCOMPARE(o->actuals().count(), 1);
2862 QCOMPARE(o->actuals().at(0), QVariant(0));
2863
2864 o->reset();
2865 QVERIFY(EVALUATE_VALUE("object.method_real(undefined)", QV4::Primitive::undefinedValue()));
2866 QCOMPARE(o->error(), false);
2867 QCOMPARE(o->invoked(), 10);
2868 QCOMPARE(o->actuals().count(), 1);
2869 QVERIFY(qIsNaN(o->actuals().at(0).toDouble()));
2870
2871 o->reset();
2872 QVERIFY(EVALUATE_VALUE("object.method_real(object)", QV4::Primitive::undefinedValue()));
2873 QCOMPARE(o->error(), false);
2874 QCOMPARE(o->invoked(), 10);
2875 QCOMPARE(o->actuals().count(), 1);
2876 QVERIFY(qIsNaN(o->actuals().at(0).toDouble()));
2877
2878 o->reset();
2879 QVERIFY(EVALUATE_VALUE("object.method_QString(\"Hello world\")", QV4::Primitive::undefinedValue()));
2880 QCOMPARE(o->error(), false);
2881 QCOMPARE(o->invoked(), 11);
2882 QCOMPARE(o->actuals().count(), 1);
2883 QCOMPARE(o->actuals().at(0), QVariant("Hello world"));
2884
2885 o->reset();
2886 QVERIFY(EVALUATE_VALUE("object.method_QString(19)", QV4::Primitive::undefinedValue()));
2887 QCOMPARE(o->error(), false);
2888 QCOMPARE(o->invoked(), 11);
2889 QCOMPARE(o->actuals().count(), 1);
2890 QCOMPARE(o->actuals().at(0), QVariant("19"));
2891
2892 o->reset();
2893 {
2894 QString expected = "MyInvokableObject(0x" + QString::number((quintptr)o, base: 16) + ")";
2895 QVERIFY(EVALUATE_VALUE("object.method_QString(object)", QV4::Primitive::undefinedValue()));
2896 QCOMPARE(o->error(), false);
2897 QCOMPARE(o->invoked(), 11);
2898 QCOMPARE(o->actuals().count(), 1);
2899 QCOMPARE(o->actuals().at(0), QVariant(expected));
2900 }
2901
2902 o->reset();
2903 QVERIFY(EVALUATE_VALUE("object.method_QString(null)", QV4::Primitive::undefinedValue()));
2904 QCOMPARE(o->error(), false);
2905 QCOMPARE(o->invoked(), 11);
2906 QCOMPARE(o->actuals().count(), 1);
2907 QCOMPARE(o->actuals().at(0), QVariant(QString()));
2908
2909 o->reset();
2910 QVERIFY(EVALUATE_VALUE("object.method_QString(undefined)", QV4::Primitive::undefinedValue()));
2911 QCOMPARE(o->error(), false);
2912 QCOMPARE(o->invoked(), 11);
2913 QCOMPARE(o->actuals().count(), 1);
2914 QCOMPARE(o->actuals().at(0), QVariant(QString()));
2915
2916 o->reset();
2917 QVERIFY(EVALUATE_VALUE("object.method_QPointF(0)", QV4::Primitive::undefinedValue()));
2918 QCOMPARE(o->error(), false);
2919 QCOMPARE(o->invoked(), 12);
2920 QCOMPARE(o->actuals().count(), 1);
2921 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2922
2923 o->reset();
2924 QVERIFY(EVALUATE_VALUE("object.method_QPointF(null)", QV4::Primitive::undefinedValue()));
2925 QCOMPARE(o->error(), false);
2926 QCOMPARE(o->invoked(), 12);
2927 QCOMPARE(o->actuals().count(), 1);
2928 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2929
2930 o->reset();
2931 QVERIFY(EVALUATE_VALUE("object.method_QPointF(undefined)", QV4::Primitive::undefinedValue()));
2932 QCOMPARE(o->error(), false);
2933 QCOMPARE(o->invoked(), 12);
2934 QCOMPARE(o->actuals().count(), 1);
2935 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2936
2937 o->reset();
2938 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object)", QV4::Primitive::undefinedValue()));
2939 QCOMPARE(o->error(), false);
2940 QCOMPARE(o->invoked(), 12);
2941 QCOMPARE(o->actuals().count(), 1);
2942 QCOMPARE(o->actuals().at(0), QVariant(QPointF()));
2943
2944 o->reset();
2945 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPointF())", QV4::Primitive::undefinedValue()));
2946 QCOMPARE(o->error(), false);
2947 QCOMPARE(o->invoked(), 12);
2948 QCOMPARE(o->actuals().count(), 1);
2949 QCOMPARE(o->actuals().at(0), QVariant(QPointF(99.3, -10.2)));
2950
2951 o->reset();
2952 QVERIFY(EVALUATE_VALUE("object.method_QPointF(object.method_get_QPoint())", QV4::Primitive::undefinedValue()));
2953 QCOMPARE(o->error(), false);
2954 QCOMPARE(o->invoked(), 12);
2955 QCOMPARE(o->actuals().count(), 1);
2956 QCOMPARE(o->actuals().at(0), QVariant(QPointF(9, 12)));
2957
2958 o->reset();
2959 QVERIFY(EVALUATE_VALUE("object.method_QObject(0)", QV4::Primitive::undefinedValue()));
2960 QCOMPARE(o->error(), false);
2961 QCOMPARE(o->invoked(), 13);
2962 QCOMPARE(o->actuals().count(), 1);
2963 QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
2964
2965 o->reset();
2966 QVERIFY(EVALUATE_VALUE("object.method_QObject(\"Hello world\")", QV4::Primitive::undefinedValue()));
2967 QCOMPARE(o->error(), false);
2968 QCOMPARE(o->invoked(), 13);
2969 QCOMPARE(o->actuals().count(), 1);
2970 QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
2971
2972 o->reset();
2973 QVERIFY(EVALUATE_VALUE("object.method_QObject(null)", QV4::Primitive::undefinedValue()));
2974 QCOMPARE(o->error(), false);
2975 QCOMPARE(o->invoked(), 13);
2976 QCOMPARE(o->actuals().count(), 1);
2977 QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
2978
2979 o->reset();
2980 QVERIFY(EVALUATE_VALUE("object.method_QObject(undefined)", QV4::Primitive::undefinedValue()));
2981 QCOMPARE(o->error(), false);
2982 QCOMPARE(o->invoked(), 13);
2983 QCOMPARE(o->actuals().count(), 1);
2984 QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)nullptr));
2985
2986 o->reset();
2987 QVERIFY(EVALUATE_VALUE("object.method_QObject(object)", QV4::Primitive::undefinedValue()));
2988 QCOMPARE(o->error(), false);
2989 QCOMPARE(o->invoked(), 13);
2990 QCOMPARE(o->actuals().count(), 1);
2991 QCOMPARE(o->actuals().at(0), QVariant::fromValue((QObject *)o));
2992
2993 o->reset();
2994 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", QV4::Primitive::undefinedValue()));
2995 QCOMPARE(o->error(), false);
2996 QCOMPARE(o->invoked(), 14);
2997 QCOMPARE(o->actuals().count(), 1);
2998 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).isNull());
2999
3000 o->reset();
3001 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", QV4::Primitive::undefinedValue()));
3002 QCOMPARE(o->error(), false);
3003 QCOMPARE(o->invoked(), 14);
3004 QCOMPARE(o->actuals().count(), 1);
3005 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).isUndefined());
3006
3007 o->reset();
3008 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", QV4::Primitive::undefinedValue()));
3009 QCOMPARE(o->error(), false);
3010 QCOMPARE(o->invoked(), 14);
3011 QCOMPARE(o->actuals().count(), 1);
3012 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).strictlyEquals(QJSValue(19)));
3013
3014 o->reset();
3015 QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", QV4::Primitive::undefinedValue()));
3016 QCOMPARE(o->error(), false);
3017 QCOMPARE(o->invoked(), 14);
3018 QCOMPARE(o->actuals().count(), 1);
3019 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(0)).isArray());
3020
3021 o->reset();
3022 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", QV4::Primitive::undefinedValue()));
3023 QCOMPARE(o->error(), false);
3024 QCOMPARE(o->invoked(), 15);
3025 QCOMPARE(o->actuals().count(), 2);
3026 QCOMPARE(o->actuals().at(0), QVariant(4));
3027 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).isNull());
3028
3029 o->reset();
3030 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", QV4::Primitive::undefinedValue()));
3031 QCOMPARE(o->error(), false);
3032 QCOMPARE(o->invoked(), 15);
3033 QCOMPARE(o->actuals().count(), 2);
3034 QCOMPARE(o->actuals().at(0), QVariant(8));
3035 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).isUndefined());
3036
3037 o->reset();
3038 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", QV4::Primitive::undefinedValue()));
3039 QCOMPARE(o->error(), false);
3040 QCOMPARE(o->invoked(), 15);
3041 QCOMPARE(o->actuals().count(), 2);
3042 QCOMPARE(o->actuals().at(0), QVariant(3));
3043 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).strictlyEquals(QJSValue(19)));
3044
3045 o->reset();
3046 QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", QV4::Primitive::undefinedValue()));
3047 QCOMPARE(o->error(), false);
3048 QCOMPARE(o->invoked(), 15);
3049 QCOMPARE(o->actuals().count(), 2);
3050 QCOMPARE(o->actuals().at(0), QVariant(44));
3051 QVERIFY(qvariant_cast<QJSValue>(o->actuals().at(1)).isArray());
3052
3053 o->reset();
3054 QVERIFY(EVALUATE_ERROR("object.method_overload()"));
3055 QCOMPARE(o->error(), false);
3056 QCOMPARE(o->invoked(), -1);
3057 QCOMPARE(o->actuals().count(), 0);
3058
3059 o->reset();
3060 QVERIFY(EVALUATE_VALUE("object.method_overload(10)", QV4::Primitive::undefinedValue()));
3061 QCOMPARE(o->error(), false);
3062 QCOMPARE(o->invoked(), 16);
3063 QCOMPARE(o->actuals().count(), 1);
3064 QCOMPARE(o->actuals().at(0), QVariant(10));
3065
3066 o->reset();
3067 QVERIFY(EVALUATE_VALUE("object.method_overload(10, 11)", QV4::Primitive::undefinedValue()));
3068 QCOMPARE(o->error(), false);
3069 QCOMPARE(o->invoked(), 17);
3070 QCOMPARE(o->actuals().count(), 2);
3071 QCOMPARE(o->actuals().at(0), QVariant(10));
3072 QCOMPARE(o->actuals().at(1), QVariant(11));
3073
3074 o->reset();
3075 QVERIFY(EVALUATE_VALUE("object.method_overload(\"Hello\")", QV4::Primitive::undefinedValue()));
3076 QCOMPARE(o->error(), false);
3077 QCOMPARE(o->invoked(), 18);
3078 QCOMPARE(o->actuals().count(), 1);
3079 QCOMPARE(o->actuals().at(0), QVariant(QString("Hello")));
3080
3081 o->reset();
3082 QVERIFY(EVALUATE_VALUE("object.method_with_enum(9)", QV4::Primitive::undefinedValue()));
3083 QCOMPARE(o->error(), false);
3084 QCOMPARE(o->invoked(), 19);
3085 QCOMPARE(o->actuals().count(), 1);
3086 QCOMPARE(o->actuals().at(0), QVariant(9));
3087
3088 o->reset();
3089 QVERIFY(EVALUATE_VALUE("object.method_default(10)", QV4::Primitive::fromInt32(19)));
3090 QCOMPARE(o->error(), false);
3091 QCOMPARE(o->invoked(), 20);
3092 QCOMPARE(o->actuals().count(), 2);
3093 QCOMPARE(o->actuals().at(0), QVariant(10));
3094 QCOMPARE(o->actuals().at(1), QVariant(19));
3095
3096 o->reset();
3097 QVERIFY(EVALUATE_VALUE("object.method_default(10, 13)", QV4::Primitive::fromInt32(13)));
3098 QCOMPARE(o->error(), false);
3099 QCOMPARE(o->invoked(), 20);
3100 QCOMPARE(o->actuals().count(), 2);
3101 QCOMPARE(o->actuals().at(0), QVariant(10));
3102 QCOMPARE(o->actuals().at(1), QVariant(13));
3103
3104 o->reset();
3105 QVERIFY(EVALUATE_VALUE("object.method_inherited(9)", QV4::Primitive::undefinedValue()));
3106 QCOMPARE(o->error(), false);
3107 QCOMPARE(o->invoked(), -3);
3108 QCOMPARE(o->actuals().count(), 1);
3109 QCOMPARE(o->actuals().at(0), QVariant(9));
3110
3111 o->reset();
3112 QVERIFY(EVALUATE_VALUE("object.method_QVariant(9)", QV4::Primitive::undefinedValue()));
3113 QCOMPARE(o->error(), false);
3114 QCOMPARE(o->invoked(), 21);
3115 QCOMPARE(o->actuals().count(), 2);
3116 QCOMPARE(o->actuals().at(0), QVariant(9));
3117 QCOMPARE(o->actuals().at(1), QVariant());
3118
3119 o->reset();
3120 QVERIFY(EVALUATE_VALUE("object.method_QVariant(\"Hello\", \"World\")", QV4::Primitive::undefinedValue()));
3121 QCOMPARE(o->error(), false);
3122 QCOMPARE(o->invoked(), 21);
3123 QCOMPARE(o->actuals().count(), 2);
3124 QCOMPARE(o->actuals().at(0), QVariant(QString("Hello")));
3125 QCOMPARE(o->actuals().at(1), QVariant(QString("World")));
3126
3127 o->reset();
3128 QVERIFY(EVALUATE_VALUE("object.method_QJsonObject({foo:123})", QV4::Primitive::undefinedValue()));
3129 QCOMPARE(o->error(), false);
3130 QCOMPARE(o->invoked(), 22);
3131 QCOMPARE(o->actuals().count(), 1);
3132 QCOMPARE(qvariant_cast<QJsonObject>(o->actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
3133
3134 o->reset();
3135 QVERIFY(EVALUATE_VALUE("object.method_QJsonArray([123])", QV4::Primitive::undefinedValue()));
3136 QCOMPARE(o->error(), false);
3137 QCOMPARE(o->invoked(), 23);
3138 QCOMPARE(o->actuals().count(), 1);
3139 QCOMPARE(qvariant_cast<QJsonArray>(o->actuals().at(0)), QJsonDocument::fromJson("[123]").array());
3140
3141 o->reset();
3142 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(123)", QV4::Primitive::undefinedValue()));
3143 QCOMPARE(o->error(), false);
3144 QCOMPARE(o->invoked(), 24);
3145 QCOMPARE(o->actuals().count(), 1);
3146 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(123));
3147
3148 o->reset();
3149 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(42.35)", QV4::Primitive::undefinedValue()));
3150 QCOMPARE(o->error(), false);
3151 QCOMPARE(o->invoked(), 24);
3152 QCOMPARE(o->actuals().count(), 1);
3153 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(42.35));
3154
3155 o->reset();
3156 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue('ciao')", QV4::Primitive::undefinedValue()));
3157 QCOMPARE(o->error(), false);
3158 QCOMPARE(o->invoked(), 24);
3159 QCOMPARE(o->actuals().count(), 1);
3160 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QStringLiteral("ciao")));
3161
3162 o->reset();
3163 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(true)", QV4::Primitive::undefinedValue()));
3164 QCOMPARE(o->error(), false);
3165 QCOMPARE(o->invoked(), 24);
3166 QCOMPARE(o->actuals().count(), 1);
3167 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(true));
3168
3169 o->reset();
3170 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(false)", QV4::Primitive::undefinedValue()));
3171 QCOMPARE(o->error(), false);
3172 QCOMPARE(o->invoked(), 24);
3173 QCOMPARE(o->actuals().count(), 1);
3174 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(false));
3175
3176 o->reset();
3177 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(null)", QV4::Primitive::undefinedValue()));
3178 QCOMPARE(o->error(), false);
3179 QCOMPARE(o->invoked(), 24);
3180 QCOMPARE(o->actuals().count(), 1);
3181 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Null));
3182
3183 o->reset();
3184 QVERIFY(EVALUATE_VALUE("object.method_QJsonValue(undefined)", QV4::Primitive::undefinedValue()));
3185 QCOMPARE(o->error(), false);
3186 QCOMPARE(o->invoked(), 24);
3187 QCOMPARE(o->actuals().count(), 1);
3188 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Undefined));
3189
3190 o->reset();
3191 QVERIFY(EVALUATE_VALUE("object.method_overload({foo:123})", QV4::Primitive::undefinedValue()));
3192 QCOMPARE(o->error(), false);
3193 QCOMPARE(o->invoked(), 25);
3194 QCOMPARE(o->actuals().count(), 1);
3195 QCOMPARE(qvariant_cast<QJsonObject>(o->actuals().at(0)), QJsonDocument::fromJson("{\"foo\":123}").object());
3196
3197 o->reset();
3198 QVERIFY(EVALUATE_VALUE("object.method_overload([123])", QV4::Primitive::undefinedValue()));
3199 QCOMPARE(o->error(), false);
3200 QCOMPARE(o->invoked(), 26);
3201 QCOMPARE(o->actuals().count(), 1);
3202 QCOMPARE(qvariant_cast<QJsonArray>(o->actuals().at(0)), QJsonDocument::fromJson("[123]").array());
3203
3204 o->reset();
3205 QVERIFY(EVALUATE_VALUE("object.method_overload(null)", QV4::Primitive::undefinedValue()));
3206 QCOMPARE(o->error(), false);
3207 QCOMPARE(o->invoked(), 27);
3208 QCOMPARE(o->actuals().count(), 1);
3209 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Null));
3210
3211 o->reset();
3212 QVERIFY(EVALUATE_VALUE("object.method_overload(undefined)", QV4::Primitive::undefinedValue()));
3213 QCOMPARE(o->error(), false);
3214 QCOMPARE(o->invoked(), 27);
3215 QCOMPARE(o->actuals().count(), 1);
3216 QCOMPARE(qvariant_cast<QJsonValue>(o->actuals().at(0)), QJsonValue(QJsonValue::Undefined));
3217
3218 o->reset();
3219 QVERIFY(EVALUATE_ERROR("object.method_unknown(null)"));
3220 QCOMPARE(o->error(), false);
3221 QCOMPARE(o->invoked(), -1);
3222 QCOMPARE(o->actuals().count(), 0);
3223
3224 o->reset();
3225 QVERIFY(EVALUATE_VALUE("object.method_QByteArray(\"Hello\")", QV4::Primitive::undefinedValue()));
3226 QCOMPARE(o->error(), false);
3227 QCOMPARE(o->invoked(), 29);
3228 QCOMPARE(o->actuals().count(), 1);
3229 QCOMPARE(qvariant_cast<QByteArray>(o->actuals().at(0)), QByteArray("Hello"));
3230
3231 o->reset();
3232 QV4::ScopedValue ret(scope, EVALUATE("object.method_intQJSValue(123, function() { return \"Hello world!\";})"));
3233 QCOMPARE(o->error(), false);
3234 QCOMPARE(o->invoked(), 30);
3235 QVERIFY(ret->isString());
3236 QCOMPARE(ret->toQStringNoThrow(), QString("Hello world!"));
3237 QCOMPARE(o->actuals().count(), 2);
3238 QCOMPARE(o->actuals().at(0), QVariant(123));
3239 QJSValue callback = qvariant_cast<QJSValue>(v: o->actuals().at(i: 1));
3240 QVERIFY(!callback.isNull());
3241 QVERIFY(callback.isCallable());
3242}
3243
3244void tst_qqmlecmascript::resolveClashingProperties()
3245{
3246 QScopedPointer<ClashingNames> o(new ClashingNames());
3247 QQmlEngine qmlengine;
3248
3249 QV4::ExecutionEngine *engine = qmlengine.handle();
3250 QV4::Scope scope(engine);
3251
3252 QV4::ScopedValue object(scope, QV4::QObjectWrapper::wrap(engine, object: o.get()));
3253 QV4::ObjectIterator it(scope, object->as<QV4::Object>(), QV4::ObjectIterator::EnumerableOnly);
3254 QV4::ScopedValue name(scope);
3255 QV4::ScopedValue value(scope);
3256
3257 bool seenProperty = false;
3258 bool seenMethod = false;
3259 while (true) {
3260 QV4::Value v;
3261 name = it.nextPropertyNameAsString(value: &v);
3262 if (name->isNull())
3263 break;
3264 QString key = name->toQStringNoThrow();
3265 if (key == QLatin1String("clashes")) {
3266 value = v;
3267 QV4::ScopedValue typeString(scope, QV4::Runtime::TypeofValue::call(engine, value));
3268 QString type = typeString->toQStringNoThrow();
3269 if (type == QLatin1String("boolean")) {
3270 QVERIFY(!seenProperty);
3271 seenProperty = true;
3272 } else if (type == QLatin1String("function")) {
3273 QVERIFY(!seenMethod);
3274 seenMethod = true;
3275 } else {
3276 QFAIL(qPrintable(QString::fromLatin1("found 'clashes' property of type %1")
3277 .arg(type)));
3278 }
3279 }
3280 }
3281 QVERIFY(seenProperty);
3282 QVERIFY(seenMethod);
3283}
3284
3285// QTBUG-13047 (check that you can pass registered object types as args)
3286void tst_qqmlecmascript::invokableObjectArg()
3287{
3288 QQmlEngine engine;
3289 QQmlComponent component(&engine, testFileUrl(fileName: "invokableObjectArg.qml"));
3290
3291 QObject *o = component.create();
3292 QVERIFY(o);
3293 MyQmlObject *qmlobject = qobject_cast<MyQmlObject *>(object: o);
3294 QVERIFY(qmlobject);
3295 QCOMPARE(qmlobject->myinvokableObject, qmlobject);
3296
3297 delete o;
3298}
3299
3300// QTBUG-13047 (check that you can return registered object types from methods)
3301void tst_qqmlecmascript::invokableObjectRet()
3302{
3303 QQmlEngine engine;
3304 QQmlComponent component(&engine, testFileUrl(fileName: "invokableObjectRet.qml"));
3305
3306 QObject *o = component.create();
3307 QVERIFY(o);
3308 QCOMPARE(o->property("test").toBool(), true);
3309 delete o;
3310}
3311
3312void tst_qqmlecmascript::invokableEnumRet()
3313{
3314 QQmlEngine engine;
3315 QQmlComponent component(&engine, testFileUrl(fileName: "invokableEnumRet.qml"));
3316
3317 QObject *o = component.create();
3318 QVERIFY(o);
3319 QCOMPARE(o->property("test").toBool(), true);
3320 delete o;
3321}
3322
3323// QTBUG-5675
3324void tst_qqmlecmascript::listToVariant()
3325{
3326 QQmlEngine engine;
3327 QQmlComponent component(&engine, testFileUrl(fileName: "listToVariant.qml"));
3328
3329 MyQmlContainer container;
3330
3331 QQmlContext context(engine.rootContext());
3332 context.setContextObject(&container);
3333
3334 QObject *object = component.create(context: &context);
3335 QVERIFY(object != nullptr);
3336
3337 QVariant v = object->property(name: "test");
3338 QCOMPARE(v.userType(), qMetaTypeId<QQmlListReference>());
3339 QCOMPARE(qvariant_cast<QQmlListReference>(v).object(), &container);
3340
3341 delete object;
3342}
3343
3344// QTBUG-16316
3345void tst_qqmlecmascript::listAssignment()
3346{
3347 QQmlEngine engine;
3348 QQmlComponent component(&engine, testFileUrl(fileName: "listAssignment.qml"));
3349 QObject *obj = component.create();
3350 QCOMPARE(obj->property("list1length").toInt(), 2);
3351 QQmlListProperty<MyQmlObject> list1 = obj->property(name: "list1").value<QQmlListProperty<MyQmlObject> >();
3352 QQmlListProperty<MyQmlObject> list2 = obj->property(name: "list2").value<QQmlListProperty<MyQmlObject> >();
3353 QCOMPARE(list1.count(&list1), list2.count(&list2));
3354 QCOMPARE(list1.at(&list1, 0), list2.at(&list2, 0));
3355 QCOMPARE(list1.at(&list1, 1), list2.at(&list2, 1));
3356 delete obj;
3357}
3358
3359// QTBUG-7957
3360void tst_qqmlecmascript::multiEngineObject()
3361{
3362 MyQmlObject obj;
3363 obj.setStringProperty("Howdy planet");
3364
3365 QQmlEngine e1;
3366 e1.rootContext()->setContextProperty("thing", &obj);
3367 QQmlComponent c1(&e1, testFileUrl(fileName: "multiEngineObject.qml"));
3368
3369 QQmlEngine e2;
3370 e2.rootContext()->setContextProperty("thing", &obj);
3371 QQmlComponent c2(&e2, testFileUrl(fileName: "multiEngineObject.qml"));
3372
3373 QObject *o1 = c1.create();
3374 QObject *o2 = c2.create();
3375
3376 QCOMPARE(o1->property("test").toString(), QString("Howdy planet"));
3377 QCOMPARE(o2->property("test").toString(), QString("Howdy planet"));
3378
3379 delete o2;
3380 delete o1;
3381}
3382
3383// Test that references to QObjects are cleanup when the object is destroyed
3384void tst_qqmlecmascript::deletedObject()
3385{
3386 QQmlEngine engine;
3387 QQmlComponent component(&engine, testFileUrl(fileName: "deletedObject.qml"));
3388
3389 QObject *object = component.create();
3390
3391 QCOMPARE(object->property("test1").toBool(), true);
3392 QCOMPARE(object->property("test2").toBool(), true);
3393 QCOMPARE(object->property("test3").toBool(), true);
3394 QCOMPARE(object->property("test4").toBool(), true);
3395
3396 delete object;
3397}
3398
3399void tst_qqmlecmascript::attachedPropertyScope()
3400{
3401 QQmlEngine engine;
3402 QQmlComponent component(&engine, testFileUrl(fileName: "attachedPropertyScope.qml"));
3403
3404 QObject *object = component.create();
3405 QVERIFY(object != nullptr);
3406
3407 MyQmlAttachedObject *attached =
3408 qobject_cast<MyQmlAttachedObject *>(object: qmlAttachedPropertiesObject<MyQmlObject>(obj: object));
3409 QVERIFY(attached != nullptr);
3410
3411 QCOMPARE(object->property("value2").toInt(), 0);
3412
3413 attached->emitMySignal();
3414
3415 QCOMPARE(object->property("value2").toInt(), 9);
3416
3417 delete object;
3418}
3419
3420void tst_qqmlecmascript::scriptConnect()
3421{
3422 QQmlEngine engine;
3423
3424 {
3425 QQmlComponent component(&engine, testFileUrl(fileName: "scriptConnect.1.qml"));
3426
3427 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3428 QVERIFY(object != nullptr);
3429
3430 QCOMPARE(object->property("test").toBool(), false);
3431 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3432 QCOMPARE(object->property("test").toBool(), true);
3433
3434 delete object;
3435 }
3436
3437 {
3438 QQmlComponent component(&engine, testFileUrl(fileName: "scriptConnect.2.qml"));
3439
3440 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3441 QVERIFY(object != nullptr);
3442
3443 QCOMPARE(object->property("test").toBool(), false);
3444 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3445 QCOMPARE(object->property("test").toBool(), true);
3446
3447 delete object;
3448 }
3449
3450 {
3451 QQmlComponent component(&engine, testFileUrl(fileName: "scriptConnect.3.qml"));
3452
3453 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3454 QVERIFY(object != nullptr);
3455
3456 QCOMPARE(object->property("test").toBool(), false);
3457 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3458 QCOMPARE(object->property("test").toBool(), true);
3459
3460 delete object;
3461 }
3462
3463 {
3464 QQmlComponent component(&engine, testFileUrl(fileName: "scriptConnect.4.qml"));
3465
3466 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3467 QVERIFY(object != nullptr);
3468
3469 QCOMPARE(object->methodCalled(), false);
3470 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3471 QCOMPARE(object->methodCalled(), true);
3472
3473 delete object;
3474 }
3475
3476 {
3477 QQmlComponent component(&engine, testFileUrl(fileName: "scriptConnect.5.qml"));
3478
3479 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3480 QVERIFY(object != nullptr);
3481
3482 QCOMPARE(object->methodCalled(), false);
3483 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3484 QCOMPARE(object->methodCalled(), true);
3485
3486 delete object;
3487 }
3488
3489 {
3490 QQmlComponent component(&engine, testFileUrl(fileName: "scriptConnect.6.qml"));
3491
3492 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3493 QVERIFY(object != nullptr);
3494
3495 QCOMPARE(object->property("test").toInt(), 0);
3496 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3497 QCOMPARE(object->property("test").toInt(), 2);
3498
3499 delete object;
3500 }
3501}
3502
3503void tst_qqmlecmascript::scriptDisconnect()
3504{
3505 QQmlEngine engine;
3506
3507 {
3508 QQmlComponent component(&engine, testFileUrl(fileName: "scriptDisconnect.1.qml"));
3509
3510 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3511 QVERIFY(object != nullptr);
3512
3513 QCOMPARE(object->property("test").toInt(), 0);
3514 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3515 QCOMPARE(object->property("test").toInt(), 1);
3516 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3517 QCOMPARE(object->property("test").toInt(), 2);
3518 emit object->basicSignal();
3519 QCOMPARE(object->property("test").toInt(), 2);
3520 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3521 QCOMPARE(object->property("test").toInt(), 2);
3522
3523 delete object;
3524 }
3525
3526 {
3527 QQmlComponent component(&engine, testFileUrl(fileName: "scriptDisconnect.2.qml"));
3528
3529 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3530 QVERIFY(object != nullptr);
3531
3532 QCOMPARE(object->property("test").toInt(), 0);
3533 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3534 QCOMPARE(object->property("test").toInt(), 1);
3535 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3536 QCOMPARE(object->property("test").toInt(), 2);
3537 emit object->basicSignal();
3538 QCOMPARE(object->property("test").toInt(), 2);
3539 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3540 QCOMPARE(object->property("test").toInt(), 2);
3541
3542 delete object;
3543 }
3544
3545 {
3546 QQmlComponent component(&engine, testFileUrl(fileName: "scriptDisconnect.3.qml"));
3547
3548 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3549 QVERIFY(object != nullptr);
3550
3551 QCOMPARE(object->property("test").toInt(), 0);
3552 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3553 QCOMPARE(object->property("test").toInt(), 1);
3554 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3555 QCOMPARE(object->property("test").toInt(), 2);
3556 emit object->basicSignal();
3557 QCOMPARE(object->property("test").toInt(), 2);
3558 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3559 QCOMPARE(object->property("test").toInt(), 3);
3560
3561 delete object;
3562 }
3563 {
3564 QQmlComponent component(&engine, testFileUrl(fileName: "scriptDisconnect.4.qml"));
3565
3566 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3567 QVERIFY(object != nullptr);
3568
3569 QCOMPARE(object->property("test").toInt(), 0);
3570 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3571 QCOMPARE(object->property("test").toInt(), 1);
3572 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3573 QCOMPARE(object->property("test").toInt(), 2);
3574 emit object->basicSignal();
3575 QCOMPARE(object->property("test").toInt(), 2);
3576 emit object->argumentSignal(a: 19, b: "Hello world!", c: 10.25, d: MyQmlObject::EnumValue4, e: Qt::RightButton);
3577 QCOMPARE(object->property("test").toInt(), 3);
3578
3579 delete object;
3580 }
3581}
3582
3583class OwnershipObject : public QObject
3584{
3585 Q_OBJECT
3586public:
3587 OwnershipObject() { object = new QObject; }
3588
3589 QPointer<QObject> object;
3590
3591public slots:
3592 QObject *getObject() { return object; }
3593};
3594
3595void tst_qqmlecmascript::ownership()
3596{
3597 QQmlEngine engine;
3598 OwnershipObject own;
3599 QQmlContext *context = new QQmlContext(engine.rootContext());
3600 context->setContextObject(&own);
3601
3602 {
3603 QQmlComponent component(&engine, testFileUrl(fileName: "ownership.qml"));
3604
3605 QVERIFY(own.object != nullptr);
3606
3607 QObject *object = component.create(context);
3608
3609 engine.collectGarbage();
3610
3611 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
3612 QCoreApplication::processEvents();
3613
3614 QVERIFY(own.object.isNull());
3615
3616 delete object;
3617 }
3618
3619 own.object = new QObject(&own);
3620
3621 {
3622 QQmlComponent component(&engine, testFileUrl(fileName: "ownership.qml"));
3623
3624 QVERIFY(own.object != nullptr);
3625
3626 QObject *object = component.create(context);
3627
3628 engine.collectGarbage();
3629
3630 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
3631 QCoreApplication::processEvents();
3632
3633 QVERIFY(own.object != nullptr);
3634
3635 delete object;
3636 }
3637
3638 delete context;
3639}
3640
3641class CppOwnershipReturnValue : public QObject
3642{
3643 Q_OBJECT
3644public:
3645 CppOwnershipReturnValue() : value(nullptr) {}
3646 ~CppOwnershipReturnValue() { delete value; }
3647
3648 Q_INVOKABLE QObject *create() {
3649 value = new QObject;
3650 QQmlEngine::setObjectOwnership(value, QQmlEngine::CppOwnership);
3651 return value;
3652 }
3653
3654 Q_INVOKABLE MyQmlObject *createQmlObject() {
3655 MyQmlObject *rv = new MyQmlObject;
3656 value = rv;
3657 return rv;
3658 }
3659
3660 QPointer<QObject> value;
3661};
3662
3663// QTBUG-15695.
3664// Test setObjectOwnership(CppOwnership) works even when there is no QQmlData
3665void tst_qqmlecmascript::cppOwnershipReturnValue()
3666{
3667 CppOwnershipReturnValue source;
3668
3669 {
3670 QQmlEngine engine;
3671 engine.rootContext()->setContextProperty("source", &source);
3672
3673 QVERIFY(source.value.isNull());
3674
3675 QQmlComponent component(&engine);
3676 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.create(); }\n}\n", baseUrl: QUrl());
3677
3678 QObject *object = component.create();
3679
3680 QVERIFY(object != nullptr);
3681 QVERIFY(source.value != nullptr);
3682
3683 delete object;
3684 }
3685
3686 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
3687 QCoreApplication::processEvents();
3688
3689 QVERIFY(source.value != nullptr);
3690}
3691
3692// QTBUG-15697
3693void tst_qqmlecmascript::ownershipCustomReturnValue()
3694{
3695 QQmlEngine engine;
3696 CppOwnershipReturnValue source;
3697
3698 {
3699 QQmlEngine engine;
3700 engine.rootContext()->setContextProperty("source", &source);
3701
3702 QVERIFY(source.value.isNull());
3703
3704 QQmlComponent component(&engine);
3705 component.setData("import QtQuick 2.0\nQtObject {\nComponent.onCompleted: { var a = source.createQmlObject(); }\n}\n", baseUrl: QUrl());
3706
3707 QObject *object = component.create();
3708
3709 QVERIFY(object != nullptr);
3710 QVERIFY(source.value != nullptr);
3711
3712 delete object;
3713 }
3714
3715 engine.collectGarbage();
3716 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
3717 QCoreApplication::processEvents();
3718
3719 QVERIFY(source.value.isNull());
3720}
3721
3722//the return value from getObject will be JS ownership,
3723//unless strong Cpp ownership has been set
3724class OwnershipChangingObject : public QObject
3725{
3726 Q_OBJECT
3727public:
3728 OwnershipChangingObject(): object(nullptr) { }
3729
3730 QPointer<QObject> object;
3731
3732public slots:
3733 QObject *getObject() { return object; }
3734 void setObject(QObject *obj) { object = obj; }
3735};
3736
3737void tst_qqmlecmascript::ownershipRootObject()
3738{
3739 QQmlEngine engine;
3740 OwnershipChangingObject own;
3741 QQmlContext *context = new QQmlContext(engine.rootContext());
3742 context->setContextObject(&own);
3743
3744 QQmlComponent component(&engine, testFileUrl(fileName: "ownershipRootObject.qml"));
3745 QPointer<QObject> object = component.create(context);
3746 QVERIFY(object);
3747
3748 engine.collectGarbage();
3749
3750 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
3751 QCoreApplication::processEvents();
3752
3753 QVERIFY(own.object != nullptr);
3754
3755 delete context;
3756 delete object;
3757}
3758
3759void tst_qqmlecmascript::ownershipConsistency()
3760{
3761 QQmlEngine engine;
3762 OwnershipChangingObject own;
3763 QQmlContext *context = new QQmlContext(engine.rootContext());
3764 context->setContextObject(&own);
3765
3766 QString expectedWarning = testFileUrl(fileName: "ownershipConsistency.qml").toString() + QLatin1String(":19: Error: Invalid attempt to destroy() an indestructible object");
3767 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3768 expectedWarning = testFileUrl(fileName: "ownershipConsistency.qml").toString() + QLatin1String(":15: Error: Invalid attempt to destroy() an indestructible object");
3769 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3770 expectedWarning = testFileUrl(fileName: "ownershipConsistency.qml").toString() + QLatin1String(":6: Error: Invalid attempt to destroy() an indestructible object");
3771 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3772 expectedWarning = testFileUrl(fileName: "ownershipConsistency.qml").toString() + QLatin1String(":10: Error: Invalid attempt to destroy() an indestructible object");
3773 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
3774
3775 QQmlComponent component(&engine, testFileUrl(fileName: "ownershipConsistency.qml"));
3776 QPointer<QObject> object = component.create(context);
3777 QVERIFY(object);
3778
3779 engine.collectGarbage();
3780
3781 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
3782 QCoreApplication::processEvents();
3783
3784 QVERIFY(own.object != nullptr);
3785
3786 delete context;
3787 delete object;
3788}
3789
3790void tst_qqmlecmascript::ownershipQmlIncubated()
3791{
3792 QQmlEngine engine;
3793 QQmlComponent component(&engine, testFileUrl(fileName: "ownershipQmlIncubated.qml"));
3794 QObject *object = component.create();
3795 QVERIFY(object);
3796
3797 QTRY_VERIFY(object->property("incubatedItem").value<QObject*>() != 0);
3798
3799 QMetaObject::invokeMethod(obj: object, member: "deleteIncubatedItem");
3800
3801 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
3802 QCoreApplication::processEvents();
3803
3804 QVERIFY(!object->property("incubatedItem").value<QObject*>());
3805
3806 delete object;
3807}
3808
3809class QListQObjectMethodsObject : public QObject
3810{
3811 Q_OBJECT
3812public:
3813 QListQObjectMethodsObject() {
3814 m_objects.append(t: new MyQmlObject());
3815 m_objects.append(t: new MyQmlObject());
3816 }
3817
3818 ~QListQObjectMethodsObject() {
3819 qDeleteAll(c: m_objects);
3820 }
3821
3822public slots:
3823 QList<QObject *> getObjects() { return m_objects; }
3824
3825private:
3826 QList<QObject *> m_objects;
3827};
3828
3829// Tests that returning a QList<QObject*> from a method works
3830void tst_qqmlecmascript::qlistqobjectMethods()
3831{
3832 QQmlEngine engine;
3833 QListQObjectMethodsObject obj;
3834 QQmlContext *context = new QQmlContext(engine.rootContext());
3835 context->setContextObject(&obj);
3836
3837 QQmlComponent component(&engine, testFileUrl(fileName: "qlistqobjectMethods.qml"));
3838
3839 QObject *object = component.create(context);
3840
3841 QCOMPARE(object->property("test").toInt(), 2);
3842 QCOMPARE(object->property("test2").toBool(), true);
3843
3844 delete object;
3845 delete context;
3846}
3847
3848// QTBUG-9205
3849void tst_qqmlecmascript::strictlyEquals()
3850{
3851 QQmlEngine engine;
3852 QQmlComponent component(&engine, testFileUrl(fileName: "strictlyEquals.qml"));
3853
3854 QObject *object = component.create();
3855 QVERIFY(object != nullptr);
3856
3857 QCOMPARE(object->property("test1").toBool(), true);
3858 QCOMPARE(object->property("test2").toBool(), true);
3859 QCOMPARE(object->property("test3").toBool(), true);
3860 QCOMPARE(object->property("test4").toBool(), true);
3861 QCOMPARE(object->property("test5").toBool(), true);
3862 QCOMPARE(object->property("test6").toBool(), true);
3863 QCOMPARE(object->property("test7").toBool(), true);
3864 QCOMPARE(object->property("test8").toBool(), true);
3865
3866 delete object;
3867}
3868
3869void tst_qqmlecmascript::compiled()
3870{
3871 QQmlEngine engine;
3872 QQmlComponent component(&engine, testFileUrl(fileName: "compiled.qml"));
3873
3874 QObject *object = component.create();
3875 QVERIFY(object != nullptr);
3876
3877 QCOMPARE(object->property("test1").toReal(), qreal(15.7));
3878 QCOMPARE(object->property("test2").toReal(), qreal(-6.7));
3879 QCOMPARE(object->property("test3").toBool(), true);
3880 QCOMPARE(object->property("test4").toBool(), false);
3881 QCOMPARE(object->property("test5").toBool(), false);
3882 QCOMPARE(object->property("test6").toBool(), true);
3883
3884 QCOMPARE(object->property("test7").toInt(), 185);
3885 QCOMPARE(object->property("test8").toInt(), 167);
3886 QCOMPARE(object->property("test9").toBool(), true);
3887 QCOMPARE(object->property("test10").toBool(), false);
3888 QCOMPARE(object->property("test11").toBool(), false);
3889 QCOMPARE(object->property("test12").toBool(), true);
3890
3891 QCOMPARE(object->property("test13").toString(), QLatin1String("HelloWorld"));
3892 QCOMPARE(object->property("test14").toString(), QLatin1String("Hello World"));
3893 QCOMPARE(object->property("test15").toBool(), false);
3894 QCOMPARE(object->property("test16").toBool(), true);
3895
3896 QCOMPARE(object->property("test17").toInt(), 4);
3897 QCOMPARE(object->property("test18").toReal(), qreal(176));
3898 QCOMPARE(object->property("test19").toInt(), 6);
3899 QCOMPARE(object->property("test20").toReal(), qreal(6.5));
3900 QCOMPARE(object->property("test21").toString(), QLatin1String("6.5"));
3901 QCOMPARE(object->property("test22").toString(), QLatin1String("!"));
3902 QCOMPARE(object->property("test23").toBool(), true);
3903 QCOMPARE(qvariant_cast<QColor>(object->property("test24")), QColor(0x11,0x22,0x33));
3904 QCOMPARE(qvariant_cast<QColor>(object->property("test25")), QColor(0x11,0x22,0x33,0xAA));
3905
3906 delete object;
3907}
3908
3909// Test that numbers assigned in bindings as strings work consistently
3910void tst_qqmlecmascript::numberAssignment()
3911{
3912 QQmlEngine engine;
3913 QQmlComponent component(&engine, testFileUrl(fileName: "numberAssignment.qml"));
3914
3915 QObject *object = component.create();
3916 QVERIFY(object != nullptr);
3917
3918 QCOMPARE(object->property("test1"), QVariant((qreal)6.7));
3919 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3920 QCOMPARE(object->property("test2"), QVariant((qreal)6.7));
3921 QCOMPARE(object->property("test3"), QVariant((qreal)6));
3922 QCOMPARE(object->property("test4"), QVariant((qreal)6));
3923
3924 QCOMPARE(object->property("test5"), QVariant((int)6));
3925 QCOMPARE(object->property("test6"), QVariant((int)7));
3926 QCOMPARE(object->property("test7"), QVariant((int)6));
3927 QCOMPARE(object->property("test8"), QVariant((int)6));
3928
3929 QCOMPARE(object->property("test9"), QVariant((unsigned int)7));
3930 QCOMPARE(object->property("test10"), QVariant((unsigned int)7));
3931 QCOMPARE(object->property("test11"), QVariant((unsigned int)6));
3932 QCOMPARE(object->property("test12"), QVariant((unsigned int)6));
3933
3934 delete object;
3935}
3936
3937void tst_qqmlecmascript::propertySplicing()
3938{
3939 QQmlEngine engine;
3940 QQmlComponent component(&engine, testFileUrl(fileName: "propertySplicing.qml"));
3941
3942 QObject *object = component.create();
3943 QVERIFY(object != nullptr);
3944
3945 QCOMPARE(object->property("test").toBool(), true);
3946
3947 delete object;
3948}
3949
3950// QTBUG-16683
3951void tst_qqmlecmascript::signalWithUnknownTypes()
3952{
3953 QQmlEngine engine;
3954 QQmlComponent component(&engine, testFileUrl(fileName: "signalWithUnknownTypes.qml"));
3955
3956 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
3957 QVERIFY(object != nullptr);
3958
3959 MyQmlObject::MyType type;
3960 type.value = 0x8971123;
3961 emit object->signalWithUnknownType(arg: type);
3962
3963 MyQmlObject::MyType result = qvariant_cast<MyQmlObject::MyType>(v: object->variant());
3964
3965 QCOMPARE(result.value, type.value);
3966
3967 MyQmlObject::MyOtherType othertype;
3968 othertype.value = 17;
3969 emit object->signalWithCompletelyUnknownType(arg: othertype);
3970
3971 QVERIFY(!object->variant().isValid());
3972
3973 delete object;
3974}
3975
3976void tst_qqmlecmascript::signalWithJSValueInVariant_data()
3977{
3978 QTest::addColumn<QString>(name: "expression");
3979 QTest::addColumn<QString>(name: "compare");
3980
3981 QString compareStrict("(function(a, b) { return a === b; })");
3982 QTest::newRow(dataTag: "true") << "true" << compareStrict;
3983 QTest::newRow(dataTag: "undefined") << "undefined" << compareStrict;
3984 QTest::newRow(dataTag: "null") << "null" << compareStrict;
3985 QTest::newRow(dataTag: "123") << "123" << compareStrict;
3986 QTest::newRow(dataTag: "'ciao'") << "'ciao'" << compareStrict;
3987
3988 QString comparePropertiesStrict(
3989 "(function compareMe(a, b) {"
3990 " if (typeof b != 'object')"
3991 " return a === b;"
3992 " var props = Object.getOwnPropertyNames(b);"
3993 " for (var i = 0; i < props.length; ++i) {"
3994 " var p = props[i];"
3995 " return compareMe(a[p], b[p]);"
3996 " }"
3997 "})");
3998 QTest::newRow(dataTag: "{ foo: 'bar' }") << "({ foo: 'bar' })" << comparePropertiesStrict;
3999 QTest::newRow(dataTag: "[10,20,30]") << "[10,20,30]" << comparePropertiesStrict;
4000}
4001
4002void tst_qqmlecmascript::signalWithJSValueInVariant()
4003{
4004 QFETCH(QString, expression);
4005 QFETCH(QString, compare);
4006
4007 QQmlEngine engine;
4008 QQmlComponent component(&engine, testFileUrl(fileName: "signalWithJSValueInVariant.qml"));
4009 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(object: component.create()));
4010 QVERIFY(object != nullptr);
4011
4012 QJSValue value = engine.evaluate(program: expression);
4013 QVERIFY(!value.isError());
4014 object->setProperty(name: "expression", value: expression);
4015 object->setProperty(name: "compare", value: compare);
4016 object->setProperty(name: "pass", value: false);
4017
4018 emit object->signalWithVariant(arg: QVariant::fromValue(value));
4019 QVERIFY(object->property("pass").toBool());
4020}
4021
4022void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines_data()
4023{
4024 signalWithJSValueInVariant_data();
4025}
4026
4027void tst_qqmlecmascript::signalWithJSValueInVariant_twoEngines()
4028{
4029 QFETCH(QString, expression);
4030 QFETCH(QString, compare);
4031
4032 QQmlEngine engine;
4033 QQmlComponent component(&engine, testFileUrl(fileName: "signalWithJSValueInVariant.qml"));
4034 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(object: component.create()));
4035 QVERIFY(object != nullptr);
4036
4037 QJSEngine engine2;
4038 QJSValue value = engine2.evaluate(program: expression);
4039 QVERIFY(!value.isError());
4040 object->setProperty(name: "expression", value: expression);
4041 object->setProperty(name: "compare", value: compare);
4042 object->setProperty(name: "pass", value: false);
4043
4044 QTest::ignoreMessage(type: QtWarningMsg, message: "JSValue can't be reassigned to another engine.");
4045 emit object->signalWithVariant(arg: QVariant::fromValue(value));
4046 if (expression == "undefined")
4047 // if the engine is wrong, we return undefined to the other engine,
4048 // making this one case pass
4049 return;
4050 QVERIFY(!object->property("pass").toBool());
4051}
4052
4053void tst_qqmlecmascript::signalWithQJSValue_data()
4054{
4055 signalWithJSValueInVariant_data();
4056}
4057
4058void tst_qqmlecmascript::signalWithQJSValue()
4059{
4060 QFETCH(QString, expression);
4061 QFETCH(QString, compare);
4062
4063 QQmlEngine engine;
4064 QQmlComponent component(&engine, testFileUrl(fileName: "signalWithQJSValue.qml"));
4065 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject *>(object: component.create()));
4066 QVERIFY(object != nullptr);
4067
4068 QJSValue value = engine.evaluate(program: expression);
4069 QVERIFY(!value.isError());
4070 object->setProperty(name: "expression", value: expression);
4071 object->setProperty(name: "compare", value: compare);
4072 object->setProperty(name: "pass", value: false);
4073
4074 emit object->signalWithQJSValue(arg: value);
4075
4076 QVERIFY(object->property("pass").toBool());
4077 QVERIFY(object->qjsvalue().strictlyEquals(value));
4078}
4079
4080void tst_qqmlecmascript::singletonType_data()
4081{
4082 QTest::addColumn<QUrl>(name: "testfile");
4083 QTest::addColumn<QString>(name: "errorMessage");
4084 QTest::addColumn<QStringList>(name: "warningMessages");
4085 QTest::addColumn<QStringList>(name: "readProperties");
4086 QTest::addColumn<QVariantList>(name: "readExpectedValues");
4087 QTest::addColumn<QStringList>(name: "writeProperties");
4088 QTest::addColumn<QVariantList>(name: "writeValues");
4089 QTest::addColumn<QStringList>(name: "readBackProperties");
4090 QTest::addColumn<QVariantList>(name: "readBackExpectedValues");
4091
4092 QTest::newRow(dataTag: "qobject, register + read + method [no qualifier]")
4093 << testFileUrl(fileName: "singletontype/qobjectSingletonTypeNoQualifier.qml")
4094 << QString()
4095 << QStringList()
4096 << (QStringList() << "qobjectPropertyTest" << "qobjectMethodTest")
4097 << (QVariantList() << 20 << 1)
4098 << QStringList()
4099 << QVariantList()
4100 << QStringList()
4101 << QVariantList();
4102
4103 QTest::newRow(dataTag: "script, register + read [no qualifier]")
4104 << testFileUrl(fileName: "singletontype/scriptSingletonTypeNoQualifier.qml")
4105 << QString()
4106 << QStringList()
4107 << (QStringList() << "scriptTest")
4108 << (QVariantList() << 13)
4109 << QStringList()
4110 << QVariantList()
4111 << QStringList()
4112 << QVariantList();
4113
4114 QTest::newRow(dataTag: "qobject, register + read + method")
4115 << testFileUrl(fileName: "singletontype/qobjectSingletonType.qml")
4116 << QString()
4117 << QStringList()
4118 << (QStringList() << "existingUriTest" << "qobjectTest" << "qobjectMethodTest"
4119 << "qobjectMinorVersionMethodTest" << "qobjectMinorVersionTest"
4120 << "qobjectMajorVersionTest" << "qobjectParentedTest")
4121 << (QVariantList() << 20 << 20 << 2 << 1 << 20 << 20 << 26)
4122 << QStringList()
4123 << QVariantList()
4124 << QStringList()
4125 << QVariantList();
4126
4127 QTest::newRow(dataTag: "script, register + read")
4128 << testFileUrl(fileName: "singletontype/scriptSingletonType.qml")
4129 << QString()
4130 << QStringList()
4131 << (QStringList() << "scriptTest")
4132 << (QVariantList() << 14) // will have incremented, since we create a new engine each row in this test.
4133 << QStringList()
4134 << QVariantList()
4135 << QStringList()
4136 << QVariantList();
4137
4138 QTest::newRow(dataTag: "qobject, writing + readonly constraints")
4139 << testFileUrl(fileName: "singletontype/qobjectSingletonTypeWriting.qml")
4140 << QString()
4141 << (QStringList() <<
4142 QString(testFileUrl(fileName: "singletontype/qobjectSingletonTypeWriting.qml").toString() + QLatin1String(":15: TypeError: Cannot assign to read-only property \"qobjectTestProperty\"")))
4143 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
4144 << (QVariantList() << 20 << 50 << 10)
4145 << (QStringList() << "firstProperty" << "secondProperty")
4146 << (QVariantList() << 30 << 30)
4147 << (QStringList() << "readOnlyProperty" << "writableProperty" << "writableFinalProperty")
4148 << (QVariantList() << 20 << 30 << 30);
4149
4150 QTest::newRow(dataTag: "script, writing + readonly constraints")
4151 << testFileUrl(fileName: "singletontype/scriptSingletonTypeWriting.qml")
4152 << QString()
4153 << (QStringList())
4154 << (QStringList() << "readBack" << "unchanged")
4155 << (QVariantList() << 15 << 42)
4156 << (QStringList() << "firstProperty" << "secondProperty")
4157 << (QVariantList() << 30 << 30)
4158 << (QStringList() << "readBack" << "unchanged")
4159 << (QVariantList() << 30 << 42);
4160
4161 QTest::newRow(dataTag: "qobject singleton Type enum values in JS")
4162 << testFileUrl(fileName: "singletontype/qobjectSingletonTypeEnums.qml")
4163 << QString()
4164 << QStringList()
4165 << (QStringList() << "enumValue" << "enumMethod")
4166 << (QVariantList() << 42 << 30)
4167 << QStringList()
4168 << QVariantList()
4169 << QStringList()
4170 << QVariantList();
4171
4172 QTest::newRow(dataTag: "qobject, invalid major version fail")
4173 << testFileUrl(fileName: "singletontype/singletonTypeMajorVersionFail.qml")
4174 << QString("QQmlComponent: Component is not ready")
4175 << QStringList()
4176 << QStringList()
4177 << QVariantList()
4178 << QStringList()
4179 << QVariantList()
4180 << QStringList()
4181 << QVariantList();
4182
4183 QTest::newRow(dataTag: "qobject, invalid minor version fail")
4184 << testFileUrl(fileName: "singletontype/singletonTypeMinorVersionFail.qml")
4185 << QString("QQmlComponent: Component is not ready")
4186 << QStringList()
4187 << QStringList()
4188 << QVariantList()
4189 << QStringList()
4190 << QVariantList()
4191 << QStringList()
4192 << QVariantList();
4193
4194 QTest::newRow(dataTag: "qobject, multiple in namespace")
4195 << testFileUrl(fileName: "singletontype/singletonTypeMultiple.qml")
4196 << QString()
4197 << QStringList()
4198 << (QStringList() << "first" << "second")
4199 << (QVariantList() << 35 << 42)
4200 << QStringList()
4201 << QVariantList()
4202 << QStringList()
4203 << QVariantList();
4204}
4205
4206void tst_qqmlecmascript::singletonType()
4207{
4208 QFETCH(QUrl, testfile);
4209 QFETCH(QString, errorMessage);
4210 QFETCH(QStringList, warningMessages);
4211 QFETCH(QStringList, readProperties);
4212 QFETCH(QVariantList, readExpectedValues);
4213 QFETCH(QStringList, writeProperties);
4214 QFETCH(QVariantList, writeValues);
4215 QFETCH(QStringList, readBackProperties);
4216 QFETCH(QVariantList, readBackExpectedValues);
4217
4218 QQmlEngine cleanEngine; // so tests don't interfere which each other, as singleton types are engine-singletons only.
4219 QQmlComponent component(&cleanEngine, testfile);
4220
4221 if (!errorMessage.isEmpty())
4222 QTest::ignoreMessage(type: QtWarningMsg, message: errorMessage.toLatin1().constData());
4223
4224 if (warningMessages.size())
4225 foreach (const QString &warning, warningMessages)
4226 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
4227
4228 QObject *object = component.create();
4229 if (!errorMessage.isEmpty()) {
4230 QVERIFY(!object);
4231 } else {
4232 QVERIFY(object != nullptr);
4233 for (int i = 0; i < readProperties.size(); ++i)
4234 QCOMPARE(object->property(readProperties.at(i).toLatin1().constData()), readExpectedValues.at(i));
4235 for (int i = 0; i < writeProperties.size(); ++i)
4236 QVERIFY(object->setProperty(writeProperties.at(i).toLatin1().constData(), writeValues.at(i)));
4237 for (int i = 0; i < readBackProperties.size(); ++i)
4238 QCOMPARE(object->property(readBackProperties.at(i).toLatin1().constData()), readBackExpectedValues.at(i));
4239 delete object;
4240 }
4241}
4242
4243void tst_qqmlecmascript::singletonTypeCaching_data()
4244{
4245 QTest::addColumn<QUrl>(name: "testfile");
4246 QTest::addColumn<QStringList>(name: "readProperties");
4247
4248 QTest::newRow(dataTag: "qobject, caching + read")
4249 << testFileUrl(fileName: "singletontype/qobjectSingletonTypeCaching.qml")
4250 << (QStringList() << "existingUriTest" << "qobjectParentedTest");
4251
4252 QTest::newRow(dataTag: "script, caching + read")
4253 << testFileUrl(fileName: "singletontype/scriptSingletonTypeCaching.qml")
4254 << (QStringList() << "scriptTest");
4255}
4256
4257void tst_qqmlecmascript::singletonTypeCaching()
4258{
4259 QFETCH(QUrl, testfile);
4260 QFETCH(QStringList, readProperties);
4261
4262 // ensure that the singleton type instances are cached per-engine.
4263
4264 QQmlEngine cleanEngine;
4265 QQmlComponent component(&cleanEngine, testfile);
4266 QObject *object = component.create();
4267 QVERIFY(object != nullptr);
4268 QList<QVariant> firstValues;
4269 QMetaObject::invokeMethod(obj: object, member: "modifyValues");
4270 for (int i = 0; i < readProperties.size(); ++i)
4271 firstValues << object->property(name: readProperties.at(i).toLatin1().constData());
4272 delete object;
4273
4274 QQmlComponent component2(&cleanEngine, testfile);
4275 QObject *object2 = component2.create();
4276 QVERIFY(object2 != nullptr);
4277 for (int i = 0; i < readProperties.size(); ++i)
4278 QCOMPARE(object2->property(readProperties.at(i).toLatin1().constData()), firstValues.at(i)); // cached, shouldn't have changed.
4279 delete object2;
4280}
4281
4282void tst_qqmlecmascript::singletonTypeImportOrder()
4283{
4284 QQmlEngine engine;
4285 QQmlComponent component(&engine, testFileUrl(fileName: "singletontype/singletonTypeImportOrder.qml"));
4286 QObject *object = component.create();
4287 QVERIFY(object);
4288 QCOMPARE(object->property("v").toInt(), 1);
4289 delete object;
4290}
4291
4292void tst_qqmlecmascript::singletonTypeResolution()
4293{
4294 QQmlEngine engine;
4295 QQmlComponent component(&engine, testFileUrl(fileName: "singletontype/singletonTypeResolution.qml"));
4296 QObject *object = component.create();
4297 QVERIFY(object);
4298 QVERIFY(object->property("success").toBool());
4299 delete object;
4300}
4301
4302void tst_qqmlecmascript::verifyContextLifetime(QQmlContextData *ctxt) {
4303 QQmlContextData *childCtxt = ctxt->childContexts;
4304
4305 if (!ctxt->importedScripts.isNullOrUndefined()) {
4306 QV4::ExecutionEngine *v4 = ctxt->engine->handle();
4307 QV4::Scope scope(v4);
4308 QV4::ScopedArrayObject scripts(scope, ctxt->importedScripts.value());
4309 QV4::Scoped<QV4::QQmlContextWrapper> qml(scope);
4310 for (quint32 i = 0; i < scripts->getLength(); ++i) {
4311 QQmlContextData *scriptContext, *newContext;
4312 qml = scripts->get(idx: i);
4313
4314 scriptContext = qml ? qml->getContext() : nullptr;
4315 qml = QV4::Encode::undefined();
4316
4317 {
4318 QV4::Scope scope(v4);
4319 QV4::ScopedContext temporaryScope(scope, QV4::QmlContext::create(parent: scope.engine->rootContext(), context: scriptContext, scopeObject: nullptr));
4320 Q_UNUSED(temporaryScope)
4321 }
4322
4323 ctxt->engine->collectGarbage();
4324 qml = scripts->get(idx: i);
4325 newContext = qml ? qml->getContext() : nullptr;
4326 QCOMPARE(scriptContext, newContext);
4327 }
4328 }
4329
4330 while (childCtxt) {
4331 verifyContextLifetime(ctxt: childCtxt);
4332
4333 childCtxt = childCtxt->nextChild;
4334 }
4335}
4336
4337void tst_qqmlecmascript::importScripts_data()
4338{
4339 QTest::addColumn<QUrl>(name: "testfile");
4340 QTest::addColumn<bool>(name: "compilationShouldSucceed");
4341 QTest::addColumn<QString>(name: "errorMessage");
4342 QTest::addColumn<QStringList>(name: "warningMessages");
4343 QTest::addColumn<QStringList>(name: "propertyNames");
4344 QTest::addColumn<QVariantList>(name: "propertyValues");
4345
4346 QTest::newRow(dataTag: "basic functionality")
4347 << testFileUrl(fileName: "jsimport/testImport.qml")
4348 << true /* compilation should succeed */
4349 << QString()
4350 << QStringList()
4351 << (QStringList() << QLatin1String("importedScriptStringValue")
4352 << QLatin1String("importedScriptFunctionValue")
4353 << QLatin1String("importedModuleAttachedPropertyValue")
4354 << QLatin1String("importedModuleEnumValue"))
4355 << (QVariantList() << QVariant(QLatin1String("Hello, World!"))
4356 << QVariant(20)
4357 << QVariant(19)
4358 << QVariant(2));
4359
4360 QTest::newRow(dataTag: "import scoping")
4361 << testFileUrl(fileName: "jsimport/testImportScoping.qml")
4362 << true /* compilation should succeed */
4363 << QString()
4364 << QStringList()
4365 << (QStringList() << QLatin1String("componentError"))
4366 << (QVariantList() << QVariant(5));
4367
4368 QTest::newRow(dataTag: "parent scope shouldn't be inherited by import with imports")
4369 << testFileUrl(fileName: "jsimportfail/failOne.qml")
4370 << true /* compilation should succeed */
4371 << QString()
4372 << (QStringList() << QString(testFileUrl(fileName: "jsimportfail/failOne.qml").toString() + QLatin1String(":6: TypeError: Cannot call method 'greetingString' of undefined")))
4373 << (QStringList() << QLatin1String("importScriptFunctionValue"))
4374 << (QVariantList() << QVariant(QString()));
4375
4376 QTest::newRow(dataTag: "javascript imports in an import should be private to the import scope")
4377 << testFileUrl(fileName: "jsimportfail/failTwo.qml")
4378 << true /* compilation should succeed */
4379 << QString()
4380 << (QStringList() << QString(testFileUrl(fileName: "jsimportfail/failTwo.qml").toString() + QLatin1String(":6: ReferenceError: ImportOneJs is not defined")))
4381 << (QStringList() << QLatin1String("importScriptFunctionValue"))
4382 << (QVariantList() << QVariant(QString()));
4383
4384 QTest::newRow(dataTag: "module imports in an import should be private to the import scope")
4385 << testFileUrl(fileName: "jsimportfail/failThree.qml")
4386 << true /* compilation should succeed */
4387 << QString()
4388 << (QStringList() << QString(testFileUrl(fileName: "jsimportfail/failThree.qml").toString() + QLatin1String(":7: TypeError: Cannot read property 'JsQtTest' of undefined")))
4389 << (QStringList() << QLatin1String("importedModuleAttachedPropertyValue"))
4390 << (QVariantList() << QVariant(false));
4391
4392 QTest::newRow(dataTag: "typenames in an import should be private to the import scope")
4393 << testFileUrl(fileName: "jsimportfail/failFour.qml")
4394 << true /* compilation should succeed */
4395 << QString()
4396 << (QStringList() << QString(testFileUrl(fileName: "jsimportfail/failFour.qml").toString() + QLatin1String(":6: ReferenceError: JsQtTest is not defined")))
4397 << (QStringList() << QLatin1String("importedModuleEnumValue"))
4398 << (QVariantList() << QVariant(0));
4399
4400 QTest::newRow(dataTag: "import with imports has it's own activation scope")
4401 << testFileUrl(fileName: "jsimportfail/failFive.qml")
4402 << true /* compilation should succeed */
4403 << QString()
4404 << (QStringList() << QString(testFileUrl(fileName: "jsimportfail/importWithImports.js").toString() + QLatin1String(":8: ReferenceError: Component is not defined")))
4405 << (QStringList() << QLatin1String("componentError"))
4406 << (QVariantList() << QVariant(0));
4407
4408 QTest::newRow(dataTag: "import pragma library script")
4409 << testFileUrl(fileName: "jsimport/testImportPragmaLibrary.qml")
4410 << true /* compilation should succeed */
4411 << QString()
4412 << QStringList()
4413 << (QStringList() << QLatin1String("testValue"))
4414 << (QVariantList() << QVariant(31));
4415
4416 QTest::newRow(dataTag: "pragma library imports shouldn't inherit parent imports or scope")
4417 << testFileUrl(fileName: "jsimportfail/testImportPragmaLibrary.qml")
4418 << true /* compilation should succeed */
4419 << QString()
4420 << (QStringList() << QString(testFileUrl(fileName: "jsimportfail/importPragmaLibrary.js").toString() + QLatin1String(":6: ReferenceError: Component is not defined")))
4421 << (QStringList() << QLatin1String("testValue"))
4422 << (QVariantList() << QVariant(0));
4423
4424 QTest::newRow(dataTag: "import pragma library script which has an import")
4425 << testFileUrl(fileName: "jsimport/testImportPragmaLibraryWithImports.qml")
4426 << true /* compilation should succeed */
4427 << QString()
4428 << QStringList()
4429 << (QStringList() << QLatin1String("testValue"))
4430 << (QVariantList() << QVariant(55));
4431
4432 QTest::newRow(dataTag: "import pragma library script which has a pragma library import")
4433 << testFileUrl(fileName: "jsimport/testImportPragmaLibraryWithPragmaLibraryImports.qml")
4434 << true /* compilation should succeed */
4435 << QString()
4436 << QStringList()
4437 << (QStringList() << QLatin1String("testValue"))
4438 << (QVariantList() << QVariant(16));
4439
4440 QTest::newRow(dataTag: "import singleton type into js import")
4441 << testFileUrl(fileName: "jsimport/testImportSingletonType.qml")
4442 << true /* compilation should succeed */
4443 << QString()
4444 << QStringList()
4445 << (QStringList() << QLatin1String("testValue"))
4446 << (QVariantList() << QVariant(20));
4447
4448 QTest::newRow(dataTag: "import module which exports a script")
4449 << testFileUrl(fileName: "jsimport/testJsImport.qml")
4450 << true /* compilation should succeed */
4451 << QString()
4452 << QStringList()
4453 << (QStringList() << QLatin1String("importedScriptStringValue")
4454 << QLatin1String("renamedScriptStringValue")
4455 << QLatin1String("reimportedScriptStringValue"))
4456 << (QVariantList() << QVariant(QString("Hello"))
4457 << QVariant(QString("Hello"))
4458 << QVariant(QString("Hello")));
4459
4460 QTest::newRow(dataTag: "import module which exports a script which imports a remote module")
4461 << testFileUrl(fileName: "jsimport/testJsRemoteImport.qml")
4462 << true /* compilation should succeed */
4463 << QString()
4464 << QStringList()
4465 << (QStringList() << QLatin1String("importedScriptStringValue")
4466 << QLatin1String("renamedScriptStringValue")
4467 << QLatin1String("reimportedScriptStringValue"))
4468 << (QVariantList() << QVariant(QString("Hello"))
4469 << QVariant(QString("Hello"))
4470 << QVariant(QString("Hello")));
4471
4472 QTest::newRow(dataTag: "malformed import statement")
4473 << testFileUrl(fileName: "jsimportfail/malformedImport.qml")
4474 << false /* compilation should succeed */
4475 << QString()
4476 << (QStringList() << testFileUrl(fileName: "jsimportfail/malformedImport.js").toString() + QLatin1String(":1:2: Syntax error"))
4477 << QStringList()
4478 << QVariantList();
4479
4480 QTest::newRow(dataTag: "malformed file name")
4481 << testFileUrl(fileName: "jsimportfail/malformedFile.qml")
4482 << false /* compilation should succeed */
4483 << QString()
4484 << (QStringList() << testFileUrl(fileName: "jsimportfail/malformedFile.js").toString() + QLatin1String(":1:9: Imported file must be a script"))
4485 << QStringList()
4486 << QVariantList();
4487
4488 QTest::newRow(dataTag: "missing file qualifier")
4489 << testFileUrl(fileName: "jsimportfail/missingFileQualifier.qml")
4490 << false /* compilation should succeed */
4491 << QString()
4492 << (QStringList() << testFileUrl(fileName: "jsimportfail/missingFileQualifier.js").toString() + QLatin1String(":1:1: File import requires a qualifier"))
4493 << QStringList()
4494 << QVariantList();
4495
4496 QTest::newRow(dataTag: "malformed file qualifier")
4497 << testFileUrl(fileName: "jsimportfail/malformedFileQualifier.qml")
4498 << false /* compilation should succeed */
4499 << QString()
4500 << (QStringList() << testFileUrl(fileName: "jsimportfail/malformedFileQualifier.js").toString() + QLatin1String(":1:20: File import requires a qualifier"))
4501 << QStringList()
4502 << QVariantList();
4503
4504 QTest::newRow(dataTag: "malformed module qualifier 2")
4505 << testFileUrl(fileName: "jsimportfail/malformedFileQualifier.2.qml")
4506 << false /* compilation should succeed */
4507 << QString()
4508 << (QStringList() << testFileUrl(fileName: "jsimportfail/malformedFileQualifier.2.js").toString() + QLatin1String(":1:23: Invalid import qualifier"))
4509 << QStringList()
4510 << QVariantList();
4511
4512 QTest::newRow(dataTag: "malformed module uri")
4513 << testFileUrl(fileName: "jsimportfail/malformedModule.qml")
4514 << false /* compilation should succeed */
4515 << QString()
4516 << (QStringList() << testFileUrl(fileName: "jsimportfail/malformedModule.js").toString() + QLatin1String(":1:17: Invalid module URI"))
4517 << QStringList()
4518 << QVariantList();
4519
4520 QTest::newRow(dataTag: "missing module version")
4521 << testFileUrl(fileName: "jsimportfail/missingModuleVersion.qml")
4522 << false /* compilation should succeed */
4523 << QString()
4524 << (QStringList() << testFileUrl(fileName: "jsimportfail/missingModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
4525 << QStringList()
4526 << QVariantList();
4527
4528 QTest::newRow(dataTag: "malformed module version")
4529 << testFileUrl(fileName: "jsimportfail/malformedModuleVersion.qml")
4530 << false /* compilation should succeed */
4531 << QString()
4532 << (QStringList() << testFileUrl(fileName: "jsimportfail/malformedModuleVersion.js").toString() + QLatin1String(":1:17: Module import requires a version"))
4533 << QStringList()
4534 << QVariantList();
4535
4536 QTest::newRow(dataTag: "missing module qualifier")
4537 << testFileUrl(fileName: "jsimportfail/missingModuleQualifier.qml")
4538 << false /* compilation should succeed */
4539 << QString()
4540 << (QStringList() << testFileUrl(fileName: "jsimportfail/missingModuleQualifier.js").toString() + QLatin1String(":1:1: Module import requires a qualifier"))
4541 << QStringList()
4542 << QVariantList();
4543
4544 QTest::newRow(dataTag: "malformed module qualifier")
4545 << testFileUrl(fileName: "jsimportfail/malformedModuleQualifier.qml")
4546 << false /* compilation should succeed */
4547 << QString()
4548 << (QStringList() << testFileUrl(fileName: "jsimportfail/malformedModuleQualifier.js").toString() + QLatin1String(":1:21: Module import requires a qualifier"))
4549 << QStringList()
4550 << QVariantList();
4551
4552 QTest::newRow(dataTag: "malformed module qualifier 2")
4553 << testFileUrl(fileName: "jsimportfail/malformedModuleQualifier.2.qml")
4554 << false /* compilation should succeed */
4555 << QString()
4556 << (QStringList() << testFileUrl(fileName: "jsimportfail/malformedModuleQualifier.2.js").toString() + QLatin1String(":1:24: Invalid import qualifier"))
4557 << QStringList()
4558 << QVariantList();
4559}
4560
4561void tst_qqmlecmascript::importScripts()
4562{
4563 QFETCH(QUrl, testfile);
4564 QFETCH(bool, compilationShouldSucceed);
4565 QFETCH(QString, errorMessage);
4566 QFETCH(QStringList, warningMessages); // error messages if !compilationShouldSucceed
4567 QFETCH(QStringList, propertyNames);
4568 QFETCH(QVariantList, propertyValues);
4569
4570 ThreadedTestHTTPServer server(dataDirectory() + "/remote");
4571
4572 QQmlEngine engine;
4573 QString dataDir(dataDirectory() + QLatin1Char('/') + QLatin1String("lib"));
4574 engine.addImportPath(dir: dataDir);
4575
4576 QStringList importPathList = engine.importPathList();
4577
4578 QString remotePath(server.urlString(documentPath: "/"));
4579 engine.addImportPath(dir: remotePath);
4580
4581 QQmlComponent component(&engine, testfile);
4582
4583 if (!errorMessage.isEmpty())
4584 QTest::ignoreMessage(type: QtWarningMsg, message: errorMessage.toLatin1().constData());
4585
4586 if (compilationShouldSucceed && warningMessages.size())
4587 foreach (const QString &warning, warningMessages)
4588 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
4589
4590 if (compilationShouldSucceed)
4591 QTRY_VERIFY(component.isReady());
4592 else {
4593 QVERIFY(component.isError());
4594 QCOMPARE(warningMessages.size(), 1);
4595 QCOMPARE(component.errors().count(), 2);
4596 QCOMPARE(component.errors().at(1).toString(), warningMessages.first());
4597 return;
4598 }
4599
4600 QObject *object = component.create();
4601 if (!errorMessage.isEmpty()) {
4602 QVERIFY(!object);
4603 } else {
4604 QVERIFY(object != nullptr);
4605
4606 QQmlContextData *ctxt = QQmlContextData::get(context: engine.rootContext());
4607 tst_qqmlecmascript::verifyContextLifetime(ctxt);
4608
4609 for (int i = 0; i < propertyNames.size(); ++i)
4610 QCOMPARE(object->property(propertyNames.at(i).toLatin1().constData()), propertyValues.at(i));
4611 delete object;
4612 }
4613
4614 engine.setImportPathList(importPathList);
4615}
4616
4617void tst_qqmlecmascript::importCreationContext()
4618{
4619 QQmlEngine engine;
4620 QQmlComponent component(&engine, testFileUrl(fileName: "jsimport/creationContext.qml"));
4621 QScopedPointer<QObject> object(component.create());
4622 QVERIFY(!object.isNull());
4623 bool success = object->property(name: "success").toBool();
4624 if (!success) {
4625 QSignalSpy readySpy(object.data(), SIGNAL(loaded()));
4626 readySpy.wait();
4627 }
4628 success = object->property(name: "success").toBool();
4629 QVERIFY(success);
4630}
4631
4632void tst_qqmlecmascript::scarceResources_other()
4633{
4634 /* These tests require knowledge of state, since we test values after
4635 performing signal or function invocation. */
4636
4637 QPixmap origPixmap(100, 100);
4638 origPixmap.fill(fillColor: Qt::blue);
4639 QString srp_name, expectedWarning;
4640 QQmlEngine engine;
4641 QV4::ExecutionEngine *v4 = engine.handle();
4642 ScarceResourceObject *eo = nullptr;
4643 QObject *srsc = nullptr;
4644 QObject *object = nullptr;
4645
4646 /* property var semantics */
4647
4648 // test that scarce resources are handled properly in signal invocation
4649 QQmlComponent varComponentTen(&engine, testFileUrl(fileName: "scarceResourceSignal.var.qml"));
4650 object = varComponentTen.create();
4651 srsc = object->findChild<QObject*>(aName: "srsc");
4652 QVERIFY(srsc);
4653 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4654 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4655 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4656 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4657 QMetaObject::invokeMethod(obj: srsc, member: "testSignal");
4658 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4659 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4660 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4661 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4662 QMetaObject::invokeMethod(obj: srsc, member: "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4663 QVERIFY(srsc->property("scarceResourceCopy").isValid());
4664 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4665 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4666 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4667 QVERIFY(v4->scarceResources.isEmpty()); // should have been released by this point.
4668 delete object;
4669
4670 // test that scarce resources are handled properly from js functions in qml files
4671 QQmlComponent varComponentEleven(&engine, testFileUrl(fileName: "scarceResourceFunction.var.qml"));
4672 object = varComponentEleven.create();
4673 QVERIFY(object != nullptr);
4674 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4675 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4676 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4677 QMetaObject::invokeMethod(obj: object, member: "retrieveScarceResource");
4678 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4679 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4680 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4681 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4682 QMetaObject::invokeMethod(obj: object, member: "releaseScarceResource");
4683 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4684 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4685 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4686 QVERIFY(v4->scarceResources.isEmpty()); // should have been released by this point.
4687 delete object;
4688
4689 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4690 QQmlComponent varComponentTwelve(&engine, testFileUrl(fileName: "scarceResourceFunctionFail.var.qml"));
4691 object = varComponentTwelve.create();
4692 QVERIFY(object != nullptr);
4693 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4694 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4695 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4696 expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function");
4697 expectedWarning = expectedWarning.arg(a: QString::number(quintptr(eo), base: 16));
4698 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4699 QMetaObject::invokeMethod(obj: object, member: "retrieveScarceResource");
4700 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4701 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4702 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4703 QVERIFY(v4->scarceResources.isEmpty()); // should have been released by this point.
4704 delete object;
4705
4706 // test that if an Item which has JS ownership but has a scarce resource property is garbage collected,
4707 // that the scarce resource is removed from the engine's list of scarce resources to clean up.
4708 QQmlComponent varComponentThirteen(&engine, testFileUrl(fileName: "scarceResourceObjectGc.var.qml"));
4709 object = varComponentThirteen.create();
4710 QVERIFY(object != nullptr);
4711 QVERIFY(!object->property("varProperty").isValid()); // not assigned yet
4712 QMetaObject::invokeMethod(obj: object, member: "assignVarProperty");
4713 QVERIFY(v4->scarceResources.isEmpty()); // the scarce resource is a VME property.
4714 QMetaObject::invokeMethod(obj: object, member: "deassignVarProperty");
4715 gc(engine);
4716 QVERIFY(v4->scarceResources.isEmpty()); // should still be empty; the resource should have been released on gc.
4717 delete object;
4718
4719 /* property variant semantics */
4720
4721 // test that scarce resources are handled properly in signal invocation
4722 QQmlComponent variantComponentTen(&engine, testFileUrl(fileName: "scarceResourceSignal.variant.qml"));
4723 object = variantComponentTen.create();
4724 QVERIFY(object != nullptr);
4725 srsc = object->findChild<QObject*>(aName: "srsc");
4726 QVERIFY(srsc);
4727 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // hasn't been instantiated yet.
4728 QCOMPARE(srsc->property("width"), QVariant(5)); // default value is 5.
4729 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4730 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4731 QMetaObject::invokeMethod(obj: srsc, member: "testSignal");
4732 QVERIFY(!srsc->property("scarceResourceCopy").isValid()); // still hasn't been instantiated
4733 QCOMPARE(srsc->property("width"), QVariant(10)); // but width was assigned to 10.
4734 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4735 QVERIFY(eo->scarceResourceIsDetached()); // should still be no other copies of it at this stage.
4736 QMetaObject::invokeMethod(obj: srsc, member: "testSignal2"); // assigns scarceResourceCopy to the scarce pixmap.
4737 QVERIFY(srsc->property("scarceResourceCopy").isValid());
4738 QCOMPARE(srsc->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4739 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4740 QVERIFY(!(eo->scarceResourceIsDetached())); // should be another copy of the resource now.
4741 QVERIFY(v4->scarceResources.isEmpty()); // should have been released by this point.
4742 delete object;
4743
4744 // test that scarce resources are handled properly from js functions in qml files
4745 QQmlComponent variantComponentEleven(&engine, testFileUrl(fileName: "scarceResourceFunction.variant.qml"));
4746 object = variantComponentEleven.create();
4747 QVERIFY(object != nullptr);
4748 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4749 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4750 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4751 QMetaObject::invokeMethod(obj: object, member: "retrieveScarceResource");
4752 QVERIFY(object->property("scarceResourceCopy").isValid()); // assigned, so should be valid.
4753 QCOMPARE(object->property("scarceResourceCopy").value<QPixmap>(), origPixmap);
4754 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4755 QVERIFY(!eo->scarceResourceIsDetached()); // should be a copy of the resource at this stage.
4756 QMetaObject::invokeMethod(obj: object, member: "releaseScarceResource");
4757 QVERIFY(!object->property("scarceResourceCopy").isValid()); // just released, so should not be valid
4758 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4759 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4760 QVERIFY(v4->scarceResources.isEmpty()); // should have been released by this point.
4761 delete object;
4762
4763 // test that if an exception occurs while invoking js function from cpp, that the resources are released.
4764 QQmlComponent variantComponentTwelve(&engine, testFileUrl(fileName: "scarceResourceFunctionFail.variant.qml"));
4765 object = variantComponentTwelve.create();
4766 QVERIFY(object != nullptr);
4767 QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid
4768 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4769 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4770 expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ScarceResourceObject(0x%1) is not a function");
4771 expectedWarning = expectedWarning.arg(a: QString::number(quintptr(eo), base: 16));
4772 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed.
4773 QMetaObject::invokeMethod(obj: object, member: "retrieveScarceResource");
4774 QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred.
4775 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
4776 QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage.
4777 QVERIFY(v4->scarceResources.isEmpty()); // should have been released by this point.
4778 delete object;
4779}
4780
4781void tst_qqmlecmascript::scarceResources_data()
4782{
4783 QTest::addColumn<QUrl>(name: "qmlFile");
4784 QTest::addColumn<bool>(name: "readDetachStatus");
4785 QTest::addColumn<bool>(name: "expectedDetachStatus");
4786 QTest::addColumn<QStringList>(name: "propertyNames");
4787 QTest::addColumn<QVariantList>(name: "expectedValidity");
4788 QTest::addColumn<QVariantList>(name: "expectedValues");
4789 QTest::addColumn<QStringList>(name: "expectedErrors");
4790
4791 QPixmap origPixmap(100, 100);
4792 origPixmap.fill(fillColor: Qt::blue);
4793
4794 /* property var semantics */
4795
4796 // in the following three cases, the instance created from the component
4797 // has a property which is a copy of the scarce resource; hence, the
4798 // resource should NOT be detached prior to deletion of the object instance,
4799 // unless the resource is destroyed explicitly.
4800 QTest::newRow(dataTag: "var: import scarce resource copy directly")
4801 << testFileUrl(fileName: "scarceResourceCopy.var.qml")
4802 << true
4803 << false // won't be detached, because assigned to property and not explicitly released
4804 << (QStringList() << QLatin1String("scarceResourceCopy"))
4805 << (QList<QVariant>() << true)
4806 << (QList<QVariant>() << origPixmap)
4807 << QStringList();
4808
4809 QTest::newRow(dataTag: "var: import scarce resource copy from JS")
4810 << testFileUrl(fileName: "scarceResourceCopyFromJs.var.qml")
4811 << true
4812 << false // won't be detached, because assigned to property and not explicitly released
4813 << (QStringList() << QLatin1String("scarceResourceCopy"))
4814 << (QList<QVariant>() << true)
4815 << (QList<QVariant>() << origPixmap)
4816 << QStringList();
4817
4818 QTest::newRow(dataTag: "var: import released scarce resource copy from JS")
4819 << testFileUrl(fileName: "scarceResourceDestroyedCopy.var.qml")
4820 << true
4821 << true // explicitly released, so it will be detached
4822 << (QStringList() << QLatin1String("scarceResourceCopy"))
4823 << (QList<QVariant>() << false)
4824 << (QList<QVariant>() << QVariant())
4825 << QStringList();
4826
4827 // in the following three cases, no other copy should exist in memory,
4828 // and so it should be detached (unless explicitly preserved).
4829 QTest::newRow(dataTag: "var: import auto-release SR from JS in binding side-effect")
4830 << testFileUrl(fileName: "scarceResourceTest.var.qml")
4831 << true
4832 << true // auto released, so it will be detached
4833 << (QStringList() << QLatin1String("scarceResourceTest"))
4834 << (QList<QVariant>() << true)
4835 << (QList<QVariant>() << QVariant(100))
4836 << QStringList();
4837 QTest::newRow(dataTag: "var: import explicit-preserve SR from JS in binding side-effect")
4838 << testFileUrl(fileName: "scarceResourceTestPreserve.var.qml")
4839 << true
4840 << false // won't be detached because we explicitly preserve it
4841 << (QStringList() << QLatin1String("scarceResourceTest"))
4842 << (QList<QVariant>() << true)
4843 << (QList<QVariant>() << QVariant(100))
4844 << QStringList();
4845 QTest::newRow(dataTag: "var: import explicit-preserve SR from JS in binding side-effect")
4846 << testFileUrl(fileName: "scarceResourceTestMultiple.var.qml")
4847 << true
4848 << true // will be detached because all resources were released manually or automatically.
4849 << (QStringList() << QLatin1String("scarceResourceTest"))
4850 << (QList<QVariant>() << true)
4851 << (QList<QVariant>() << QVariant(100))
4852 << QStringList();
4853
4854 // In the following three cases, test that scarce resources are handled
4855 // correctly for imports.
4856 QTest::newRow(dataTag: "var: import with no binding")
4857 << testFileUrl(fileName: "scarceResourceCopyImportNoBinding.var.qml")
4858 << false // cannot check detach status.
4859 << false
4860 << QStringList()
4861 << QList<QVariant>()
4862 << QList<QVariant>()
4863 << QStringList();
4864 QTest::newRow(dataTag: "var: import with binding without explicit preserve")
4865 << testFileUrl(fileName: "scarceResourceCopyImportNoBinding.var.qml")
4866 << false
4867 << false
4868 << (QStringList() << QLatin1String("scarceResourceCopy"))
4869 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4870 << (QList<QVariant>() << QVariant())
4871 << QStringList();
4872 QTest::newRow(dataTag: "var: import with explicit release after binding evaluation")
4873 << testFileUrl(fileName: "scarceResourceCopyImport.var.qml")
4874 << false
4875 << false
4876 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4877 << (QList<QVariant>() << false << false << false << true) // since property var = JS object reference, by releasing the provider's resource, all handles are invalidated.
4878 << (QList<QVariant>() << QVariant() << QVariant() << QVariant() << QVariant(true))
4879 << QStringList();
4880 QTest::newRow(dataTag: "var: import with different js objects")
4881 << testFileUrl(fileName: "scarceResourceCopyImportDifferent.var.qml")
4882 << false
4883 << false
4884 << (QStringList() << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo") << QLatin1String("arePropertiesEqual"))
4885 << (QList<QVariant>() << false << true << true) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4886 << (QList<QVariant>() << QVariant() << QVariant(origPixmap) << QVariant(false))
4887 << QStringList();
4888 QTest::newRow(dataTag: "var: import with different js objects and explicit release")
4889 << testFileUrl(fileName: "scarceResourceMultipleDifferentNoBinding.var.qml")
4890 << false
4891 << false
4892 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4893 << (QList<QVariant>() << true << false) // invalidating one shouldn't invalidate the other, because they're not references to the same JS object.
4894 << (QList<QVariant>() << QVariant(origPixmap) << QVariant())
4895 << QStringList();
4896 QTest::newRow(dataTag: "var: import with same js objects and explicit release")
4897 << testFileUrl(fileName: "scarceResourceMultipleSameNoBinding.var.qml")
4898 << false
4899 << false
4900 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4901 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4902 << (QList<QVariant>() << QVariant() << QVariant())
4903 << QStringList();
4904 QTest::newRow(dataTag: "var: binding with same js objects and explicit release")
4905 << testFileUrl(fileName: "scarceResourceMultipleSameWithBinding.var.qml")
4906 << false
4907 << false
4908 << (QStringList() << QLatin1String("resourceOne") << QLatin1String("resourceTwo"))
4909 << (QList<QVariant>() << false << false) // invalidating one should invalidate the other, because they're references to the same JS object.
4910 << (QList<QVariant>() << QVariant() << QVariant())
4911 << QStringList();
4912
4913
4914 /* property variant semantics */
4915
4916 // in the following three cases, the instance created from the component
4917 // has a property which is a copy of the scarce resource; hence, the
4918 // resource should NOT be detached prior to deletion of the object instance,
4919 // unless the resource is destroyed explicitly.
4920 QTest::newRow(dataTag: "variant: import scarce resource copy directly")
4921 << testFileUrl(fileName: "scarceResourceCopy.variant.qml")
4922 << true
4923 << false // won't be detached, because assigned to property and not explicitly released
4924 << (QStringList() << QLatin1String("scarceResourceCopy"))
4925 << (QList<QVariant>() << true)
4926 << (QList<QVariant>() << origPixmap)
4927 << QStringList();
4928
4929 QTest::newRow(dataTag: "variant: import scarce resource copy from JS")
4930 << testFileUrl(fileName: "scarceResourceCopyFromJs.variant.qml")
4931 << true
4932 << false // won't be detached, because assigned to property and not explicitly released
4933 << (QStringList() << QLatin1String("scarceResourceCopy"))
4934 << (QList<QVariant>() << true)
4935 << (QList<QVariant>() << origPixmap)
4936 << QStringList();
4937
4938 QTest::newRow(dataTag: "variant: import released scarce resource copy from JS")
4939 << testFileUrl(fileName: "scarceResourceDestroyedCopy.variant.qml")
4940 << true
4941 << true // explicitly released, so it will be detached
4942 << (QStringList() << QLatin1String("scarceResourceCopy"))
4943 << (QList<QVariant>() << false)
4944 << (QList<QVariant>() << QVariant())
4945 << QStringList();
4946
4947 // in the following three cases, no other copy should exist in memory,
4948 // and so it should be detached (unless explicitly preserved).
4949 QTest::newRow(dataTag: "variant: import auto-release SR from JS in binding side-effect")
4950 << testFileUrl(fileName: "scarceResourceTest.variant.qml")
4951 << true
4952 << true // auto released, so it will be detached
4953 << (QStringList() << QLatin1String("scarceResourceTest"))
4954 << (QList<QVariant>() << true)
4955 << (QList<QVariant>() << QVariant(100))
4956 << QStringList();
4957 QTest::newRow(dataTag: "variant: import explicit-preserve SR from JS in binding side-effect")
4958 << testFileUrl(fileName: "scarceResourceTestPreserve.variant.qml")
4959 << true
4960 << false // won't be detached because we explicitly preserve it
4961 << (QStringList() << QLatin1String("scarceResourceTest"))
4962 << (QList<QVariant>() << true)
4963 << (QList<QVariant>() << QVariant(100))
4964 << QStringList();
4965 QTest::newRow(dataTag: "variant: import multiple scarce resources")
4966 << testFileUrl(fileName: "scarceResourceTestMultiple.variant.qml")
4967 << true
4968 << true // will be detached because all resources were released manually or automatically.
4969 << (QStringList() << QLatin1String("scarceResourceTest"))
4970 << (QList<QVariant>() << true)
4971 << (QList<QVariant>() << QVariant(100))
4972 << QStringList();
4973
4974 // In the following three cases, test that scarce resources are handled
4975 // correctly for imports.
4976 QTest::newRow(dataTag: "variant: import with no binding")
4977 << testFileUrl(fileName: "scarceResourceCopyImportNoBinding.variant.qml")
4978 << false // cannot check detach status.
4979 << false
4980 << QStringList()
4981 << QList<QVariant>()
4982 << QList<QVariant>()
4983 << QStringList();
4984 QTest::newRow(dataTag: "variant: import with binding without explicit preserve")
4985 << testFileUrl(fileName: "scarceResourceCopyImportNoBinding.variant.qml")
4986 << false
4987 << false
4988 << (QStringList() << QLatin1String("scarceResourceCopy"))
4989 << (QList<QVariant>() << false) // will have been released prior to evaluation of binding.
4990 << (QList<QVariant>() << QVariant())
4991 << QStringList();
4992 QTest::newRow(dataTag: "variant: import with explicit release after binding evaluation")
4993 << testFileUrl(fileName: "scarceResourceCopyImport.variant.qml")
4994 << false
4995 << false
4996 << (QStringList() << QLatin1String("scarceResourceImportedCopy") << QLatin1String("scarceResourceAssignedCopyOne") << QLatin1String("scarceResourceAssignedCopyTwo"))
4997 << (QList<QVariant>() << true << true << false) // since property variant = variant copy, releasing the provider's resource does not invalidate previously assigned copies.
4998 << (QList<QVariant>() << origPixmap << origPixmap << QVariant())
4999 << QStringList();
5000}
5001
5002void tst_qqmlecmascript::scarceResources()
5003{
5004 QFETCH(QUrl, qmlFile);
5005 QFETCH(bool, readDetachStatus);
5006 QFETCH(bool, expectedDetachStatus);
5007 QFETCH(QStringList, propertyNames);
5008 QFETCH(QVariantList, expectedValidity);
5009 QFETCH(QVariantList, expectedValues);
5010 QFETCH(QStringList, expectedErrors);
5011
5012 QQmlEngine engine;
5013 QV4::ExecutionEngine *v4 = engine.handle();
5014 ScarceResourceObject *eo = nullptr;
5015 QObject *object = nullptr;
5016
5017 QQmlComponent c(&engine, qmlFile);
5018 object = c.create();
5019 QVERIFY(object != nullptr);
5020 for (int i = 0; i < propertyNames.size(); ++i) {
5021 QString prop = propertyNames.at(i);
5022 bool validity = expectedValidity.at(i).toBool();
5023 QVariant value = expectedValues.at(i);
5024
5025 QCOMPARE(object->property(prop.toLatin1().constData()).isValid(), validity);
5026 if (value.type() == QVariant::Int) {
5027 QCOMPARE(object->property(prop.toLatin1().constData()).toInt(), value.toInt());
5028 } else if (value.type() == QVariant::Pixmap) {
5029 QCOMPARE(object->property(prop.toLatin1().constData()).value<QPixmap>(), value.value<QPixmap>());
5030 }
5031 }
5032
5033 if (readDetachStatus) {
5034 eo = qobject_cast<ScarceResourceObject*>(object: QQmlProperty::read(object, "a").value<QObject*>());
5035 QCOMPARE(eo->scarceResourceIsDetached(), expectedDetachStatus);
5036 }
5037
5038 QVERIFY(v4->scarceResources.isEmpty());
5039 delete object;
5040}
5041
5042void tst_qqmlecmascript::propertyChangeSlots()
5043{
5044 // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly.
5045 QQmlEngine engine;
5046 QQmlComponent component(&engine, testFileUrl(fileName: "changeslots/propertyChangeSlots.qml"));
5047 QObject *object = component.create();
5048 QVERIFY(object != nullptr);
5049 delete object;
5050
5051 // ensure that invalid property names fail properly.
5052 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready");
5053 QQmlComponent e1(&engine, testFileUrl(fileName: "changeslots/propertyChangeSlotErrors.1.qml"));
5054 QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\"");
5055 QCOMPARE(e1.errors().at(0).toString(), expectedErrorString);
5056 object = e1.create();
5057 QVERIFY(!object);
5058 delete object;
5059
5060 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready");
5061 QQmlComponent e2(&engine, testFileUrl(fileName: "changeslots/propertyChangeSlotErrors.2.qml"));
5062 expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\"");
5063 QCOMPARE(e2.errors().at(0).toString(), expectedErrorString);
5064 object = e2.create();
5065 QVERIFY(!object);
5066 delete object;
5067
5068 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready");
5069 QQmlComponent e3(&engine, testFileUrl(fileName: "changeslots/propertyChangeSlotErrors.3.qml"));
5070 expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\"");
5071 QCOMPARE(e3.errors().at(0).toString(), expectedErrorString);
5072 object = e3.create();
5073 QVERIFY(!object);
5074 delete object;
5075
5076 QTest::ignoreMessage(type: QtWarningMsg, message: "QQmlComponent: Component is not ready");
5077 QQmlComponent e4(&engine, testFileUrl(fileName: "changeslots/propertyChangeSlotErrors.4.qml"));
5078 expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\"");
5079 QCOMPARE(e4.errors().at(0).toString(), expectedErrorString);
5080 object = e4.create();
5081 QVERIFY(!object);
5082 delete object;
5083}
5084
5085void tst_qqmlecmascript::propertyVar_data()
5086{
5087 QTest::addColumn<QUrl>(name: "qmlFile");
5088
5089 // valid
5090 QTest::newRow(dataTag: "non-bindable object subproperty changed") << testFileUrl(fileName: "propertyVar.1.qml");
5091 QTest::newRow(dataTag: "non-bindable object changed") << testFileUrl(fileName: "propertyVar.2.qml");
5092 QTest::newRow(dataTag: "primitive changed") << testFileUrl(fileName: "propertyVar.3.qml");
5093 QTest::newRow(dataTag: "javascript array modification") << testFileUrl(fileName: "propertyVar.4.qml");
5094 QTest::newRow(dataTag: "javascript map modification") << testFileUrl(fileName: "propertyVar.5.qml");
5095 QTest::newRow(dataTag: "javascript array assignment") << testFileUrl(fileName: "propertyVar.6.qml");
5096 QTest::newRow(dataTag: "javascript map assignment") << testFileUrl(fileName: "propertyVar.7.qml");
5097 QTest::newRow(dataTag: "literal property assignment") << testFileUrl(fileName: "propertyVar.8.qml");
5098 QTest::newRow(dataTag: "qobject property assignment") << testFileUrl(fileName: "propertyVar.9.qml");
5099 QTest::newRow(dataTag: "base class var property assignment") << testFileUrl(fileName: "propertyVar.10.qml");
5100 QTest::newRow(dataTag: "javascript function assignment") << testFileUrl(fileName: "propertyVar.11.qml");
5101 QTest::newRow(dataTag: "javascript special assignment") << testFileUrl(fileName: "propertyVar.12.qml");
5102 QTest::newRow(dataTag: "declarative binding assignment") << testFileUrl(fileName: "propertyVar.13.qml");
5103 QTest::newRow(dataTag: "imperative binding assignment") << testFileUrl(fileName: "propertyVar.14.qml");
5104 QTest::newRow(dataTag: "stored binding assignment") << testFileUrl(fileName: "propertyVar.15.qml");
5105 QTest::newRow(dataTag: "function expression binding assignment") << testFileUrl(fileName: "propertyVar.16.qml");
5106}
5107
5108void tst_qqmlecmascript::propertyVar()
5109{
5110 QFETCH(QUrl, qmlFile);
5111
5112 QQmlEngine engine;
5113 QQmlComponent component(&engine, qmlFile);
5114 QObject *object = component.create();
5115 QVERIFY(object != nullptr);
5116
5117 QCOMPARE(object->property("test").toBool(), true);
5118
5119 delete object;
5120}
5121
5122void tst_qqmlecmascript::propertyQJSValue_data()
5123{
5124 QTest::addColumn<QUrl>(name: "qmlFile");
5125
5126 // valid
5127 QTest::newRow(dataTag: "non-bindable object subproperty changed") << testFileUrl(fileName: "propertyQJSValue.1.qml");
5128 QTest::newRow(dataTag: "non-bindable object changed") << testFileUrl(fileName: "propertyQJSValue.2.qml");
5129 QTest::newRow(dataTag: "primitive changed") << testFileUrl(fileName: "propertyQJSValue.3.qml");
5130 QTest::newRow(dataTag: "javascript array modification") << testFileUrl(fileName: "propertyQJSValue.4.qml");
5131 QTest::newRow(dataTag: "javascript map modification") << testFileUrl(fileName: "propertyQJSValue.5.qml");
5132 QTest::newRow(dataTag: "javascript array assignment") << testFileUrl(fileName: "propertyQJSValue.6.qml");
5133 QTest::newRow(dataTag: "javascript map assignment") << testFileUrl(fileName: "propertyQJSValue.7.qml");
5134 QTest::newRow(dataTag: "literal property assignment") << testFileUrl(fileName: "propertyQJSValue.8.qml");
5135 QTest::newRow(dataTag: "qobject property assignment") << testFileUrl(fileName: "propertyQJSValue.9.qml");
5136 QTest::newRow(dataTag: "base class var property assignment") << testFileUrl(fileName: "propertyQJSValue.10.qml");
5137 QTest::newRow(dataTag: "javascript function assignment") << testFileUrl(fileName: "propertyQJSValue.11.qml");
5138 QTest::newRow(dataTag: "javascript special assignment") << testFileUrl(fileName: "propertyQJSValue.12.qml");
5139 QTest::newRow(dataTag: "declarative binding assignment") << testFileUrl(fileName: "propertyQJSValue.13.qml");
5140 QTest::newRow(dataTag: "imperative binding assignment") << testFileUrl(fileName: "propertyQJSValue.14.qml");
5141 QTest::newRow(dataTag: "stored binding assignment") << testFileUrl(fileName: "propertyQJSValue.15.qml");
5142 QTest::newRow(dataTag: "javascript function binding") << testFileUrl(fileName: "propertyQJSValue.16.qml");
5143
5144 QTest::newRow(dataTag: "reset property") << testFileUrl(fileName: "propertyQJSValue.reset.qml");
5145 QTest::newRow(dataTag: "reset property in binding") << testFileUrl(fileName: "propertyQJSValue.bindingreset.qml");
5146}
5147
5148void tst_qqmlecmascript::propertyQJSValue()
5149{
5150 QFETCH(QUrl, qmlFile);
5151
5152 QQmlEngine engine;
5153 QQmlComponent component(&engine, qmlFile);
5154 QObject *object = component.create();
5155 QVERIFY(object != nullptr);
5156
5157 QCOMPARE(object->property("test").toBool(), true);
5158
5159 delete object;
5160}
5161
5162// Tests that we can write QVariant values to var properties from C++
5163void tst_qqmlecmascript::propertyVarCpp()
5164{
5165 QObject *object = nullptr;
5166
5167 // ensure that writing to and reading from a var property from cpp works as required.
5168 // Literal values stored in var properties can be read and written as QVariants
5169 // of a specific type, whereas object values are read as QVariantMaps.
5170 QQmlEngine engine;
5171 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVarCpp.qml"));
5172 object = component.create();
5173 QVERIFY(object != nullptr);
5174 // assign int to property var that currently has int assigned
5175 QVERIFY(object->setProperty("varProperty", QVariant::fromValue(10)));
5176 QCOMPARE(object->property("varBound"), QVariant(15));
5177 QCOMPARE(object->property("intBound"), QVariant(15));
5178 QVERIFY(isJSNumberType(object->property("varProperty").userType()));
5179 QVERIFY(isJSNumberType(object->property("varBound").userType()));
5180 // assign string to property var that current has bool assigned
5181 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::Bool);
5182 QVERIFY(object->setProperty("varProperty2", QVariant(QLatin1String("randomString"))));
5183 QCOMPARE(object->property("varProperty2"), QVariant(QLatin1String("randomString")));
5184 QCOMPARE(object->property("varProperty2").userType(), (int)QVariant::String);
5185 // now enforce behaviour when accessing JavaScript objects from cpp.
5186 QCOMPARE(object->property("jsobject").userType(), qMetaTypeId<QJSValue>());
5187 delete object;
5188}
5189
5190void tst_qqmlecmascript::propertyVarOwnership()
5191{
5192 QQmlEngine engine;
5193
5194 // Referenced JS objects are not collected
5195 {
5196 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVarOwnership.qml"));
5197 QObject *object = component.create();
5198 QVERIFY(object != nullptr);
5199 QCOMPARE(object->property("test").toBool(), false);
5200 QMetaObject::invokeMethod(obj: object, member: "runTest");
5201 QCOMPARE(object->property("test").toBool(), true);
5202 delete object;
5203 }
5204 // Referenced JS objects are not collected
5205 {
5206 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVarOwnership.2.qml"));
5207 QObject *object = component.create();
5208 QVERIFY(object != nullptr);
5209 QCOMPARE(object->property("test").toBool(), false);
5210 QMetaObject::invokeMethod(obj: object, member: "runTest");
5211 QCOMPARE(object->property("test").toBool(), true);
5212 delete object;
5213 }
5214 // Qt objects are not collected until they've been dereferenced
5215 {
5216 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVarOwnership.3.qml"));
5217 QObject *object = component.create();
5218 QVERIFY(object != nullptr);
5219
5220 QCOMPARE(object->property("test2").toBool(), false);
5221 QCOMPARE(object->property("test2").toBool(), false);
5222
5223 QMetaObject::invokeMethod(obj: object, member: "runTest");
5224 QCOMPARE(object->property("test1").toBool(), true);
5225
5226 QPointer<QObject> referencedObject = object->property(name: "object").value<QObject*>();
5227 QVERIFY(!referencedObject.isNull());
5228 gc(engine);
5229 QVERIFY(!referencedObject.isNull());
5230
5231 QMetaObject::invokeMethod(obj: object, member: "runTest2");
5232 QCOMPARE(object->property("test2").toBool(), true);
5233 gc(engine);
5234 QVERIFY(referencedObject.isNull());
5235
5236 delete object;
5237 }
5238 // Self reference does not prevent Qt object collection
5239 {
5240 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVarOwnership.4.qml"));
5241 QObject *object = component.create();
5242 QVERIFY(object != nullptr);
5243
5244 QCOMPARE(object->property("test").toBool(), true);
5245
5246 QPointer<QObject> referencedObject = object->property(name: "object").value<QObject*>();
5247 QVERIFY(!referencedObject.isNull());
5248 gc(engine);
5249 QVERIFY(!referencedObject.isNull());
5250
5251 QMetaObject::invokeMethod(obj: object, member: "runTest");
5252 gc(engine);
5253 QVERIFY(referencedObject.isNull());
5254
5255 delete object;
5256 }
5257 // Garbage collection cannot result in attempted dereference of empty handle
5258 {
5259 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVarOwnership.5.qml"));
5260 QObject *object = component.create();
5261 QVERIFY(object != nullptr);
5262 QMetaObject::invokeMethod(obj: object, member: "createComponent");
5263 engine.collectGarbage();
5264 QMetaObject::invokeMethod(obj: object, member: "runTest");
5265 QCOMPARE(object->property("test").toBool(), true);
5266 delete object;
5267 }
5268}
5269
5270void tst_qqmlecmascript::propertyVarImplicitOwnership()
5271{
5272 // The childObject has a reference to a different QObject. We want to ensure
5273 // that the different item will not be cleaned up until required. IE, the childObject
5274 // has implicit ownership of the constructed QObject.
5275 QQmlEngine engine;
5276 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVarImplicitOwnership.qml"));
5277 QObject *object = component.create();
5278 QVERIFY(object != nullptr);
5279 QMetaObject::invokeMethod(obj: object, member: "assignCircular");
5280 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
5281 QCoreApplication::processEvents();
5282 QObject *rootObject = object->property(name: "vp").value<QObject*>();
5283 QVERIFY(rootObject != nullptr);
5284 QObject *childObject = rootObject->findChild<QObject*>(aName: "text");
5285 QVERIFY(childObject != nullptr);
5286 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
5287 QCOMPARE(childObject->property("textCanary").toInt(), 10);
5288 QMetaObject::invokeMethod(obj: childObject, member: "constructQObject"); // creates a reference to a constructed QObject.
5289 QPointer<QObject> qobjectGuard(childObject->property(name: "vp").value<QObject*>()); // get the pointer prior to processing deleteLater events.
5290 QVERIFY(!qobjectGuard.isNull());
5291 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
5292 QCoreApplication::processEvents();
5293 QVERIFY(!qobjectGuard.isNull());
5294 QMetaObject::invokeMethod(obj: object, member: "deassignCircular");
5295 gc(engine);
5296 QVERIFY(qobjectGuard.isNull()); // should have been collected now.
5297 delete object;
5298}
5299
5300void tst_qqmlecmascript::propertyVarReparent()
5301{
5302 // ensure that nothing breaks if we re-parent objects
5303 QQmlEngine engine;
5304 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVar.reparent.qml"));
5305 QObject *object = component.create();
5306 QVERIFY(object != nullptr);
5307 QMetaObject::invokeMethod(obj: object, member: "assignVarProp");
5308 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
5309 QCoreApplication::processEvents();
5310 QObject *rect = object->property(name: "vp").value<QObject*>();
5311 QObject *text = rect->findChild<QObject*>(aName: "textOne");
5312 QObject *text2 = rect->findChild<QObject*>(aName: "textTwo");
5313 QPointer<QObject> rectGuard(rect);
5314 QPointer<QObject> textGuard(text);
5315 QPointer<QObject> text2Guard(text2);
5316 QVERIFY(!rectGuard.isNull());
5317 QVERIFY(!textGuard.isNull());
5318 QVERIFY(!text2Guard.isNull());
5319 QCOMPARE(text->property("textCanary").toInt(), 11);
5320 QCOMPARE(text2->property("textCanary").toInt(), 12);
5321 // now construct an image which we will reparent.
5322 QMetaObject::invokeMethod(obj: text2, member: "constructQObject");
5323 QObject *image = text2->property(name: "vp").value<QObject*>();
5324 QPointer<QObject> imageGuard(image);
5325 QVERIFY(!imageGuard.isNull());
5326 QCOMPARE(image->property("imageCanary").toInt(), 13);
5327 // now reparent the "Image" object (currently, it has JS ownership)
5328 image->setParent(text); // shouldn't be collected after deassignVp now, since has a parent.
5329 QMetaObject::invokeMethod(obj: text2, member: "deassignVp");
5330 gc(engine);
5331 QCOMPARE(text->property("textCanary").toInt(), 11);
5332 QCOMPARE(text2->property("textCanary").toInt(), 22);
5333 QVERIFY(!imageGuard.isNull()); // should still be alive.
5334 QCOMPARE(image->property("imageCanary").toInt(), 13); // still able to access var properties
5335 QMetaObject::invokeMethod(obj: object, member: "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
5336 gc(engine);
5337 QVERIFY(imageGuard.isNull()); // should now have been deleted, due to parent being deleted.
5338 delete object;
5339}
5340
5341void tst_qqmlecmascript::propertyVarReparentNullContext()
5342{
5343 // sometimes reparenting can cause problems
5344 // (eg, if the ctxt is collected, varproperties are no longer available)
5345 // this test ensures that no crash occurs in that situation.
5346 QQmlEngine engine;
5347 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVar.reparent.qml"));
5348 QObject *object = component.create();
5349 QVERIFY(object != nullptr);
5350 QMetaObject::invokeMethod(obj: object, member: "assignVarProp");
5351 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
5352 QCoreApplication::processEvents();
5353 QObject *rect = object->property(name: "vp").value<QObject*>();
5354 QObject *text = rect->findChild<QObject*>(aName: "textOne");
5355 QObject *text2 = rect->findChild<QObject*>(aName: "textTwo");
5356 QPointer<QObject> rectGuard(rect);
5357 QPointer<QObject> textGuard(text);
5358 QPointer<QObject> text2Guard(text2);
5359 QVERIFY(!rectGuard.isNull());
5360 QVERIFY(!textGuard.isNull());
5361 QVERIFY(!text2Guard.isNull());
5362 QCOMPARE(text->property("textCanary").toInt(), 11);
5363 QCOMPARE(text2->property("textCanary").toInt(), 12);
5364 // now construct an image which we will reparent.
5365 QMetaObject::invokeMethod(obj: text2, member: "constructQObject");
5366 QObject *image = text2->property(name: "vp").value<QObject*>();
5367 QPointer<QObject> imageGuard(image);
5368 QVERIFY(!imageGuard.isNull());
5369 QCOMPARE(image->property("imageCanary").toInt(), 13);
5370 // now reparent the "Image" object (currently, it has JS ownership)
5371 image->setParent(object); // reparented to base object. after deassignVarProp, the ctxt will be invalid.
5372 QMetaObject::invokeMethod(obj: object, member: "deassignVarProp"); // now deassign the root-object's vp, causing gc of rect+text+text2
5373 gc(engine);
5374 QVERIFY(!imageGuard.isNull()); // should still be alive.
5375 QVERIFY(!image->property("imageCanary").isValid()); // but varProperties won't be available (null context).
5376 delete object;
5377 QVERIFY(imageGuard.isNull()); // should now be dead.
5378}
5379
5380void tst_qqmlecmascript::propertyVarCircular()
5381{
5382 // enforce behaviour regarding circular references - ensure qdvmemo deletion.
5383 QQmlEngine engine;
5384 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVar.circular.qml"));
5385 QObject *object = component.create();
5386 QVERIFY(object != nullptr);
5387 QMetaObject::invokeMethod(obj: object, member: "assignCircular"); // cause assignment and gc
5388 {
5389 QCOMPARE(object->property("canaryInt"), QVariant(5));
5390 QVariant canaryResourceVariant = object->property(name: "canaryResource");
5391 QVERIFY(canaryResourceVariant.isValid());
5392 }
5393
5394 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
5395 QCoreApplication::processEvents();
5396 QCOMPARE(object->property("canaryInt"), QVariant(5));
5397 QVariant canaryResourceVariant = object->property(name: "canaryResource");
5398 QVERIFY(canaryResourceVariant.isValid());
5399 QPixmap canaryResourcePixmap = canaryResourceVariant.value<QPixmap>();
5400 canaryResourceVariant = QVariant(); // invalidate it to remove one copy of the pixmap from memory.
5401 QMetaObject::invokeMethod(obj: object, member: "deassignCanaryResource"); // remove one copy of the pixmap from memory
5402 gc(engine);
5403 QVERIFY(!canaryResourcePixmap.isDetached()); // two copies extant - this and the propertyVar.vp.vp.vp.vp.memoryHog.
5404 QMetaObject::invokeMethod(obj: object, member: "deassignCircular"); // cause deassignment and gc
5405 gc(engine);
5406 QCOMPARE(object->property("canaryInt"), QVariant(2));
5407 QCOMPARE(object->property("canaryResource"), QVariant(1));
5408 QVERIFY(canaryResourcePixmap.isDetached()); // now detached, since orig copy was member of qdvmemo which was deleted.
5409 delete object;
5410}
5411
5412void tst_qqmlecmascript::propertyVarCircular2()
5413{
5414 // track deletion of JS-owned parent item with Cpp-owned child
5415 // where the child has a var property referencing its parent.
5416 QQmlEngine engine;
5417 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVar.circular.2.qml"));
5418 QObject *object = component.create();
5419 QVERIFY(object != nullptr);
5420 QMetaObject::invokeMethod(obj: object, member: "assignCircular");
5421 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
5422 QCoreApplication::processEvents();
5423 QObject *rootObject = object->property(name: "vp").value<QObject*>();
5424 QVERIFY(rootObject != nullptr);
5425 QObject *childObject = rootObject->findChild<QObject*>(aName: "text");
5426 QVERIFY(childObject != nullptr);
5427 QPointer<QObject> rootObjectTracker(rootObject);
5428 QVERIFY(!rootObjectTracker.isNull());
5429 QPointer<QObject> childObjectTracker(childObject);
5430 QVERIFY(!childObjectTracker.isNull());
5431 gc(engine);
5432 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
5433 QCOMPARE(childObject->property("textCanary").toInt(), 10);
5434 QMetaObject::invokeMethod(obj: object, member: "deassignCircular");
5435 gc(engine);
5436 QVERIFY(rootObjectTracker.isNull()); // should have been collected
5437 QVERIFY(childObjectTracker.isNull()); // should have been collected
5438 delete object;
5439}
5440
5441void tst_qqmlecmascript::propertyVarInheritance()
5442{
5443 // enforce behaviour regarding element inheritance - ensure handle disposal.
5444 // The particular component under test here has a chain of references.
5445 QQmlEngine engine;
5446 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVar.inherit.qml"));
5447 QObject *object = component.create();
5448 QVERIFY(object != nullptr);
5449 QMetaObject::invokeMethod(obj: object, member: "assignCircular"); // cause assignment and gc
5450 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
5451 QCoreApplication::processEvents();
5452 // we want to be able to track when the varProperties array of the last metaobject is disposed
5453 QObject *cco5 = object->property(name: "varProperty").value<QObject*>()->property(name: "vp").value<QObject*>()->property(name: "vp").value<QObject*>()->property(name: "vp").value<QObject*>()->property(name: "vp").value<QObject*>();
5454 QObject *ico5 = object->property(name: "varProperty").value<QObject*>()->property(name: "inheritanceVarProperty").value<QObject*>()->property(name: "vp").value<QObject*>()->property(name: "vp").value<QObject*>()->property(name: "vp").value<QObject*>()->property(name: "vp").value<QObject*>();
5455 QVERIFY(cco5);
5456 QVERIFY(ico5);
5457 QQmlVMEMetaObject *icovmemo = QQmlVMEMetaObject::get(obj: ico5);
5458 QQmlVMEMetaObject *ccovmemo = QQmlVMEMetaObject::get(obj: cco5);
5459 QV4::WeakValue icoCanaryHandle;
5460 QV4::WeakValue ccoCanaryHandle;
5461 {
5462 // XXX NOTE: this is very implementation dependent. QDVMEMO->vmeProperty() is the only
5463 // public function which can return us a handle to something in the varProperties array.
5464 QV4::ReturnedValue tmp = icovmemo->vmeProperty(index: ico5->metaObject()->indexOfProperty(name: "circ"));
5465 icoCanaryHandle.set(engine: engine.handle(), value: tmp);
5466 tmp = ccovmemo->vmeProperty(index: cco5->metaObject()->indexOfProperty(name: "circ"));
5467 ccoCanaryHandle.set(engine: engine.handle(), value: tmp);
5468 tmp = QV4::Encode::null();
5469 QVERIFY(!icoCanaryHandle.isUndefined());
5470 QVERIFY(!ccoCanaryHandle.isUndefined());
5471 gc(engine);
5472 QVERIFY(!icoCanaryHandle.isUndefined());
5473 QVERIFY(!ccoCanaryHandle.isUndefined());
5474 }
5475 // now we deassign the var prop, which should trigger collection of item subtrees.
5476 QMetaObject::invokeMethod(obj: object, member: "deassignCircular"); // cause deassignment and gc
5477 // ensure that there are only weak handles to the underlying varProperties array remaining.
5478 gc(engine);
5479 // an equivalent for pragma GCC optimize is still work-in-progress for CLang, so this test will fail.
5480 QVERIFY(icoCanaryHandle.isUndefined());
5481 QVERIFY(ccoCanaryHandle.isUndefined());
5482 delete object;
5483 // since there are no parent vmemo's to keep implicit references alive, and the only handles
5484 // to what remains are weak, all varProperties arrays must have been collected.
5485}
5486
5487void tst_qqmlecmascript::propertyVarInheritance2()
5488{
5489 // The particular component under test here does NOT have a chain of references; the
5490 // only link between rootObject and childObject is that rootObject is the parent of childObject.
5491 QQmlEngine engine;
5492 QQmlComponent component(&engine, testFileUrl(fileName: "propertyVar.circular.2.qml"));
5493 QObject *object = component.create();
5494 QVERIFY(object != nullptr);
5495 QMetaObject::invokeMethod(obj: object, member: "assignCircular");
5496 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete); // process deleteLater() events from QV8QObjectWrapper.
5497 QCoreApplication::processEvents();
5498 QObject *rootObject = object->property(name: "vp").value<QObject*>();
5499 QVERIFY(rootObject != nullptr);
5500 QObject *childObject = rootObject->findChild<QObject*>(aName: "text");
5501 QVERIFY(childObject != nullptr);
5502 QCOMPARE(rootObject->property("rectCanary").toInt(), 5);
5503 QCOMPARE(childObject->property("textCanary").toInt(), 10);
5504 QV4::WeakValue childObjectVarArrayValueHandle;
5505 {
5506 childObjectVarArrayValueHandle.set(engine: engine.handle(),
5507 value: QQmlVMEMetaObject::get(obj: childObject)->vmeProperty(index: childObject->metaObject()->indexOfProperty(name: "vp")));
5508 QVERIFY(!childObjectVarArrayValueHandle.isUndefined());
5509 gc(engine);
5510 QVERIFY(!childObjectVarArrayValueHandle.isUndefined()); // should not have been collected yet.
5511 QCOMPARE(childObject->property("vp").value<QObject*>(), rootObject);
5512 QCOMPARE(childObject->property("textCanary").toInt(), 10);
5513 }
5514 QMetaObject::invokeMethod(obj: object, member: "deassignCircular");
5515 gc(engine);
5516 // an equivalent for pragma GCC optimize is still work-in-progress for CLang, so this test will fail.
5517 QVERIFY(childObjectVarArrayValueHandle.isUndefined()); // should have been collected now.
5518 delete object;
5519}
5520
5521
5522// Ensure that QObject type conversion works on binding assignment
5523void tst_qqmlecmascript::elementAssign()
5524{
5525 QQmlEngine engine;
5526 QQmlComponent component(&engine, testFileUrl(fileName: "elementAssign.qml"));
5527
5528 QObject *object = component.create();
5529 QVERIFY(object != nullptr);
5530
5531 QCOMPARE(object->property("test").toBool(), true);
5532
5533 delete object;
5534}
5535
5536// QTBUG-12457
5537void tst_qqmlecmascript::objectPassThroughSignals()
5538{
5539 QQmlEngine engine;
5540 QQmlComponent component(&engine, testFileUrl(fileName: "objectsPassThroughSignals.qml"));
5541
5542 QObject *object = component.create();
5543 QVERIFY(object != nullptr);
5544
5545 QCOMPARE(object->property("test").toBool(), true);
5546
5547 delete object;
5548}
5549
5550// QTBUG-21626
5551void tst_qqmlecmascript::objectConversion()
5552{
5553 QQmlEngine engine;
5554 QQmlComponent component(&engine, testFileUrl(fileName: "objectConversion.qml"));
5555
5556 QObject *object = component.create();
5557 QVERIFY(object != nullptr);
5558 QVariant retn;
5559 QMetaObject::invokeMethod(obj: object, member: "circularObject", Q_RETURN_ARG(QVariant, retn));
5560 QCOMPARE(retn.value<QJSValue>().property("test").toInt(), int(100));
5561
5562 delete object;
5563}
5564
5565
5566// QTBUG-20242
5567void tst_qqmlecmascript::booleanConversion()
5568{
5569 QQmlEngine engine;
5570 QQmlComponent component(&engine, testFileUrl(fileName: "booleanConversion.qml"));
5571
5572 QObject *object = component.create();
5573 QVERIFY(object != nullptr);
5574
5575 QCOMPARE(object->property("test_true1").toBool(), true);
5576 QCOMPARE(object->property("test_true2").toBool(), true);
5577 QCOMPARE(object->property("test_true3").toBool(), true);
5578 QCOMPARE(object->property("test_true4").toBool(), true);
5579 QCOMPARE(object->property("test_true5").toBool(), true);
5580
5581 QCOMPARE(object->property("test_false1").toBool(), false);
5582 QCOMPARE(object->property("test_false2").toBool(), false);
5583 QCOMPARE(object->property("test_false3").toBool(), false);
5584
5585 delete object;
5586}
5587
5588void tst_qqmlecmascript::handleReferenceManagement()
5589{
5590 int dtorCount = 0;
5591 {
5592 // Linear QObject reference
5593 QQmlEngine hrmEngine;
5594 QQmlComponent component(&hrmEngine, testFileUrl(fileName: "handleReferenceManagement.object.1.qml"));
5595 QObject *object = component.create();
5596 QVERIFY(object != nullptr);
5597 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>(aName: "cro");
5598 cro->setDtorCount(&dtorCount);
5599 QMetaObject::invokeMethod(obj: object, member: "createReference");
5600 gc(engine&: hrmEngine);
5601 QCOMPARE(dtorCount, 0); // second has JS ownership, kept alive by first's reference
5602 delete object;
5603 hrmEngine.collectGarbage();
5604 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
5605 QCoreApplication::processEvents();
5606 QCOMPARE(dtorCount, 3);
5607 }
5608
5609 dtorCount = 0;
5610 {
5611 // Circular QObject reference
5612 QQmlEngine hrmEngine;
5613 QQmlComponent component(&hrmEngine, testFileUrl(fileName: "handleReferenceManagement.object.2.qml"));
5614 QObject *object = component.create();
5615 QVERIFY(object != nullptr);
5616 CircularReferenceObject *cro = object->findChild<CircularReferenceObject*>(aName: "cro");
5617 cro->setDtorCount(&dtorCount);
5618 QMetaObject::invokeMethod(obj: object, member: "circularReference");
5619 gc(engine&: hrmEngine);
5620 QCOMPARE(dtorCount, 2); // both should be cleaned up, since circular references shouldn't keep alive.
5621 delete object;
5622 hrmEngine.collectGarbage();
5623 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
5624 QCoreApplication::processEvents();
5625 QCOMPARE(dtorCount, 3);
5626 }
5627
5628 {
5629 // Dynamic variant property reference keeps target alive
5630 QQmlEngine hrmEngine;
5631 QQmlComponent component(&hrmEngine, testFileUrl(fileName: "handleReferenceManagement.dynprop.qml"));
5632 QObject *object = component.create();
5633 QVERIFY(object != nullptr);
5634 QMetaObject::invokeMethod(obj: object, member: "createReference");
5635 gc(engine&: hrmEngine);
5636 QMetaObject::invokeMethod(obj: object, member: "ensureReference");
5637 gc(engine&: hrmEngine);
5638 QMetaObject::invokeMethod(obj: object, member: "removeReference");
5639 gc(engine&: hrmEngine);
5640 QMetaObject::invokeMethod(obj: object, member: "ensureDeletion");
5641 QCOMPARE(object->property("success").toBool(), true);
5642 delete object;
5643 }
5644
5645 {
5646 // Dynamic Item property reference keeps target alive
5647 QQmlEngine hrmEngine;
5648 QQmlComponent component(&hrmEngine, testFileUrl(fileName: "handleReferenceManagement.dynprop.2.qml"));
5649 QObject *object = component.create();
5650 QVERIFY(object != nullptr);
5651 QMetaObject::invokeMethod(obj: object, member: "createReference");
5652 gc(engine&: hrmEngine);
5653 QMetaObject::invokeMethod(obj: object, member: "ensureReference");
5654 gc(engine&: hrmEngine);
5655 QMetaObject::invokeMethod(obj: object, member: "removeReference");
5656 gc(engine&: hrmEngine);
5657 QMetaObject::invokeMethod(obj: object, member: "ensureDeletion");
5658 QCOMPARE(object->property("success").toBool(), true);
5659 delete object;
5660 }
5661
5662 {
5663 // Item property reference to deleted item doesn't crash
5664 QQmlEngine hrmEngine;
5665 QQmlComponent component(&hrmEngine, testFileUrl(fileName: "handleReferenceManagement.dynprop.3.qml"));
5666 QObject *object = component.create();
5667 QVERIFY(object != nullptr);
5668 QMetaObject::invokeMethod(obj: object, member: "createReference");
5669 gc(engine&: hrmEngine);
5670 QMetaObject::invokeMethod(obj: object, member: "ensureReference");
5671 gc(engine&: hrmEngine);
5672 QMetaObject::invokeMethod(obj: object, member: "manuallyDelete");
5673 gc(engine&: hrmEngine);
5674 QMetaObject::invokeMethod(obj: object, member: "ensureDeleted");
5675 QCOMPARE(object->property("success").toBool(), true);
5676 delete object;
5677 }
5678}
5679
5680void tst_qqmlecmascript::stringArg()
5681{
5682 QQmlEngine engine;
5683 QQmlComponent component(&engine, testFileUrl(fileName: "stringArg.qml"));
5684 QObject *object = component.create();
5685 QVERIFY(object != nullptr);
5686 QMetaObject::invokeMethod(obj: object, member: "success");
5687 QVERIFY(object->property("returnValue").toBool());
5688
5689 QString w1 = testFileUrl(fileName: "stringArg.qml").toString() + QLatin1String(":45: Error: String.arg(): Invalid arguments");
5690 QTest::ignoreMessage(type: QtWarningMsg, message: w1.toLatin1().constData());
5691 QMetaObject::invokeMethod(obj: object, member: "failure");
5692 QVERIFY(object->property("returnValue").toBool());
5693
5694 delete object;
5695}
5696
5697void tst_qqmlecmascript::readonlyDeclaration()
5698{
5699 QQmlEngine engine;
5700 QQmlComponent component(&engine, testFileUrl(fileName: "readonlyDeclaration.qml"));
5701
5702 QObject *object = component.create();
5703 QVERIFY(object != nullptr);
5704
5705 QCOMPARE(object->property("test").toBool(), true);
5706
5707 delete object;
5708}
5709
5710Q_DECLARE_METATYPE(QList<int>)
5711Q_DECLARE_METATYPE(QList<qreal>)
5712Q_DECLARE_METATYPE(QList<bool>)
5713Q_DECLARE_METATYPE(QList<QString>)
5714Q_DECLARE_METATYPE(QList<QUrl>)
5715void tst_qqmlecmascript::sequenceConversionRead()
5716{
5717 QQmlEngine engine;
5718
5719 {
5720 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.read.qml");
5721 QQmlComponent component(&engine, qmlFile);
5722 QObject *object = component.create();
5723 QVERIFY(object != nullptr);
5724 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>(aName: "msco");
5725 QVERIFY(seq != nullptr);
5726
5727 QMetaObject::invokeMethod(obj: object, member: "readSequences");
5728 QList<int> intList; intList << 1 << 2 << 3 << 4;
5729 QCOMPARE(object->property("intListLength").toInt(), intList.length());
5730 QCOMPARE(object->property("intList").value<QList<int> >(), intList);
5731 QList<qreal> qrealList; qrealList << 1.1 << 2.2 << 3.3 << 4.4;
5732 QCOMPARE(object->property("qrealListLength").toInt(), qrealList.length());
5733 QCOMPARE(object->property("qrealList").value<QList<qreal> >(), qrealList);
5734 QList<bool> boolList; boolList << true << false << true << false;
5735 QCOMPARE(object->property("boolListLength").toInt(), boolList.length());
5736 QCOMPARE(object->property("boolList").value<QList<bool> >(), boolList);
5737 QList<QString> stringList; stringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5738 QCOMPARE(object->property("stringListLength").toInt(), stringList.length());
5739 QCOMPARE(object->property("stringList").value<QList<QString> >(), stringList);
5740 QList<QUrl> urlList; urlList << QUrl("http://www.example1.com") << QUrl("http://www.example2.com") << QUrl("http://www.example3.com");
5741 QCOMPARE(object->property("urlListLength").toInt(), urlList.length());
5742 QCOMPARE(object->property("urlList").value<QList<QUrl> >(), urlList);
5743 QStringList qstringList; qstringList << QLatin1String("first") << QLatin1String("second") << QLatin1String("third") << QLatin1String("fourth");
5744 QCOMPARE(object->property("qstringListLength").toInt(), qstringList.length());
5745 QCOMPARE(object->property("qstringList").value<QStringList>(), qstringList);
5746
5747 QMetaObject::invokeMethod(obj: object, member: "readSequenceElements");
5748 QCOMPARE(object->property("intVal").toInt(), 2);
5749 QCOMPARE(object->property("qrealVal").toReal(), 2.2);
5750 QCOMPARE(object->property("boolVal").toBool(), false);
5751 QCOMPARE(object->property("stringVal").toString(), QString(QLatin1String("second")));
5752 QCOMPARE(object->property("urlVal").toUrl(), QUrl("http://www.example2.com"));
5753 QCOMPARE(object->property("qstringVal").toString(), QString(QLatin1String("second")));
5754
5755 QMetaObject::invokeMethod(obj: object, member: "enumerateSequenceElements");
5756 QCOMPARE(object->property("enumerationMatches").toBool(), true);
5757
5758 intList.clear(); intList << 1 << 2 << 3 << 4 << 5; // set by the enumerateSequenceElements test.
5759 QQmlProperty seqProp(seq, "intListProperty");
5760 QCOMPARE(seqProp.read().value<QList<int> >(), intList);
5761 QQmlProperty seqProp2(seq, "intListProperty", &engine);
5762 QCOMPARE(seqProp2.read().value<QList<int> >(), intList);
5763
5764 QMetaObject::invokeMethod(obj: object, member: "testReferenceDeletion");
5765 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5766
5767 delete object;
5768 }
5769
5770 {
5771 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.read.error.qml");
5772 QQmlComponent component(&engine, qmlFile);
5773 QObject *object = component.create();
5774 QVERIFY(object != nullptr);
5775 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>(aName: "msco");
5776 QVERIFY(seq != nullptr);
5777
5778 // we haven't registered QList<NonRegisteredType> as a sequence type.
5779 QString warningOne = QLatin1String("QMetaProperty::read: Unable to handle unregistered datatype 'QList<NonRegisteredType>' for property 'MySequenceConversionObject::typeListProperty'");
5780 QString warningTwo = qmlFile.toString() + QLatin1String(":18: TypeError: Cannot read property 'length' of undefined");
5781 QTest::ignoreMessage(type: QtWarningMsg, message: warningOne.toLatin1().constData());
5782 QTest::ignoreMessage(type: QtWarningMsg, message: warningTwo.toLatin1().constData());
5783
5784 QMetaObject::invokeMethod(obj: object, member: "performTest");
5785
5786 // QList<NonRegisteredType> has not been registered as a sequence type.
5787 QCOMPARE(object->property("pointListLength").toInt(), 0);
5788 QVERIFY(!object->property("pointList").isValid());
5789 QTest::ignoreMessage(type: QtWarningMsg, message: "QMetaProperty::read: Unable to handle unregistered datatype 'QList<NonRegisteredType>' for property 'MySequenceConversionObject::typeListProperty'");
5790 QQmlProperty seqProp(seq, "typeListProperty", &engine);
5791 QVERIFY(!seqProp.read().isValid()); // not a valid/known sequence type
5792
5793 delete object;
5794 }
5795}
5796
5797void tst_qqmlecmascript::sequenceConversionWrite()
5798{
5799 QQmlEngine engine;
5800 {
5801 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.write.qml");
5802 QQmlComponent component(&engine, qmlFile);
5803 QObject *object = component.create();
5804 QVERIFY(object != nullptr);
5805 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>(aName: "msco");
5806 QVERIFY(seq != nullptr);
5807
5808 QMetaObject::invokeMethod(obj: object, member: "writeSequences");
5809 QCOMPARE(object->property("success").toBool(), true);
5810
5811 QMetaObject::invokeMethod(obj: object, member: "writeSequenceElements");
5812 QCOMPARE(object->property("success").toBool(), true);
5813
5814 QMetaObject::invokeMethod(obj: object, member: "writeOtherElements");
5815 QCOMPARE(object->property("success").toBool(), true);
5816
5817 QMetaObject::invokeMethod(obj: object, member: "testReferenceDeletion");
5818 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5819
5820 delete object;
5821 }
5822
5823 {
5824 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.write.error.qml");
5825 QQmlComponent component(&engine, qmlFile);
5826 QObject *object = component.create();
5827 QVERIFY(object != nullptr);
5828 MySequenceConversionObject *seq = object->findChild<MySequenceConversionObject*>(aName: "msco");
5829 QVERIFY(seq != nullptr);
5830
5831 // Behavior change in 5.14: due to added auto-magical conversions, it is possible to assign to
5832 // QList<QPoint>, even though it is not a registered sequence type
5833 QTest::ignoreMessage(type: QtMsgType::QtWarningMsg, messagePattern: QRegularExpression("Could not convert array value at position 1 from QString to QPoint"));
5834 QMetaObject::invokeMethod(obj: object, member: "performTest");
5835
5836 QList<QPoint> pointList; pointList << QPoint(7, 7) << QPoint(0,0) << QPoint(8, 8) << QPoint(9, 9); // original values, shouldn't have changed
5837 QCOMPARE(seq->pointListProperty(), pointList);
5838
5839 delete object;
5840 }
5841}
5842
5843void tst_qqmlecmascript::sequenceConversionArray()
5844{
5845 // ensure that in JS the returned sequences act just like normal JS Arrays.
5846 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.array.qml");
5847 QQmlEngine engine;
5848 QQmlComponent component(&engine, qmlFile);
5849 QScopedPointer<QObject> object(component.create());
5850 QVERIFY(object != nullptr);
5851 QMetaObject::invokeMethod(obj: object.data(), member: "indexedAccess");
5852 QVERIFY(object->property("success").toBool());
5853 QMetaObject::invokeMethod(obj: object.data(), member: "arrayOperations");
5854 QVERIFY(object->property("success").toBool());
5855 QMetaObject::invokeMethod(obj: object.data(), member: "testEqualitySemantics");
5856 QVERIFY(object->property("success").toBool());
5857 QMetaObject::invokeMethod(obj: object.data(), member: "testReferenceDeletion");
5858 QCOMPARE(object->property("referenceDeletion").toBool(), true);
5859 QMetaObject::invokeMethod(obj: object.data(), member: "jsonConversion");
5860 QVERIFY(object->property("success").toBool());
5861}
5862
5863
5864void tst_qqmlecmascript::sequenceConversionIndexes()
5865{
5866 // ensure that we gracefully fail if unsupported index values are specified.
5867 // Qt container classes only support non-negative, signed integer index values.
5868 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.indexes.qml");
5869 QQmlEngine engine;
5870 QQmlComponent component(&engine, qmlFile);
5871 QObject *object = component.create();
5872 QVERIFY(object != nullptr);
5873 QString w1 = qmlFile.toString() + QLatin1String(":34: Index out of range during length set");
5874 QString w2 = qmlFile.toString() + QLatin1String(":41: Index out of range during indexed set");
5875 QString w3 = qmlFile.toString() + QLatin1String(":48: Index out of range during indexed get");
5876 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w1));
5877 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w2));
5878 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(w3));
5879 QMetaObject::invokeMethod(obj: object, member: "indexedAccess");
5880 QVERIFY(object->property("success").toBool());
5881 QMetaObject::invokeMethod(obj: object, member: "indexOf");
5882 QVERIFY(object->property("success").toBool());
5883 delete object;
5884}
5885
5886void tst_qqmlecmascript::sequenceConversionThreads()
5887{
5888 // ensure that sequence conversion operations work correctly in a worker thread
5889 // and that serialisation between the main and worker thread succeeds.
5890 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.threads.qml");
5891 QQmlEngine engine;
5892 QQmlComponent component(&engine, qmlFile);
5893 QObject *object = component.create();
5894 QVERIFY(object != nullptr);
5895
5896 QMetaObject::invokeMethod(obj: object, member: "testIntSequence");
5897 QTRY_VERIFY(object->property("finished").toBool());
5898 QVERIFY(object->property("success").toBool());
5899
5900 QMetaObject::invokeMethod(obj: object, member: "testQrealSequence");
5901 QTRY_VERIFY(object->property("finished").toBool());
5902 QVERIFY(object->property("success").toBool());
5903
5904 QMetaObject::invokeMethod(obj: object, member: "testBoolSequence");
5905 QTRY_VERIFY(object->property("finished").toBool());
5906 QVERIFY(object->property("success").toBool());
5907
5908 QMetaObject::invokeMethod(obj: object, member: "testStringSequence");
5909 QTRY_VERIFY(object->property("finished").toBool());
5910 QVERIFY(object->property("success").toBool());
5911
5912 QMetaObject::invokeMethod(obj: object, member: "testQStringSequence");
5913 QTRY_VERIFY(object->property("finished").toBool());
5914 QVERIFY(object->property("success").toBool());
5915
5916 QMetaObject::invokeMethod(obj: object, member: "testUrlSequence");
5917 QTRY_VERIFY(object->property("finished").toBool());
5918 QVERIFY(object->property("success").toBool());
5919
5920 QMetaObject::invokeMethod(obj: object, member: "testVariantSequence");
5921 QTRY_VERIFY(object->property("finished").toBool());
5922 QVERIFY(object->property("success").toBool());
5923
5924 delete object;
5925}
5926
5927void tst_qqmlecmascript::sequenceConversionBindings()
5928{
5929 QQmlEngine engine;
5930 {
5931 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.bindings.qml");
5932 QQmlComponent component(&engine, qmlFile);
5933 QObject *object = component.create();
5934 QVERIFY(object != nullptr);
5935 QList<int> intList; intList << 1 << 2 << 3 << 12 << 7;
5936 QCOMPARE(object->property("boundSequence").value<QList<int> >(), intList);
5937 QCOMPARE(object->property("boundElement").toInt(), intList.at(3));
5938 QList<int> intListTwo; intListTwo << 1 << 2 << 3 << 12 << 14;
5939 QCOMPARE(object->property("boundSequenceTwo").value<QList<int> >(), intListTwo);
5940 delete object;
5941 }
5942
5943 {
5944 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.bindings.error.qml");
5945 QString warning = QString(QLatin1String("%1:17:9: Unable to assign QList<int> to QList<bool>")).arg(a: qmlFile.toString());
5946 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
5947 QQmlComponent component(&engine, qmlFile);
5948 QObject *object = component.create();
5949 QVERIFY(object != nullptr);
5950 delete object;
5951 }
5952}
5953
5954void tst_qqmlecmascript::sequenceConversionCopy()
5955{
5956 QUrl qmlFile = testFileUrl(fileName: "sequenceConversion.copy.qml");
5957 QQmlEngine engine;
5958 QQmlComponent component(&engine, qmlFile);
5959 QObject *object = component.create();
5960 QVERIFY(object != nullptr);
5961 QMetaObject::invokeMethod(obj: object, member: "testCopySequences");
5962 QCOMPARE(object->property("success").toBool(), true);
5963 QMetaObject::invokeMethod(obj: object, member: "readSequenceCopyElements");
5964 QCOMPARE(object->property("success").toBool(), true);
5965 QMetaObject::invokeMethod(obj: object, member: "testEqualitySemantics");
5966 QCOMPARE(object->property("success").toBool(), true);
5967 QMetaObject::invokeMethod(obj: object, member: "testCopyParameters");
5968 QCOMPARE(object->property("success").toBool(), true);
5969 QMetaObject::invokeMethod(obj: object, member: "testReferenceParameters");
5970 QCOMPARE(object->property("success").toBool(), true);
5971 delete object;
5972}
5973
5974void tst_qqmlecmascript::assignSequenceTypes()
5975{
5976 QQmlEngine engine;
5977
5978 // test binding array to sequence type property
5979 {
5980 QQmlComponent component(&engine, testFileUrl(fileName: "assignSequenceTypes.1.qml"));
5981 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(object: component.create());
5982 QVERIFY(object != nullptr);
5983 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
5984 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2 << 3));
5985 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
5986 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
5987 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
5988 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
5989 delete object;
5990 }
5991
5992 // test binding literal to sequence type property
5993 {
5994 QQmlComponent component(&engine, testFileUrl(fileName: "assignSequenceTypes.2.qml"));
5995 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(object: component.create());
5996 QVERIFY(object != nullptr);
5997 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
5998 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
5999 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
6000 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
6001 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
6002 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
6003 delete object;
6004 }
6005
6006 // test binding single value to sequence type property
6007 {
6008 QQmlComponent component(&engine, testFileUrl(fileName: "assignSequenceTypes.3.qml"));
6009 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(object: component.create());
6010 QVERIFY(object != nullptr);
6011 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
6012 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1));
6013 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
6014 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
6015 delete object;
6016 }
6017
6018 // test assigning array to sequence type property in js function
6019 {
6020 QQmlComponent component(&engine, testFileUrl(fileName: "assignSequenceTypes.4.qml"));
6021 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(object: component.create());
6022 QVERIFY(object != nullptr);
6023 QCOMPARE(object->intListProperty(), (QList<int>() << 1 << 2));
6024 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1 << 2.2 << 3));
6025 QCOMPARE(object->boolListProperty(), (QList<bool>() << false << true));
6026 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com") << QUrl("http://www.example2.com")));
6027 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one") << QLatin1String("two")));
6028 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("one") << QLatin1String("two")));
6029 delete object;
6030 }
6031
6032 // test assigning literal to sequence type property in js function
6033 {
6034 QQmlComponent component(&engine, testFileUrl(fileName: "assignSequenceTypes.5.qml"));
6035 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(object: component.create());
6036 QVERIFY(object != nullptr);
6037 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
6038 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1.1));
6039 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
6040 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl("http://www.example1.com")));
6041 QCOMPARE(object->stringListProperty(), (QList<QString>() << QLatin1String("one")));
6042 QCOMPARE(object->qstringListProperty(), (QStringList() << QLatin1String("two")));
6043 delete object;
6044 }
6045
6046 // test assigning single value to sequence type property in js function
6047 {
6048 QQmlComponent component(&engine, testFileUrl(fileName: "assignSequenceTypes.6.qml"));
6049 MySequenceConversionObject *object = qobject_cast<MySequenceConversionObject *>(object: component.create());
6050 QVERIFY(object != nullptr);
6051 QCOMPARE(object->intListProperty(), (QList<int>() << 1));
6052 QCOMPARE(object->qrealListProperty(), (QList<qreal>() << 1));
6053 QCOMPARE(object->boolListProperty(), (QList<bool>() << false));
6054 QCOMPARE(object->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
6055 delete object;
6056 }
6057
6058 // test QList<QUrl> literal assignment and binding assignment causes url resolution when required
6059 {
6060 QQmlComponent component(&engine, testFileUrl(fileName: "assignSequenceTypes.7.qml"));
6061 QObject *object = component.create();
6062 QVERIFY(object != nullptr);
6063 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(aName: QLatin1String("msco1"));
6064 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(aName: QLatin1String("msco2"));
6065 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(aName: QLatin1String("msco3"));
6066 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(aName: QLatin1String("msco4"));
6067 MySequenceConversionObject *msco5 = object->findChild<MySequenceConversionObject *>(aName: QLatin1String("msco5"));
6068 QVERIFY(msco1 != nullptr && msco2 != nullptr && msco3 != nullptr && msco4 != nullptr && msco5 != nullptr);
6069 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
6070 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html"))));
6071 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
6072 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
6073 QCOMPARE(msco5->urlListProperty(), (QList<QUrl>() << QUrl(testFileUrl("example.html")) << QUrl(testFileUrl("example2.html"))));
6074 delete object;
6075 }
6076
6077 {
6078 QQmlComponent component(&engine, testFileUrl(fileName: "assignSequenceTypes.8.qml"));
6079 QScopedPointer<QObject> object(component.create());
6080 QVERIFY(object != nullptr);
6081 QVariant result;
6082 QMetaObject::invokeMethod(obj: object.data(), member: "tryWritingReadOnlySequence", Q_RETURN_ARG(QVariant, result));
6083 QVERIFY(result.type() == QVariant::Bool);
6084 QVERIFY(result.toBool());
6085 }
6086}
6087
6088// Test that assigning a null object works
6089// Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4
6090void tst_qqmlecmascript::nullObjectBinding()
6091{
6092 QQmlEngine engine;
6093 QQmlComponent component(&engine, testFileUrl(fileName: "nullObjectBinding.qml"));
6094
6095 QObject *object = component.create();
6096 QVERIFY(object != nullptr);
6097
6098 QVERIFY(object->property("test") == QVariant::fromValue((QObject *)nullptr));
6099
6100 delete object;
6101}
6102
6103void tst_qqmlecmascript::nullObjectInitializer()
6104{
6105 QQmlEngine engine;
6106 {
6107 QQmlComponent component(&engine, testFileUrl(fileName: "nullObjectInitializer.qml"));
6108 QScopedPointer<QObject> obj(component.create());
6109 QVERIFY(!obj.isNull());
6110
6111 QQmlData *ddata = QQmlData::get(object: obj.data(), /*create*/false);
6112 QVERIFY(ddata);
6113
6114 {
6115 const int propertyIndex = obj->metaObject()->indexOfProperty(name: "testProperty");
6116 QVERIFY(propertyIndex > 0);
6117 QVERIFY(!ddata->hasBindingBit(propertyIndex));
6118 }
6119
6120 QVariant value = obj->property(name: "testProperty");
6121 QVERIFY(value.userType() == qMetaTypeId<QObject*>());
6122 QVERIFY(!value.value<QObject*>());
6123 }
6124
6125 {
6126 QQmlComponent component(&engine, testFileUrl(fileName: "nullObjectInitializer.2.qml"));
6127 QScopedPointer<QObject> obj(component.create());
6128 QVERIFY(!obj.isNull());
6129
6130 QQmlData *ddata = QQmlData::get(object: obj.data(), /*create*/false);
6131 QVERIFY(ddata);
6132
6133 {
6134 const int propertyIndex = obj->metaObject()->indexOfProperty(name: "testProperty");
6135 QVERIFY(propertyIndex > 0);
6136 QVERIFY(!ddata->hasBindingBit(propertyIndex));
6137 }
6138
6139 QVERIFY(obj->property("success").toBool());
6140
6141 QVariant value = obj->property(name: "testProperty");
6142 QVERIFY(value.userType() == qMetaTypeId<QObject*>());
6143 QVERIFY(!value.value<QObject*>());
6144 }
6145
6146 {
6147 QQmlComponent component(&engine, testFileUrl(fileName: "qmlVarNullBinding.qml"));
6148 QScopedPointer<QObject> obj(component.create());
6149 QVERIFY(!obj.isNull());
6150 QVERIFY(!obj->property("signalSeen").toBool());
6151 }
6152}
6153
6154// Test that bindings don't evaluate once the engine has been destroyed
6155void tst_qqmlecmascript::deletedEngine()
6156{
6157 QQmlEngine *engine = new QQmlEngine;
6158 QQmlComponent component(engine, testFileUrl(fileName: "deletedEngine.qml"));
6159
6160 QObject *object = component.create();
6161 QVERIFY(object != nullptr);
6162
6163 QCOMPARE(object->property("a").toInt(), 39);
6164 object->setProperty(name: "b", value: QVariant(9));
6165 QCOMPARE(object->property("a").toInt(), 117);
6166
6167 delete engine;
6168
6169 QCOMPARE(object->property("a").toInt(), 0);
6170 object->setProperty(name: "b", value: QVariant(10));
6171 object->setProperty(name: "b", value: QVariant());
6172 QCOMPARE(object->property("a").toInt(), 0);
6173
6174 delete object;
6175}
6176
6177// Test the crashing part of QTBUG-9705
6178void tst_qqmlecmascript::libraryScriptAssert()
6179{
6180 QQmlEngine engine;
6181 QQmlComponent component(&engine, testFileUrl(fileName: "libraryScriptAssert.qml"));
6182
6183 QObject *object = component.create();
6184 QVERIFY(object != nullptr);
6185
6186 delete object;
6187}
6188
6189void tst_qqmlecmascript::variantsAssignedUndefined()
6190{
6191 QQmlEngine engine;
6192 QQmlComponent component(&engine, testFileUrl(fileName: "variantsAssignedUndefined.qml"));
6193
6194 QObject *object = component.create();
6195 QVERIFY(object != nullptr);
6196
6197 QCOMPARE(object->property("test1").toInt(), 10);
6198 QCOMPARE(object->property("test2").toInt(), 11);
6199
6200 object->setProperty(name: "runTest", value: true);
6201
6202 QCOMPARE(object->property("test1"), QVariant());
6203 QCOMPARE(object->property("test2"), QVariant());
6204
6205
6206 delete object;
6207}
6208
6209void tst_qqmlecmascript::variants()
6210{
6211 QQmlEngine engine;
6212 QQmlComponent component(&engine, testFileUrl(fileName: "variants.qml"));
6213
6214 QScopedPointer<QObject> object(component.create());
6215 QVERIFY(object != nullptr);
6216
6217 QCOMPARE(object->property("undefinedVariant").type(), QVariant::Invalid);
6218 QCOMPARE(int(object->property("nullVariant").type()), int(QMetaType::Nullptr));
6219 QCOMPARE(object->property("intVariant").type(), QVariant::Int);
6220 QCOMPARE(object->property("doubleVariant").type(), QVariant::Double);
6221
6222 QVariant result;
6223 QMetaObject::invokeMethod(obj: object.get(), member: "checkNull", Q_RETURN_ARG(QVariant, result));
6224 QCOMPARE(result.toBool(), true);
6225
6226 QMetaObject::invokeMethod(obj: object.get(), member: "checkUndefined", Q_RETURN_ARG(QVariant, result));
6227 QCOMPARE(result.toBool(), true);
6228
6229 QMetaObject::invokeMethod(obj: object.get(), member: "checkNumber", Q_RETURN_ARG(QVariant, result));
6230 QCOMPARE(result.toBool(), true);
6231}
6232
6233void tst_qqmlecmascript::qtbug_9792()
6234{
6235 QQmlEngine engine;
6236 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_9792.qml"));
6237
6238 QQmlContext *context = new QQmlContext(engine.rootContext());
6239
6240 MyQmlObject *object = qobject_cast<MyQmlObject*>(object: component.create(context));
6241 QVERIFY(object != nullptr);
6242
6243 QTest::ignoreMessage(type: QtDebugMsg, message: "Hello world!");
6244 object->basicSignal();
6245
6246 delete context;
6247
6248 QQmlTestMessageHandler messageHandler;
6249
6250 object->basicSignal();
6251
6252 QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
6253
6254 delete object;
6255}
6256
6257// Verifies that QPointer<>s used in the vmemetaobject are cleaned correctly
6258void tst_qqmlecmascript::qtcreatorbug_1289()
6259{
6260 QQmlEngine engine;
6261 QQmlComponent component(&engine, testFileUrl(fileName: "qtcreatorbug_1289.qml"));
6262
6263 QObject *o = component.create();
6264 QVERIFY(o != nullptr);
6265
6266 QObject *nested = qvariant_cast<QObject *>(v: o->property(name: "object"));
6267 QVERIFY(nested != nullptr);
6268
6269 QVERIFY(qvariant_cast<QObject *>(nested->property("nestedObject")) == o);
6270
6271 delete nested;
6272 nested = qvariant_cast<QObject *>(v: o->property(name: "object"));
6273 QVERIFY(!nested);
6274
6275 // If the bug is present, the next line will crash
6276 delete o;
6277}
6278
6279// Test that we shut down without stupid warnings
6280void tst_qqmlecmascript::noSpuriousWarningsAtShutdown()
6281{
6282 QQmlEngine engine;
6283 {
6284 QQmlComponent component(&engine, testFileUrl(fileName: "noSpuriousWarningsAtShutdown.qml"));
6285
6286 QObject *o = component.create();
6287
6288 QQmlTestMessageHandler messageHandler;
6289
6290 delete o;
6291
6292 QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
6293 }
6294
6295
6296 {
6297 QQmlComponent component(&engine, testFileUrl(fileName: "noSpuriousWarningsAtShutdown.2.qml"));
6298
6299 QObject *o = component.create();
6300
6301 QQmlTestMessageHandler messageHandler;
6302
6303 delete o;
6304
6305 QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
6306 }
6307}
6308
6309void tst_qqmlecmascript::canAssignNullToQObject()
6310{
6311 QQmlEngine engine;
6312 {
6313 QQmlComponent component(&engine, testFileUrl(fileName: "canAssignNullToQObject.1.qml"));
6314
6315 MyQmlObject *o = qobject_cast<MyQmlObject *>(object: component.create());
6316 QVERIFY(o != nullptr);
6317
6318 QVERIFY(o->objectProperty() != nullptr);
6319
6320 o->setProperty(name: "runTest", value: true);
6321
6322 QVERIFY(!o->objectProperty());
6323
6324 delete o;
6325 }
6326
6327 {
6328 QQmlComponent component(&engine, testFileUrl(fileName: "canAssignNullToQObject.2.qml"));
6329
6330 MyQmlObject *o = qobject_cast<MyQmlObject *>(object: component.create());
6331 QVERIFY(o != nullptr);
6332
6333 QVERIFY(!o->objectProperty());
6334
6335 delete o;
6336 }
6337}
6338
6339void tst_qqmlecmascript::functionAssignment_fromBinding()
6340{
6341 QQmlEngine engine;
6342 QQmlComponent component(&engine, testFileUrl(fileName: "functionAssignment.1.qml"));
6343
6344 QString url = component.url().toString();
6345 QString w1 = url + ":4:5: Unable to assign a function to a property of any type other than var.";
6346 QString w2 = url + ":5:5: Invalid use of Qt.binding() in a binding declaration.";
6347 QString w3 = url + ":6:5: Invalid use of Qt.binding() in a binding declaration.";
6348 QString w4 = url + ":7:5: Invalid use of Qt.binding() in a binding declaration.";
6349 QTest::ignoreMessage(type: QtWarningMsg, message: w1.toLatin1().constData());
6350 QTest::ignoreMessage(type: QtWarningMsg, message: w2.toLatin1().constData());
6351 QTest::ignoreMessage(type: QtWarningMsg, message: w3.toLatin1().constData());
6352 QTest::ignoreMessage(type: QtWarningMsg, message: w4.toLatin1().constData());
6353
6354 MyQmlObject *o = qobject_cast<MyQmlObject *>(object: component.create());
6355 QVERIFY(o != nullptr);
6356
6357 QVERIFY(!o->property("a").isValid());
6358
6359 delete o;
6360}
6361
6362void tst_qqmlecmascript::functionAssignment_fromJS()
6363{
6364 QFETCH(QString, triggerProperty);
6365
6366 QQmlEngine engine;
6367 QQmlComponent component(&engine, testFileUrl(fileName: "functionAssignment.2.qml"));
6368 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
6369
6370 MyQmlObject *o = qobject_cast<MyQmlObject *>(object: component.create());
6371 QVERIFY(o != nullptr);
6372 QVERIFY(!o->property("a").isValid());
6373
6374 o->setProperty(name: "aNumber", value: QVariant(5));
6375 o->setProperty(name: triggerProperty.toUtf8().constData(), value: true);
6376 QCOMPARE(o->property("a"), QVariant(50));
6377
6378 o->setProperty(name: "aNumber", value: QVariant(10));
6379 QCOMPARE(o->property("a"), QVariant(100));
6380
6381 delete o;
6382}
6383
6384void tst_qqmlecmascript::functionAssignment_fromJS_data()
6385{
6386 QTest::addColumn<QString>(name: "triggerProperty");
6387
6388 QTest::newRow(dataTag: "assign to property") << "assignToProperty";
6389 QTest::newRow(dataTag: "assign to property, from JS file") << "assignToPropertyFromJsFile";
6390
6391 QTest::newRow(dataTag: "assign to value type") << "assignToValueType";
6392
6393 QTest::newRow(dataTag: "use 'this'") << "assignWithThis";
6394 QTest::newRow(dataTag: "use 'this' from JS file") << "assignWithThisFromJsFile";
6395}
6396
6397void tst_qqmlecmascript::functionAssignmentfromJS_invalid()
6398{
6399 QQmlEngine engine;
6400 QQmlComponent component(&engine, testFileUrl(fileName: "functionAssignment.2.qml"));
6401 QVERIFY2(component.errorString().isEmpty(), qPrintable(component.errorString()));
6402
6403 MyQmlObject *o = qobject_cast<MyQmlObject *>(object: component.create());
6404 QVERIFY(o != nullptr);
6405 QVERIFY(!o->property("a").isValid());
6406
6407 o->setProperty(name: "assignFuncWithoutReturn", value: true);
6408 QVERIFY(!o->property("a").isValid());
6409
6410 QString url = component.url().toString();
6411 QString warning = url + ":67: Unable to assign QString to int";
6412 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
6413 o->setProperty(name: "assignWrongType", value: true);
6414
6415 warning = url + ":71: Unable to assign QString to int";
6416 QTest::ignoreMessage(type: QtWarningMsg, message: warning.toLatin1().constData());
6417 o->setProperty(name: "assignWrongTypeToValueType", value: true);
6418
6419 delete o;
6420}
6421
6422void tst_qqmlecmascript::functionAssignment_afterBinding()
6423{
6424 QQmlEngine engine;
6425 QQmlComponent component(&engine, testFileUrl(fileName: "functionAssignment.3.qml"));
6426
6427 QString url = component.url().toString();
6428 QString w1 = url + ":16: Error: Cannot assign JavaScript function to int";
6429 QTest::ignoreMessage(type: QtWarningMsg, message: w1.toLatin1().constData());
6430
6431 QObject *o = component.create();
6432 QVERIFY(o != nullptr);
6433 QCOMPARE(o->property("t1"), QVariant::fromValue<int>(4)); // should have bound
6434 QCOMPARE(o->property("t2"), QVariant::fromValue<int>(2)); // should not have changed
6435
6436 delete o;
6437}
6438
6439void tst_qqmlecmascript::eval()
6440{
6441 QQmlEngine engine;
6442 QQmlComponent component(&engine, testFileUrl(fileName: "eval.qml"));
6443
6444 QObject *o = component.create();
6445 QVERIFY(o != nullptr);
6446
6447 QCOMPARE(o->property("test1").toBool(), true);
6448 QCOMPARE(o->property("test2").toBool(), true);
6449 QCOMPARE(o->property("test3").toBool(), true);
6450 QCOMPARE(o->property("test4").toBool(), true);
6451 QCOMPARE(o->property("test5").toBool(), true);
6452
6453 delete o;
6454}
6455
6456void tst_qqmlecmascript::function()
6457{
6458 QQmlEngine engine;
6459 QQmlComponent component(&engine, testFileUrl(fileName: "function.qml"));
6460
6461 QObject *o = component.create();
6462 QVERIFY(o != nullptr);
6463
6464 QCOMPARE(o->property("test1").toBool(), true);
6465 QCOMPARE(o->property("test2").toBool(), true);
6466 QCOMPARE(o->property("test3").toBool(), true);
6467
6468 delete o;
6469}
6470
6471// QTBUG-77096
6472void tst_qqmlecmascript::topLevelGeneratorFunction()
6473{
6474 QQmlEngine engine;
6475 QQmlComponent component(&engine, testFileUrl(fileName: "generatorFunction.qml"));
6476
6477 QScopedPointer<QObject> o {component.create()};
6478 if (!o)
6479 qDebug() << component.errorString();
6480 QVERIFY(o != nullptr);
6481
6482 // check that generator works correctly in QML
6483 QCOMPARE(o->property("test1").toBool(), true);
6484 QCOMPARE(o->property("test2").toBool(), true);
6485 QCOMPARE(o->property("test3").toBool(), true);
6486 QCOMPARE(o->property("done").toBool(), true);
6487
6488 // check that generator is accessible from C++
6489 QVariant returnedValue;
6490 QMetaObject::invokeMethod(obj: o.get(), member: "gen", Q_RETURN_ARG(QVariant, returnedValue));
6491 auto it = returnedValue.value<QJSValue>();
6492 QCOMPARE(it.property("next").callWithInstance(it).property("value").toInt(), 1);
6493}
6494
6495// QTBUG-91491
6496void tst_qqmlecmascript::generatorCrashNewProperty()
6497{
6498 QQmlEngine engine;
6499 QQmlComponent component(&engine, testFileUrl(fileName: "generatorCrashNewProperty.qml"));
6500
6501 QScopedPointer<QObject> o(component.create());
6502
6503 QVERIFY2(o != nullptr, qPrintable(component.errorString()));
6504
6505 QCOMPARE(o->property("a").toInt(), 42);
6506 QCOMPARE(o->property("b").toInt(), 12);
6507 QCOMPARE(o->property("c").toInt(), 42);
6508}
6509
6510void tst_qqmlecmascript::generatorCallsGC()
6511{
6512 QQmlEngine engine;
6513 QQmlComponent component(&engine, testFileUrl(fileName: "generatorCallsGC.qml"));
6514
6515 QScopedPointer<QObject> o(component.create()); // should not crash
6516 QVERIFY2(o != nullptr, qPrintable(component.errorString()));
6517}
6518
6519// Test the "Qt.include" method
6520void tst_qqmlecmascript::include()
6521{
6522 QQmlEngine engine;
6523 // Non-library relative include
6524 {
6525 QQmlComponent component(&engine, testFileUrl(fileName: "include.qml"));
6526 QObject *o = component.create();
6527 QVERIFY(o != nullptr);
6528
6529 QCOMPARE(o->property("test0").toInt(), 99);
6530 QCOMPARE(o->property("test1").toBool(), true);
6531 QCOMPARE(o->property("test2").toBool(), true);
6532 QCOMPARE(o->property("test2_1").toBool(), true);
6533 QCOMPARE(o->property("test3").toBool(), true);
6534 QCOMPARE(o->property("test3_1").toBool(), true);
6535
6536 delete o;
6537 }
6538
6539 // Library relative include
6540 {
6541 QQmlComponent component(&engine, testFileUrl(fileName: "include_shared.qml"));
6542 QObject *o = component.create();
6543 QVERIFY(o != nullptr);
6544
6545 QCOMPARE(o->property("test0").toInt(), 99);
6546 QCOMPARE(o->property("test1").toBool(), true);
6547 QCOMPARE(o->property("test2").toBool(), true);
6548 QCOMPARE(o->property("test2_1").toBool(), true);
6549 QCOMPARE(o->property("test3").toBool(), true);
6550 QCOMPARE(o->property("test3_1").toBool(), true);
6551
6552 delete o;
6553 }
6554
6555 // Callback
6556 {
6557 QQmlComponent component(&engine, testFileUrl(fileName: "include_callback.qml"));
6558 QObject *o = component.create();
6559 QVERIFY(o != nullptr);
6560
6561 QCOMPARE(o->property("test1").toBool(), true);
6562 QCOMPARE(o->property("test2").toBool(), true);
6563 QCOMPARE(o->property("test3").toBool(), true);
6564 QCOMPARE(o->property("test4").toBool(), true);
6565 QCOMPARE(o->property("test5").toBool(), true);
6566 QCOMPARE(o->property("test6").toBool(), true);
6567
6568 delete o;
6569 }
6570
6571 // Including file with ".pragma library"
6572 {
6573 QQmlComponent component(&engine, testFileUrl(fileName: "include_pragma.qml"));
6574 QObject *o = component.create();
6575 QVERIFY(o != nullptr);
6576 QCOMPARE(o->property("test1").toInt(), 100);
6577
6578 delete o;
6579 }
6580
6581 // Including file with ".pragma library", shadowing a global var
6582 {
6583 QQmlComponent component(&engine, testFileUrl(fileName: "include_pragma_shadow.qml"));
6584 QScopedPointer<QObject> o(component.create());
6585 QVERIFY(!o.isNull());
6586 QCOMPARE(o->property("result").toBool(), true);
6587 }
6588
6589 // Remote - error
6590 {
6591 TestHTTPServer server;
6592 QVERIFY2(server.listen(), qPrintable(server.errorString()));
6593 server.serveDirectory(dataDirectory());
6594
6595 QQmlComponent component(&engine, testFileUrl(fileName: "include_remote_missing.qml"));
6596 QObject *o = component.beginCreate(engine.rootContext());
6597 QVERIFY(o != nullptr);
6598 o->setProperty(name: "serverBaseUrl", value: server.baseUrl().toString());
6599 component.completeCreate();
6600
6601 QTRY_VERIFY(o->property("done").toBool());
6602
6603 QCOMPARE(o->property("test1").toBool(), true);
6604 QCOMPARE(o->property("test2").toBool(), true);
6605 QCOMPARE(o->property("test3").toBool(), true);
6606
6607 delete o;
6608 }
6609
6610 // include from resources
6611 {
6612 QQmlComponent component(&engine, QUrl("qrc:///data/include.qml"));
6613 QObject *o = component.create();
6614 QVERIFY(o != nullptr);
6615
6616 QCOMPARE(o->property("test0").toInt(), 99);
6617 QCOMPARE(o->property("test1").toBool(), true);
6618 QCOMPARE(o->property("test2").toBool(), true);
6619 QCOMPARE(o->property("test2_1").toBool(), true);
6620 QCOMPARE(o->property("test3").toBool(), true);
6621 QCOMPARE(o->property("test3_1").toBool(), true);
6622
6623 delete o;
6624 }
6625
6626}
6627
6628void tst_qqmlecmascript::includeRemoteSuccess()
6629{
6630 // Remote - success
6631 TestHTTPServer server;
6632 QVERIFY2(server.listen(), qPrintable(server.errorString()));
6633 server.serveDirectory(dataDirectory());
6634
6635 QQmlEngine engine;
6636 QQmlComponent component(&engine, testFileUrl(fileName: "include_remote.qml"));
6637 QObject *o = component.beginCreate(engine.rootContext());
6638 QVERIFY(o != nullptr);
6639 o->setProperty(name: "serverBaseUrl", value: server.baseUrl().toString());
6640 component.completeCreate();
6641
6642 QTRY_VERIFY(o->property("done").toBool());
6643 QTRY_VERIFY(o->property("done2").toBool());
6644
6645 QCOMPARE(o->property("test1").toBool(), true);
6646 QCOMPARE(o->property("test2").toBool(), true);
6647 QCOMPARE(o->property("test3").toBool(), true);
6648 QCOMPARE(o->property("test4").toBool(), true);
6649 QCOMPARE(o->property("test5").toBool(), true);
6650
6651 QCOMPARE(o->property("test6").toBool(), true);
6652 QCOMPARE(o->property("test7").toBool(), true);
6653 QCOMPARE(o->property("test8").toBool(), true);
6654 QCOMPARE(o->property("test9").toBool(), true);
6655 QCOMPARE(o->property("test10").toBool(), true);
6656
6657 delete o;
6658}
6659
6660void tst_qqmlecmascript::signalHandlers()
6661{
6662 QQmlEngine engine;
6663 QQmlComponent component(&engine, testFileUrl(fileName: "signalHandlers.qml"));
6664 QScopedPointer<QObject> o(component.create());
6665 QVERIFY(o != nullptr);
6666 QCOMPARE(o->property("count").toInt(), 0);
6667 QMetaObject::invokeMethod(obj: o.data(), member: "testSignalCall");
6668 QCOMPARE(o->property("count").toInt(), 1);
6669
6670 QMetaObject::invokeMethod(obj: o.data(), member: "testSignalHandlerCall");
6671 QCOMPARE(o->property("count").toInt(), 1);
6672 QString scopeObjectAsString = o->property(name: "scopeObjectAsString").toString();
6673 QCOMPARE(o->property("errorString").toString(), QString("TypeError: Property 'onTestSignal' of object %1 is not a function").arg(scopeObjectAsString));
6674
6675 QCOMPARE(o->property("funcCount").toInt(), 0);
6676 QMetaObject::invokeMethod(obj: o.data(), member: "testSignalConnection");
6677 QCOMPARE(o->property("funcCount").toInt(), 1);
6678
6679 QMetaObject::invokeMethod(obj: o.data(), member: "testSignalHandlerConnection");
6680 QCOMPARE(o->property("funcCount").toInt(), 2);
6681
6682 QMetaObject::invokeMethod(obj: o.data(), member: "testSignalDefined");
6683 QCOMPARE(o->property("definedResult").toBool(), true);
6684
6685 QMetaObject::invokeMethod(obj: o.data(), member: "testSignalHandlerDefined");
6686 QCOMPARE(o->property("definedHandlerResult").toBool(), true);
6687
6688 QVariant result;
6689 QMetaObject::invokeMethod(obj: o.data(), member: "testConnectionOnAlias", Q_RETURN_ARG(QVariant, result));
6690 QCOMPARE(result.toBool(), true);
6691
6692 QMetaObject::invokeMethod(obj: o.data(), member: "testAliasSignalHandler", Q_RETURN_ARG(QVariant, result));
6693 QCOMPARE(result.toBool(), true);
6694
6695 QMetaObject::invokeMethod(obj: o.data(), member: "testSignalWithClosureArgument", Q_RETURN_ARG(QVariant, result));
6696 QCOMPARE(result.toBool(), true);
6697 QMetaObject::invokeMethod(obj: o.data(), member: "testThisInSignalHandler", Q_RETURN_ARG(QVariant, result));
6698 QCOMPARE(result.toBool(), true);
6699}
6700
6701void tst_qqmlecmascript::qtbug_37351()
6702{
6703 MyTypeObject signalEmitter;
6704 {
6705 QQmlEngine engine;
6706 QQmlComponent component(&engine);
6707 component.setData("import Qt.test 1.0; import QtQml 2.0;\nQtObject {\n"
6708 " Component.onCompleted: {\n"
6709 " testObject.action.connect(function() { print('dont crash'); });"
6710 " }\n"
6711 "}", baseUrl: QUrl());
6712
6713 engine.rootContext()->setContextProperty("testObject", &signalEmitter);
6714
6715 QScopedPointer<QObject> object(component.create());
6716 QVERIFY(!object.isNull());
6717 }
6718 signalEmitter.doAction();
6719 // Don't crash
6720}
6721
6722void tst_qqmlecmascript::qtbug_10696()
6723{
6724 QQmlEngine engine;
6725 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_10696.qml"));
6726 QObject *o = component.create();
6727 QVERIFY(o != nullptr);
6728 delete o;
6729}
6730
6731void tst_qqmlecmascript::qtbug_11606()
6732{
6733 QQmlEngine engine;
6734 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_11606.qml"));
6735 QObject *o = component.create();
6736 QVERIFY(o != nullptr);
6737 QCOMPARE(o->property("test").toBool(), true);
6738 delete o;
6739}
6740
6741void tst_qqmlecmascript::qtbug_11600()
6742{
6743 QQmlEngine engine;
6744 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_11600.qml"));
6745 QObject *o = component.create();
6746 QVERIFY(o != nullptr);
6747 QCOMPARE(o->property("test").toBool(), true);
6748 delete o;
6749}
6750
6751void tst_qqmlecmascript::qtbug_21864()
6752{
6753 QQmlEngine engine;
6754 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_21864.qml"));
6755 QObject *o = component.create();
6756 QVERIFY(o != nullptr);
6757 QCOMPARE(o->property("test").toBool(), true);
6758 delete o;
6759}
6760
6761void tst_qqmlecmascript::rewriteMultiLineStrings()
6762{
6763 QQmlEngine engine;
6764 {
6765 // QTBUG-23387
6766 QQmlComponent component(&engine, testFileUrl(fileName: "rewriteMultiLineStrings.qml"));
6767 QObject *o = component.create();
6768 QVERIFY(o != nullptr);
6769 QTRY_COMPARE(o->property("test").toBool(), true);
6770 delete o;
6771 }
6772
6773 {
6774 QQmlComponent component(&engine, testFileUrl(fileName: "rewriteMultiLineStrings_crlf.1.qml"));
6775 QObject *o = component.create();
6776 QVERIFY(o != nullptr);
6777 delete o;
6778 }
6779}
6780
6781void tst_qqmlecmascript::qobjectConnectionListExceptionHandling()
6782{
6783 // QTBUG-23375
6784 QQmlEngine engine;
6785 QQmlComponent component(&engine, testFileUrl(fileName: "qobjectConnectionListExceptionHandling.qml"));
6786 QString warning = component.url().toString() + QLatin1String(":13: TypeError: Cannot read property 'undefined' of undefined");
6787 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
6788 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
6789 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
6790 QObject *o = component.create();
6791 QVERIFY(o != nullptr);
6792 QCOMPARE(o->property("test").toBool(), true);
6793 delete o;
6794}
6795
6796// Reading and writing non-scriptable properties should fail
6797void tst_qqmlecmascript::nonscriptable()
6798{
6799 QQmlEngine engine;
6800 QQmlComponent component(&engine, testFileUrl(fileName: "nonscriptable.qml"));
6801 QObject *o = component.create();
6802 QVERIFY(o != nullptr);
6803 QCOMPARE(o->property("readOk").toBool(), true);
6804 QCOMPARE(o->property("writeOk").toBool(), true);
6805 delete o;
6806}
6807
6808// deleteLater() should not be callable from QML
6809void tst_qqmlecmascript::deleteLater()
6810{
6811 QQmlEngine engine;
6812 QQmlComponent component(&engine, testFileUrl(fileName: "deleteLater.qml"));
6813 QObject *o = component.create();
6814 QVERIFY(o != nullptr);
6815 QCOMPARE(o->property("test").toBool(), true);
6816 delete o;
6817}
6818
6819// objectNameChanged() should be usable from QML
6820void tst_qqmlecmascript::objectNameChangedSignal()
6821{
6822 QQmlEngine engine;
6823 QQmlComponent component(&engine, testFileUrl(fileName: "objectNameChangedSignal.qml"));
6824 QObject *o = component.create();
6825 QVERIFY(o != nullptr);
6826 QCOMPARE(o->property("test").toBool(), false);
6827 o->setObjectName("obj");
6828 QCOMPARE(o->property("test").toBool(), true);
6829 delete o;
6830}
6831
6832// destroyed() should not be usable from QML
6833void tst_qqmlecmascript::destroyedSignal()
6834{
6835 QQmlEngine engine;
6836 QQmlComponent component(&engine, testFileUrl(fileName: "destroyedSignal.qml"));
6837 QVERIFY(component.isError());
6838
6839 QString expectedErrorString = component.url().toString() + QLatin1String(":5:5: Cannot assign to non-existent property \"onDestroyed\"");
6840 QCOMPARE(component.errors().at(0).toString(), expectedErrorString);
6841}
6842
6843void tst_qqmlecmascript::in()
6844{
6845 QQmlEngine engine;
6846 QQmlComponent component(&engine, testFileUrl(fileName: "in.qml"));
6847 QObject *o = component.create();
6848 QVERIFY(o != nullptr);
6849 QCOMPARE(o->property("test1").toBool(), true);
6850 QCOMPARE(o->property("test2").toBool(), true);
6851 delete o;
6852}
6853
6854void tst_qqmlecmascript::typeOf()
6855{
6856 QQmlEngine engine;
6857 QQmlComponent component(&engine, testFileUrl(fileName: "typeOf.qml"));
6858
6859 QObject *o = component.create();
6860 QVERIFY(o != nullptr);
6861
6862 QCOMPARE(o->property("test1").toString(), QLatin1String("undefined"));
6863 QCOMPARE(o->property("test2").toString(), QLatin1String("object"));
6864 QCOMPARE(o->property("test3").toString(), QLatin1String("number"));
6865 QCOMPARE(o->property("test4").toString(), QLatin1String("string"));
6866 QCOMPARE(o->property("test5").toString(), QLatin1String("function"));
6867 QCOMPARE(o->property("test6").toString(), QLatin1String("object"));
6868 QCOMPARE(o->property("test7").toString(), QLatin1String("undefined"));
6869 QCOMPARE(o->property("test8").toString(), QLatin1String("boolean"));
6870 QCOMPARE(o->property("test9").toString(), QLatin1String("object"));
6871
6872 delete o;
6873}
6874
6875void tst_qqmlecmascript::qtbug_24448()
6876{
6877 QQmlEngine engine;
6878 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_24448.qml"));
6879 QScopedPointer<QObject> o(component.create());
6880 QVERIFY(o != nullptr);
6881 QVERIFY(o->property("test").toBool());
6882}
6883
6884void tst_qqmlecmascript::sharedAttachedObject()
6885{
6886 QQmlEngine engine;
6887 QQmlComponent component(&engine, testFileUrl(fileName: "sharedAttachedObject.qml"));
6888 QObject *o = component.create();
6889 QVERIFY(o != nullptr);
6890 QCOMPARE(o->property("test1").toBool(), true);
6891 QCOMPARE(o->property("test2").toBool(), true);
6892 delete o;
6893}
6894
6895// QTBUG-13999
6896void tst_qqmlecmascript::objectName()
6897{
6898 QQmlEngine engine;
6899 QQmlComponent component(&engine, testFileUrl(fileName: "objectName.qml"));
6900 QObject *o = component.create();
6901 QVERIFY(o != nullptr);
6902
6903 QCOMPARE(o->property("test1").toString(), QString("hello"));
6904 QCOMPARE(o->property("test2").toString(), QString("ell"));
6905
6906 o->setObjectName("world");
6907
6908 QCOMPARE(o->property("test1").toString(), QString("world"));
6909 QCOMPARE(o->property("test2").toString(), QString("orl"));
6910
6911 delete o;
6912}
6913
6914void tst_qqmlecmascript::writeRemovesBinding()
6915{
6916 QQmlEngine engine;
6917 QQmlComponent component(&engine, testFileUrl(fileName: "writeRemovesBinding.qml"));
6918 QObject *o = component.create();
6919 QVERIFY(o != nullptr);
6920
6921 QCOMPARE(o->property("test").toBool(), true);
6922
6923 delete o;
6924}
6925
6926// Test bindings assigned to alias properties actually assign to the alias' target
6927void tst_qqmlecmascript::aliasBindingsAssignCorrectly()
6928{
6929 QQmlEngine engine;
6930 QQmlComponent component(&engine, testFileUrl(fileName: "aliasBindingsAssignCorrectly.qml"));
6931 QObject *o = component.create();
6932 QVERIFY(o != nullptr);
6933
6934 QCOMPARE(o->property("test").toBool(), true);
6935
6936 delete o;
6937}
6938
6939// Test bindings assigned to alias properties override a binding on the target (QTBUG-13719)
6940void tst_qqmlecmascript::aliasBindingsOverrideTarget()
6941{
6942 QQmlEngine engine;
6943 {
6944 QQmlComponent component(&engine, testFileUrl(fileName: "aliasBindingsOverrideTarget.qml"));
6945 QObject *o = component.create();
6946 QVERIFY(o != nullptr);
6947
6948 QCOMPARE(o->property("test").toBool(), true);
6949
6950 delete o;
6951 }
6952
6953 {
6954 QQmlComponent component(&engine, testFileUrl(fileName: "aliasBindingsOverrideTarget.2.qml"));
6955 QObject *o = component.create();
6956 QVERIFY(o != nullptr);
6957
6958 QCOMPARE(o->property("test").toBool(), true);
6959
6960 delete o;
6961 }
6962
6963 {
6964 QQmlComponent component(&engine, testFileUrl(fileName: "aliasBindingsOverrideTarget.3.qml"));
6965 QObject *o = component.create();
6966 QVERIFY(o != nullptr);
6967
6968 QCOMPARE(o->property("test").toBool(), true);
6969
6970 delete o;
6971 }
6972}
6973
6974// Test that writes to alias properties override bindings on the alias target (QTBUG-13719)
6975void tst_qqmlecmascript::aliasWritesOverrideBindings()
6976{
6977 QQmlEngine engine;
6978 {
6979 QQmlComponent component(&engine, testFileUrl(fileName: "aliasWritesOverrideBindings.qml"));
6980 QObject *o = component.create();
6981 QVERIFY(o != nullptr);
6982
6983 QCOMPARE(o->property("test").toBool(), true);
6984
6985 delete o;
6986 }
6987
6988 {
6989 QQmlComponent component(&engine, testFileUrl(fileName: "aliasWritesOverrideBindings.2.qml"));
6990 QObject *o = component.create();
6991 QVERIFY(o != nullptr);
6992
6993 QCOMPARE(o->property("test").toBool(), true);
6994
6995 delete o;
6996 }
6997
6998 {
6999 QQmlComponent component(&engine, testFileUrl(fileName: "aliasWritesOverrideBindings.3.qml"));
7000 QObject *o = component.create();
7001 QVERIFY(o != nullptr);
7002
7003 QCOMPARE(o->property("test").toBool(), true);
7004
7005 delete o;
7006 }
7007}
7008
7009// Allow an alais to a composite element
7010// QTBUG-20200
7011void tst_qqmlecmascript::aliasToCompositeElement()
7012{
7013 QQmlEngine engine;
7014 QQmlComponent component(&engine, testFileUrl(fileName: "aliasToCompositeElement.qml"));
7015
7016 QObject *object = component.create();
7017 QVERIFY(object != nullptr);
7018
7019 delete object;
7020}
7021
7022void tst_qqmlecmascript::qtbug_20344()
7023{
7024 QQmlEngine engine;
7025 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_20344.qml"));
7026
7027 QString warning = component.url().toString() + ":5: Error: Exception thrown from within QObject slot";
7028 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
7029
7030 QObject *object = component.create();
7031 QVERIFY(object != nullptr);
7032
7033 delete object;
7034}
7035
7036void tst_qqmlecmascript::revisionErrors()
7037{
7038 QQmlEngine engine;
7039 {
7040 QQmlComponent component(&engine, testFileUrl(fileName: "metaobjectRevisionErrors.qml"));
7041 QString url = component.url().toString();
7042
7043 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
7044 QString warning2 = url + ":11: ReferenceError: prop2 is not defined";
7045 QString warning3 = url + ":13: ReferenceError: method2 is not defined";
7046
7047 QTest::ignoreMessage(type: QtWarningMsg, message: warning1.toLatin1().constData());
7048 QTest::ignoreMessage(type: QtWarningMsg, message: warning2.toLatin1().constData());
7049 QTest::ignoreMessage(type: QtWarningMsg, message: warning3.toLatin1().constData());
7050 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(object: component.create());
7051 QVERIFY(object != nullptr);
7052 delete object;
7053 }
7054 {
7055 QQmlComponent component(&engine, testFileUrl(fileName: "metaobjectRevisionErrors2.qml"));
7056 QString url = component.url().toString();
7057
7058 // MyRevisionedSubclass 1.0 uses MyRevisionedClass revision 0
7059 // method2, prop2 from MyRevisionedClass not available
7060 // method4, prop4 from MyRevisionedSubclass not available
7061 QString warning1 = url + ":8: ReferenceError: prop2 is not defined";
7062 QString warning2 = url + ":14: ReferenceError: prop2 is not defined";
7063 QString warning3 = url + ":10: ReferenceError: prop4 is not defined";
7064 QString warning4 = url + ":16: ReferenceError: prop4 is not defined";
7065 QString warning5 = url + ":20: ReferenceError: method2 is not defined";
7066
7067 QTest::ignoreMessage(type: QtWarningMsg, message: warning1.toLatin1().constData());
7068 QTest::ignoreMessage(type: QtWarningMsg, message: warning2.toLatin1().constData());
7069 QTest::ignoreMessage(type: QtWarningMsg, message: warning3.toLatin1().constData());
7070 QTest::ignoreMessage(type: QtWarningMsg, message: warning4.toLatin1().constData());
7071 QTest::ignoreMessage(type: QtWarningMsg, message: warning5.toLatin1().constData());
7072 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(object: component.create());
7073 QVERIFY(object != nullptr);
7074 delete object;
7075 }
7076 {
7077 QQmlComponent component(&engine, testFileUrl(fileName: "metaobjectRevisionErrors3.qml"));
7078 QString url = component.url().toString();
7079
7080 // MyRevisionedSubclass 1.1 uses MyRevisionedClass revision 1
7081 // All properties/methods available, except MyRevisionedBaseClassUnregistered rev 1
7082 QString warning1 = url + ":30: ReferenceError: methodD is not defined";
7083 QString warning2 = url + ":10: ReferenceError: propD is not defined";
7084 QString warning3 = url + ":20: ReferenceError: propD is not defined";
7085 QTest::ignoreMessage(type: QtWarningMsg, message: warning1.toLatin1().constData());
7086 QTest::ignoreMessage(type: QtWarningMsg, message: warning2.toLatin1().constData());
7087 QTest::ignoreMessage(type: QtWarningMsg, message: warning3.toLatin1().constData());
7088 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(object: component.create());
7089 QVERIFY(object != nullptr);
7090 delete object;
7091 }
7092}
7093
7094void tst_qqmlecmascript::revision()
7095{
7096 QQmlEngine engine;
7097 {
7098 QQmlComponent component(&engine, testFileUrl(fileName: "metaobjectRevision.qml"));
7099 QString url = component.url().toString();
7100
7101 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(object: component.create());
7102 QVERIFY(object != nullptr);
7103 delete object;
7104 }
7105 {
7106 QQmlComponent component(&engine, testFileUrl(fileName: "metaobjectRevision2.qml"));
7107 QString url = component.url().toString();
7108
7109 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(object: component.create());
7110 QVERIFY(object != nullptr);
7111 delete object;
7112 }
7113 {
7114 QQmlComponent component(&engine, testFileUrl(fileName: "metaobjectRevision3.qml"));
7115 QString url = component.url().toString();
7116
7117 MyRevisionedClass *object = qobject_cast<MyRevisionedClass *>(object: component.create());
7118 QVERIFY(object != nullptr);
7119 delete object;
7120 }
7121 // Test that non-root classes can resolve revisioned methods
7122 {
7123 QQmlComponent component(&engine, testFileUrl(fileName: "metaobjectRevision4.qml"));
7124
7125 QObject *object = component.create();
7126 QVERIFY(object != nullptr);
7127 QCOMPARE(object->property("test").toReal(), 11.);
7128 delete object;
7129 }
7130
7131 {
7132 QQmlComponent component(&engine, testFileUrl(fileName: "metaobjectRevision5.qml"));
7133
7134 QObject *object = component.create();
7135 QVERIFY(object != nullptr);
7136 QCOMPARE(object->property("test").toReal(), 11.);
7137 delete object;
7138 }
7139}
7140
7141void tst_qqmlecmascript::realToInt()
7142{
7143 QQmlEngine engine;
7144 QQmlComponent component(&engine, testFileUrl(fileName: "realToInt.qml"));
7145 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7146 QVERIFY(object != nullptr);
7147
7148 QMetaObject::invokeMethod(obj: object.get(), member: "test1");
7149 QCOMPARE(object->value(), int(4));
7150 QMetaObject::invokeMethod(obj: object.get(), member: "test2");
7151 QCOMPARE(object->value(), int(7));
7152}
7153
7154void tst_qqmlecmascript::urlProperty()
7155{
7156 QQmlEngine engine;
7157 {
7158 QQmlComponent component(&engine, testFileUrl(fileName: "urlProperty.1.qml"));
7159 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7160 QVERIFY(object != nullptr);
7161 object->setStringProperty("http://qt-project.org");
7162 QCOMPARE(object->urlProperty(), QUrl("http://qt-project.org/index.html"));
7163 QCOMPARE(object->intProperty(), 123);
7164 QCOMPARE(object->value(), 1);
7165 QCOMPARE(object->property("result").toBool(), true);
7166 }
7167}
7168
7169void tst_qqmlecmascript::urlPropertyWithEncoding()
7170{
7171 QQmlEngine engine;
7172 {
7173 QQmlComponent component(&engine, testFileUrl(fileName: "urlProperty.2.qml"));
7174 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7175 QVERIFY(object != nullptr);
7176 object->setStringProperty("http://qt-project.org");
7177 const QUrl encoded = QUrl::fromEncoded(url: "http://qt-project.org/?get%3cDATA%3e", mode: QUrl::TolerantMode);
7178 QCOMPARE(object->urlProperty(), encoded);
7179 QCOMPARE(object->value(), 0); // Interpreting URL as string yields canonicalised version
7180 QCOMPARE(object->property("result").toBool(), true);
7181 }
7182}
7183
7184void tst_qqmlecmascript::urlListPropertyWithEncoding()
7185{
7186 QQmlEngine engine;
7187 {
7188 QQmlComponent component(&engine, testFileUrl(fileName: "urlListProperty.qml"));
7189 QObject *object = component.create();
7190 QVERIFY(object != nullptr);
7191 MySequenceConversionObject *msco1 = object->findChild<MySequenceConversionObject *>(aName: QLatin1String("msco1"));
7192 MySequenceConversionObject *msco2 = object->findChild<MySequenceConversionObject *>(aName: QLatin1String("msco2"));
7193 MySequenceConversionObject *msco3 = object->findChild<MySequenceConversionObject *>(aName: QLatin1String("msco3"));
7194 MySequenceConversionObject *msco4 = object->findChild<MySequenceConversionObject *>(aName: QLatin1String("msco4"));
7195 QVERIFY(msco1 != nullptr && msco2 != nullptr && msco3 != nullptr && msco4 != nullptr);
7196 const QUrl encoded = QUrl::fromEncoded(url: "http://qt-project.org/?get%3cDATA%3e", mode: QUrl::TolerantMode);
7197 QCOMPARE(msco1->urlListProperty(), (QList<QUrl>() << encoded));
7198 QCOMPARE(msco2->urlListProperty(), (QList<QUrl>() << encoded));
7199 QCOMPARE(msco3->urlListProperty(), (QList<QUrl>() << encoded << encoded));
7200 QCOMPARE(msco4->urlListProperty(), (QList<QUrl>() << encoded << encoded));
7201 delete object;
7202 }
7203}
7204
7205void tst_qqmlecmascript::dynamicString()
7206{
7207 QQmlEngine engine;
7208 QQmlComponent component(&engine, testFileUrl(fileName: "dynamicString.qml"));
7209 QScopedPointer<QObject> object(component.create());
7210 QVERIFY(object != nullptr);
7211 QCOMPARE(object->property("stringProperty").toString(),
7212 QString::fromLatin1("string:Hello World false:0 true:1 uint32:100 int32:-100 double:3.14159 date:2011-02-11 05::30:50!"));
7213}
7214
7215void tst_qqmlecmascript::deleteLaterObjectMethodCall()
7216{
7217 QQmlEngine engine;
7218 QQmlComponent component(&engine, testFileUrl(fileName: "deleteLaterObjectMethodCall.qml"));
7219 QScopedPointer<QObject> object(component.create());
7220 QVERIFY(object != nullptr);
7221}
7222
7223void tst_qqmlecmascript::automaticSemicolon()
7224{
7225 QQmlEngine engine;
7226 QQmlComponent component(&engine, testFileUrl(fileName: "automaticSemicolon.qml"));
7227 QScopedPointer<QObject> object(component.create());
7228 QVERIFY(object != nullptr);
7229}
7230
7231void tst_qqmlecmascript::compatibilitySemicolon()
7232{
7233 QQmlEngine engine;
7234 QQmlComponent component(&engine, testFileUrl(fileName: "compatibilitySemicolon.qml"));
7235 QScopedPointer<QObject> object(component.create());
7236 QVERIFY(object != nullptr);
7237}
7238
7239void tst_qqmlecmascript::incrDecrSemicolon1()
7240{
7241 QQmlEngine engine;
7242 QQmlComponent component(&engine, testFileUrl(fileName: "incrDecrSemicolon1.qml"));
7243 QScopedPointer<QObject> object(component.create());
7244 QVERIFY(object != nullptr);
7245}
7246
7247void tst_qqmlecmascript::incrDecrSemicolon2()
7248{
7249 QQmlEngine engine;
7250 QQmlComponent component(&engine, testFileUrl(fileName: "incrDecrSemicolon2.qml"));
7251 QScopedPointer<QObject> object(component.create());
7252 QVERIFY(object != nullptr);
7253}
7254
7255void tst_qqmlecmascript::incrDecrSemicolon_error1()
7256{
7257 QQmlEngine engine;
7258 QQmlComponent component(&engine, testFileUrl(fileName: "incrDecrSemicolon_error1.qml"));
7259 QObject *object = component.create();
7260 QVERIFY(!object);
7261}
7262
7263void tst_qqmlecmascript::unaryExpression()
7264{
7265 QQmlEngine engine;
7266 QQmlComponent component(&engine, testFileUrl(fileName: "unaryExpression.qml"));
7267 QScopedPointer<QObject> object(component.create());
7268 QVERIFY(object != nullptr);
7269}
7270
7271// Makes sure that a binding isn't double re-evaluated when it depends on the same variable twice
7272void tst_qqmlecmascript::doubleEvaluate()
7273{
7274 QQmlEngine engine;
7275 QQmlComponent component(&engine, testFileUrl(fileName: "doubleEvaluate.qml"));
7276 QObject *object = component.create();
7277 QVERIFY(object != nullptr);
7278 WriteCounter *wc = qobject_cast<WriteCounter *>(object);
7279 QVERIFY(wc != nullptr);
7280 QCOMPARE(wc->count(), 1);
7281
7282 wc->setProperty(name: "x", value: 9);
7283
7284 QCOMPARE(wc->count(), 2);
7285
7286 delete object;
7287}
7288
7289void tst_qqmlecmascript::nonNotifyable()
7290{
7291 QQmlEngine engine;
7292 QQmlComponent component(&engine, testFileUrl(fileName: "nonNotifyable.qml"));
7293
7294 QQmlTestMessageHandler messageHandler;
7295
7296 QObject *object = component.create();
7297
7298 QVERIFY(object != nullptr);
7299
7300 QString expected1 = QLatin1String("QQmlExpression: Expression ") +
7301 component.url().toString() +
7302 QLatin1String(":5:5 depends on non-NOTIFYable properties:");
7303 QString expected2 = QLatin1String(" ") +
7304 QLatin1String(object->metaObject()->className()) +
7305 QLatin1String("::value");
7306
7307 QCOMPARE(messageHandler.messages().length(), 2);
7308 QCOMPARE(messageHandler.messages().at(0), expected1);
7309 QCOMPARE(messageHandler.messages().at(1), expected2);
7310
7311 delete object;
7312}
7313
7314void tst_qqmlecmascript::nonNotifyableConstant()
7315{
7316 QQmlEngine engine;
7317 QQmlComponent component(&engine, testFileUrl(fileName: "nonNotifyableConstant.qml"));
7318 QQmlTestMessageHandler messageHandler;
7319
7320 // Shouldn't produce an error message about non-NOTIFYable properties,
7321 // as the property has the CONSTANT attribute.
7322 QScopedPointer<QObject> object(component.create());
7323 QVERIFY(object);
7324 QCOMPARE(messageHandler.messages().size(), 0);
7325}
7326
7327void tst_qqmlecmascript::forInLoop()
7328{
7329 QQmlEngine engine;
7330 QQmlComponent component(&engine, testFileUrl(fileName: "forInLoop.qml"));
7331 QObject *object = component.create();
7332 QVERIFY(object != nullptr);
7333
7334 QMetaObject::invokeMethod(obj: object, member: "listProperty");
7335
7336 QStringList r = object->property(name: "listResult").toString().split(sep: "|", behavior: Qt::SkipEmptyParts);
7337 QCOMPARE(r.size(), 3);
7338 QCOMPARE(r[0],QLatin1String("0=obj1"));
7339 QCOMPARE(r[1],QLatin1String("1=obj2"));
7340 QCOMPARE(r[2],QLatin1String("2=obj3"));
7341
7342 //TODO: should test for in loop for other objects (such as QObjects) as well.
7343
7344 delete object;
7345}
7346
7347// An object the binding depends on is deleted while the binding is still running
7348void tst_qqmlecmascript::deleteWhileBindingRunning()
7349{
7350 QQmlEngine engine;
7351 QQmlComponent component(&engine, testFileUrl(fileName: "deleteWhileBindingRunning.qml"));
7352 QObject *object = component.create();
7353 QVERIFY(object != nullptr);
7354 delete object;
7355}
7356
7357void tst_qqmlecmascript::qtbug_22679()
7358{
7359 MyQmlObject object;
7360 object.setStringProperty(QLatin1String("Please work correctly"));
7361 QQmlEngine engine;
7362 engine.rootContext()->setContextProperty("contextProp", &object);
7363
7364 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_22679.qml"));
7365 qRegisterMetaType<QList<QQmlError> >(typeName: "QList<QQmlError>");
7366 QSignalSpy warningsSpy(&engine, SIGNAL(warnings(QList<QQmlError>)));
7367
7368 QObject *o = component.create();
7369 QVERIFY(o != nullptr);
7370 QCOMPARE(warningsSpy.count(), 0);
7371 delete o;
7372}
7373
7374void tst_qqmlecmascript::qtbug_22843_data()
7375{
7376 QTest::addColumn<bool>(name: "library");
7377
7378 QTest::newRow(dataTag: "without .pragma library") << false;
7379 QTest::newRow(dataTag: "with .pragma library") << true;
7380}
7381
7382void tst_qqmlecmascript::qtbug_22843()
7383{
7384 QFETCH(bool, library);
7385
7386 QString fileName("qtbug_22843");
7387 if (library)
7388 fileName += QLatin1String(".library");
7389 fileName += QLatin1String(".qml");
7390
7391 QQmlEngine engine;
7392 QQmlComponent component(&engine, testFileUrl(fileName));
7393
7394 QString url = component.url().toString();
7395 QString expectedError = url.left(n: url.length()-3) + QLatin1String("js:4:16: Expected token `;'");
7396
7397 QVERIFY(component.isError());
7398 QCOMPARE(component.errors().value(1).toString(), expectedError);
7399}
7400
7401
7402void tst_qqmlecmascript::switchStatement()
7403{
7404 QQmlEngine engine;
7405 {
7406 QQmlComponent component(&engine, testFileUrl(fileName: "switchStatement.1.qml"));
7407 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7408 QVERIFY(object != nullptr);
7409
7410 // `object->value()' is the number of executed statements
7411
7412 object->setStringProperty("A");
7413 QCOMPARE(object->value(), 5);
7414
7415 object->setStringProperty("S");
7416 QCOMPARE(object->value(), 3);
7417
7418 object->setStringProperty("D");
7419 QCOMPARE(object->value(), 3);
7420
7421 object->setStringProperty("F");
7422 QCOMPARE(object->value(), 4);
7423
7424 object->setStringProperty("something else");
7425 QCOMPARE(object->value(), 1);
7426 }
7427
7428 {
7429 QQmlComponent component(&engine, testFileUrl(fileName: "switchStatement.2.qml"));
7430 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7431 QVERIFY(object != nullptr);
7432
7433 // `object->value()' is the number of executed statements
7434
7435 object->setStringProperty("A");
7436 QCOMPARE(object->value(), 5);
7437
7438 object->setStringProperty("S");
7439 QCOMPARE(object->value(), 3);
7440
7441 object->setStringProperty("D");
7442 QCOMPARE(object->value(), 3);
7443
7444 object->setStringProperty("F");
7445 QCOMPARE(object->value(), 3);
7446
7447 object->setStringProperty("something else");
7448 QCOMPARE(object->value(), 4);
7449 }
7450
7451 {
7452 QQmlComponent component(&engine, testFileUrl(fileName: "switchStatement.3.qml"));
7453 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7454 QVERIFY(object != nullptr);
7455
7456 // `object->value()' is the number of executed statements
7457
7458 object->setStringProperty("A");
7459 QCOMPARE(object->value(), 5);
7460
7461 object->setStringProperty("S");
7462 QCOMPARE(object->value(), 3);
7463
7464 object->setStringProperty("D");
7465 QCOMPARE(object->value(), 3);
7466
7467 object->setStringProperty("F");
7468 QCOMPARE(object->value(), 3);
7469
7470 object->setStringProperty("something else");
7471 QCOMPARE(object->value(), 6);
7472 }
7473
7474 {
7475 QQmlComponent component(&engine, testFileUrl(fileName: "switchStatement.4.qml"));
7476
7477 QString warning = component.url().toString() + ":4:5: Unable to assign [undefined] to int";
7478 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
7479
7480 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7481 QVERIFY(object != nullptr);
7482
7483 // `object->value()' is the number of executed statements
7484
7485 object->setStringProperty("A");
7486 QCOMPARE(object->value(), 5);
7487
7488 object->setStringProperty("S");
7489 QCOMPARE(object->value(), 3);
7490
7491 object->setStringProperty("D");
7492 QCOMPARE(object->value(), 3);
7493
7494 object->setStringProperty("F");
7495 QCOMPARE(object->value(), 3);
7496
7497 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
7498
7499 object->setStringProperty("something else");
7500 }
7501
7502 {
7503 QQmlComponent component(&engine, testFileUrl(fileName: "switchStatement.5.qml"));
7504 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7505 QVERIFY(object != nullptr);
7506
7507 // `object->value()' is the number of executed statements
7508
7509 object->setStringProperty("A");
7510 QCOMPARE(object->value(), 1);
7511
7512 object->setStringProperty("S");
7513 QCOMPARE(object->value(), 1);
7514
7515 object->setStringProperty("D");
7516 QCOMPARE(object->value(), 1);
7517
7518 object->setStringProperty("F");
7519 QCOMPARE(object->value(), 1);
7520
7521 object->setStringProperty("something else");
7522 QCOMPARE(object->value(), 1);
7523 }
7524
7525 {
7526 QQmlComponent component(&engine, testFileUrl(fileName: "switchStatement.6.qml"));
7527 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7528 QVERIFY(object != nullptr);
7529
7530 // `object->value()' is the number of executed statements
7531
7532 object->setStringProperty("A");
7533 QCOMPARE(object->value(), 123);
7534
7535 object->setStringProperty("S");
7536 QCOMPARE(object->value(), 123);
7537
7538 object->setStringProperty("D");
7539 QCOMPARE(object->value(), 321);
7540
7541 object->setStringProperty("F");
7542 QCOMPARE(object->value(), 321);
7543
7544 object->setStringProperty("something else");
7545 QCOMPARE(object->value(), 0);
7546 }
7547}
7548
7549void tst_qqmlecmascript::withStatement()
7550{
7551 QQmlEngine engine;
7552 {
7553 QUrl url = testFileUrl(fileName: "withStatement.1.qml");
7554 QQmlComponent component(&engine, url);
7555 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7556 QVERIFY(object != nullptr);
7557
7558 QCOMPARE(object->value(), 123);
7559 }
7560}
7561
7562void tst_qqmlecmascript::tryStatement()
7563{
7564 QQmlEngine engine;
7565 {
7566 QQmlComponent component(&engine, testFileUrl(fileName: "tryStatement.1.qml"));
7567 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7568 QVERIFY(object != nullptr);
7569
7570 QCOMPARE(object->value(), 123);
7571 }
7572
7573 {
7574 QQmlComponent component(&engine, testFileUrl(fileName: "tryStatement.2.qml"));
7575 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7576 QVERIFY(object != nullptr);
7577
7578 QCOMPARE(object->value(), 321);
7579 }
7580
7581 {
7582 QQmlComponent component(&engine, testFileUrl(fileName: "tryStatement.3.qml"));
7583 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7584 QVERIFY(object != nullptr);
7585
7586 QVERIFY(object->qjsvalue().isUndefined());
7587 }
7588
7589 {
7590 QQmlComponent component(&engine, testFileUrl(fileName: "tryStatement.4.qml"));
7591 QScopedPointer<MyQmlObject> object(qobject_cast<MyQmlObject*>(object: component.create()));
7592 QVERIFY(object != nullptr);
7593
7594 QVERIFY(object->qjsvalue().isUndefined());
7595 }
7596}
7597
7598class CppInvokableWithQObjectDerived : public QObject
7599{
7600 Q_OBJECT
7601public:
7602 CppInvokableWithQObjectDerived() {}
7603 ~CppInvokableWithQObjectDerived() {}
7604
7605 Q_INVOKABLE MyQmlObject *createMyQmlObject(QString data)
7606 {
7607 MyQmlObject *obj = new MyQmlObject();
7608 obj->setStringProperty(data);
7609 return obj;
7610 }
7611
7612 Q_INVOKABLE QString getStringProperty(MyQmlObject *obj)
7613 {
7614 return obj->stringProperty();
7615 }
7616};
7617
7618void tst_qqmlecmascript::invokableWithQObjectDerived()
7619{
7620 CppInvokableWithQObjectDerived invokable;
7621
7622 {
7623 QQmlEngine engine;
7624 engine.rootContext()->setContextProperty("invokable", &invokable);
7625
7626 QQmlComponent component(&engine, testFileUrl(fileName: "qobjectDerivedArgument.qml"));
7627
7628 QObject *object = component.create();
7629
7630 QVERIFY(object != nullptr);
7631 QVERIFY(object->property("result").value<bool>());
7632
7633 delete object;
7634 }
7635}
7636
7637void tst_qqmlecmascript::realTypePrecision()
7638{
7639 // Properties and signal parameters of type real should have double precision.
7640 QQmlEngine engine;
7641 QQmlComponent component(&engine, testFileUrl(fileName: "realTypePrecision.qml"));
7642 QScopedPointer<QObject> object(component.create());
7643 QVERIFY(object != nullptr);
7644 QCOMPARE(object->property("test").toDouble(), 1234567890.);
7645 QCOMPARE(object->property("test2").toDouble(), 1234567890.);
7646 QCOMPARE(object->property("test3").toDouble(), 1234567890.);
7647 QCOMPARE(object->property("test4").toDouble(), 1234567890.);
7648 QCOMPARE(object->property("test5").toDouble(), 1234567890.);
7649 QCOMPARE(object->property("test6").toDouble(), 1234567890.*2);
7650}
7651
7652void tst_qqmlecmascript::registeredFlagMethod()
7653{
7654 QQmlEngine engine;
7655 QQmlComponent component(&engine, testFileUrl(fileName: "registeredFlagMethod.qml"));
7656 MyQmlObject *object = qobject_cast<MyQmlObject *>(object: component.create());
7657 QVERIFY(object != nullptr);
7658
7659 QCOMPARE(object->buttons(), 0);
7660 emit object->basicSignal();
7661 QCOMPARE(object->buttons(), Qt::RightButton);
7662
7663 delete object;
7664}
7665
7666// QTBUG-23138
7667void tst_qqmlecmascript::replaceBinding()
7668{
7669 QQmlEngine engine;
7670 QQmlComponent c(&engine, testFileUrl(fileName: "replaceBinding.qml"));
7671 QObject *obj = c.create();
7672 QVERIFY(obj != nullptr);
7673
7674 QVERIFY(obj->property("success").toBool());
7675 delete obj;
7676}
7677
7678void tst_qqmlecmascript::bindingBoundFunctions()
7679{
7680 QQmlEngine engine;
7681 QQmlComponent c(&engine, testFileUrl(fileName: "bindingBoundFunctions.qml"));
7682 QObject *obj = c.create();
7683 QVERIFY(obj != nullptr);
7684
7685 QVERIFY(obj->property("success").toBool());
7686 delete obj;
7687}
7688
7689void tst_qqmlecmascript::deleteRootObjectInCreation()
7690{
7691 QQmlEngine engine;
7692 {
7693 QQmlComponent c(&engine, testFileUrl(fileName: "deleteRootObjectInCreation.qml"));
7694 QObject *obj = c.create();
7695 QVERIFY(obj != nullptr);
7696 QVERIFY(obj->property("rootIndestructible").toBool());
7697 QVERIFY(!obj->property("childDestructible").toBool());
7698 QTest::qWait(ms: 1);
7699 QVERIFY(obj->property("childDestructible").toBool());
7700 delete obj;
7701 }
7702
7703 {
7704 QQmlComponent c(&engine, testFileUrl(fileName: "deleteRootObjectInCreation.2.qml"));
7705 QObject *object = c.create();
7706 QVERIFY(object != nullptr);
7707 QVERIFY(object->property("testConditionsMet").toBool());
7708 delete object;
7709 }
7710}
7711
7712void tst_qqmlecmascript::onDestruction()
7713{
7714 {
7715 // Delete object manually to invoke the associated handlers,
7716 // prior to engine destruction.
7717 QQmlEngine engine;
7718 QQmlComponent c(&engine, testFileUrl(fileName: "onDestruction.qml"));
7719 QObject *obj = c.create();
7720 QVERIFY(obj != nullptr);
7721 delete obj;
7722 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
7723 }
7724
7725 {
7726 // In this case, the teardown of the engine causes deletion
7727 // of contexts and child items. This triggers the
7728 // onDestruction handler of a (previously .destroy()ed)
7729 // component instance. This shouldn't crash.
7730 QQmlEngine engine;
7731 QQmlComponent c(&engine, testFileUrl(fileName: "onDestruction.qml"));
7732 QScopedPointer<QObject> obj(c.create());
7733 QVERIFY(obj != nullptr);
7734 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
7735 }
7736}
7737
7738class WeakReferenceMutator : public QObject
7739{
7740 Q_OBJECT
7741public:
7742 WeakReferenceMutator()
7743 : resultPtr(nullptr)
7744 , weakRef(nullptr)
7745 {}
7746
7747 void init(QV4::ExecutionEngine *v4, QV4::WeakValue *weakRef, bool *resultPtr)
7748 {
7749 QV4::QObjectWrapper::wrap(engine: v4, object: this);
7750 QQmlEngine::setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
7751
7752 this->resultPtr = resultPtr;
7753 this->weakRef = weakRef;
7754
7755 QObject::connect(sender: QQmlComponent::qmlAttachedProperties(this), signal: &QQmlComponentAttached::destruction, receiver: this, slot: &WeakReferenceMutator::reviveFirstWeakReference);
7756 }
7757
7758private slots:
7759 void reviveFirstWeakReference() {
7760 // weakRef is not required to be undefined here. The gc can clear it later.
7761 *resultPtr = weakRef->valueRef();
7762 if (!*resultPtr)
7763 return;
7764 QV4::ExecutionEngine *v4 = qmlEngine(this)->handle();
7765 weakRef->set(engine: v4, obj: v4->newObject());
7766 *resultPtr = weakRef->valueRef() && !weakRef->isNullOrUndefined();
7767 }
7768
7769public:
7770 bool *resultPtr;
7771
7772 QV4::WeakValue *weakRef;
7773};
7774
7775QT_BEGIN_NAMESPACE
7776
7777namespace QV4 {
7778
7779namespace Heap {
7780struct WeakReferenceSentinel : public Object {
7781 void init(WeakValue *weakRef, bool *resultPtr)
7782 {
7783 Object::init();
7784 this->weakRef = weakRef;
7785 this->resultPtr = resultPtr;
7786 }
7787
7788 void destroy() {
7789 *resultPtr = weakRef->isNullOrUndefined();
7790 Object::destroy();
7791 }
7792
7793 WeakValue *weakRef;
7794 bool *resultPtr;
7795};
7796} // namespace Heap
7797
7798struct WeakReferenceSentinel : public Object {
7799 V4_OBJECT2(WeakReferenceSentinel, Object)
7800 V4_NEEDS_DESTROY
7801};
7802
7803} // namespace QV4
7804
7805QT_END_NAMESPACE
7806
7807DEFINE_OBJECT_VTABLE(QV4::WeakReferenceSentinel);
7808
7809void tst_qqmlecmascript::onDestructionViaGC()
7810{
7811 qmlRegisterType<WeakReferenceMutator>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "WeakReferenceMutator");
7812
7813 QQmlEngine engine;
7814 QV4::ExecutionEngine *v4 =engine.handle();
7815
7816 QQmlComponent component(&engine, testFileUrl(fileName: "DestructionHelper.qml"));
7817
7818 QScopedPointer<QV4::WeakValue> weakRef;
7819
7820 bool mutatorResult = false;
7821 bool sentinelResult = false;
7822
7823 {
7824 weakRef.reset(other: new QV4::WeakValue);
7825 weakRef->set(engine: v4, obj: v4->newObject());
7826 QVERIFY(!weakRef->isNullOrUndefined());
7827
7828 QPointer<WeakReferenceMutator> weakReferenceMutator = qobject_cast<WeakReferenceMutator *>(object: component.create());
7829 QVERIFY2(!weakReferenceMutator.isNull(), qPrintable(component.errorString()));
7830 weakReferenceMutator->init(v4, weakRef: weakRef.data(), resultPtr: &mutatorResult);
7831
7832 v4->memoryManager->allocate<QV4::WeakReferenceSentinel>(args: weakRef.data(), args: &sentinelResult);
7833 }
7834 gc(engine);
7835
7836 QVERIFY2(mutatorResult, "We failed to re-assign the weak reference a new value during GC");
7837 QVERIFY2(sentinelResult, "The weak reference was not cleared properly");
7838}
7839
7840struct EventProcessor : public QObject
7841{
7842 Q_OBJECT
7843public:
7844 Q_INVOKABLE void process()
7845 {
7846 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
7847 QCoreApplication::processEvents();
7848 }
7849};
7850
7851void tst_qqmlecmascript::bindingSuppression()
7852{
7853 QQmlEngine engine;
7854 EventProcessor processor;
7855 engine.rootContext()->setContextProperty("pendingEvents", &processor);
7856
7857 QQmlTestMessageHandler messageHandler;
7858
7859 QQmlComponent c(&engine, testFileUrl(fileName: "bindingSuppression.qml"));
7860 QObject *obj = c.create();
7861 QVERIFY(obj != nullptr);
7862 delete obj;
7863
7864 QVERIFY2(messageHandler.messages().isEmpty(), qPrintable(messageHandler.messageString()));
7865}
7866
7867void tst_qqmlecmascript::signalEmitted()
7868{
7869 {
7870 // calling destroy on the sibling.
7871 QQmlEngine engine;
7872 QQmlComponent c(&engine, testFileUrl(fileName: "signalEmitted.2.qml"));
7873 QObject *obj = c.create();
7874 QVERIFY(obj != nullptr);
7875 QTRY_VERIFY(obj->property("success").toBool());
7876 delete obj;
7877 }
7878
7879 {
7880 // allowing gc to clean up the sibling.
7881 QQmlEngine engine;
7882 QQmlComponent c(&engine, testFileUrl(fileName: "signalEmitted.3.qml"));
7883 QObject *obj = c.create();
7884 QVERIFY(obj != nullptr);
7885 gc(engine); // should collect c1.
7886 QTRY_VERIFY(obj->property("success").toBool());
7887 delete obj;
7888 }
7889
7890 {
7891 // allowing gc to clean up the sibling after manually destroying target.
7892 QQmlEngine engine;
7893 QQmlComponent c(&engine, testFileUrl(fileName: "signalEmitted.4.qml"));
7894 QObject *obj = c.create();
7895 QVERIFY(obj != nullptr);
7896 gc(engine); // should collect c1.
7897 QMetaObject::invokeMethod(obj, member: "destroyC2");
7898 QTRY_VERIFY(obj->property("success").toBool()); // handles events (incl. delete later).
7899 delete obj;
7900 }
7901}
7902
7903// QTBUG-25647
7904void tst_qqmlecmascript::threadSignal()
7905{
7906 QQmlEngine engine;
7907 {
7908 QQmlComponent c(&engine, testFileUrl(fileName: "threadSignal.qml"));
7909 QScopedPointer<QObject> object(c.create());
7910 QVERIFY(!object.isNull());
7911 QTRY_VERIFY(object->property("passed").toBool());
7912 }
7913 {
7914 QQmlComponent c(&engine, testFileUrl(fileName: "threadSignal.2.qml"));
7915 QScopedPointer<QObject> object(c.create());
7916 QVERIFY(!object.isNull());
7917 QMetaObject::invokeMethod(obj: object.data(), member: "doIt");
7918 QTRY_VERIFY(object->property("passed").toBool());
7919 }
7920}
7921
7922// ensure that the qqmldata::destroyed() handler doesn't cause problems
7923void tst_qqmlecmascript::qqmldataDestroyed()
7924{
7925 QQmlEngine engine;
7926 // gc cleans up a qobject, later the qqmldata destroyed handler will run.
7927 {
7928 QQmlComponent c(&engine, testFileUrl(fileName: "qqmldataDestroyed.qml"));
7929 QObject *object = c.create();
7930 QVERIFY(object != nullptr);
7931 // now gc causing the collection of the dynamically constructed object.
7932 engine.collectGarbage();
7933 engine.collectGarbage();
7934 // now process events to allow deletion (calling qqmldata::destroyed())
7935 QCoreApplication::sendPostedEvents(receiver: nullptr, event_type: QEvent::DeferredDelete);
7936 QCoreApplication::processEvents();
7937 // shouldn't crash.
7938 delete object;
7939 }
7940
7941 // in this case, the object has CPP ownership, and the gc will
7942 // be triggered during its beginCreate stage.
7943 {
7944 QQmlComponent c(&engine, testFileUrl(fileName: "qqmldataDestroyed.2.qml"));
7945 QObject *object = c.create();
7946 QVERIFY(object != nullptr);
7947 QVERIFY(object->property("testConditionsMet").toBool());
7948 // the gc() within the handler will have triggered the weak
7949 // qobject reference callback. If that incorrectly disposes
7950 // the handle, when the qqmldata::destroyed() handler is
7951 // called due to object deletion we will see a crash.
7952 delete object;
7953 // shouldn't have crashed.
7954 }
7955}
7956
7957void tst_qqmlecmascript::secondAlias()
7958{
7959 QQmlEngine engine;
7960 QQmlComponent c(&engine, testFileUrl(fileName: "secondAlias.qml"));
7961 QObject *object = c.create();
7962 QVERIFY(object != nullptr);
7963 QCOMPARE(object->property("test").toInt(), 200);
7964 delete object;
7965}
7966
7967// An alias to a var property works
7968void tst_qqmlecmascript::varAlias()
7969{
7970 QQmlEngine engine;
7971 QQmlComponent c(&engine, testFileUrl(fileName: "varAlias.qml"));
7972 QObject *object = c.create();
7973 QVERIFY(object != nullptr);
7974 QCOMPARE(object->property("test").toInt(), 192);
7975 delete object;
7976}
7977
7978// Used to trigger an assert in the lazy meta object creation stage
7979void tst_qqmlecmascript::overrideDataAssert()
7980{
7981 QQmlEngine engine;
7982 QQmlComponent c(&engine, testFileUrl(fileName: "overrideDataAssert.qml"));
7983 QObject *object = c.create();
7984 QVERIFY(object != nullptr);
7985 object->metaObject();
7986 delete object;
7987}
7988
7989void tst_qqmlecmascript::fallbackBindings_data()
7990{
7991 QTest::addColumn<QString>(name: "source");
7992
7993 QTest::newRow(dataTag: "Property without fallback") << "fallbackBindings.1.qml";
7994 QTest::newRow(dataTag: "Property fallback") << "fallbackBindings.2.qml";
7995 QTest::newRow(dataTag: "SingletonType without fallback") << "fallbackBindings.3.qml";
7996 QTest::newRow(dataTag: "SingletonType fallback") << "fallbackBindings.4.qml";
7997 QTest::newRow(dataTag: "Attached without fallback") << "fallbackBindings.5.qml";
7998 QTest::newRow(dataTag: "Attached fallback") << "fallbackBindings.6.qml";
7999 QTest::newRow(dataTag: "Subproperty without fallback") << "fallbackBindings.7.qml";
8000 QTest::newRow(dataTag: "Subproperty fallback") << "fallbackBindings.8.qml";
8001}
8002
8003void tst_qqmlecmascript::fallbackBindings()
8004{
8005 QFETCH(QString, source);
8006
8007 QQmlEngine engine;
8008 QQmlComponent component(&engine, testFileUrl(fileName: source));
8009 QScopedPointer<QObject> object(component.create());
8010 QVERIFY(object != nullptr);
8011
8012 QCOMPARE(object->property("success").toBool(), true);
8013}
8014
8015void tst_qqmlecmascript::propertyOverride()
8016{
8017 QQmlEngine engine;
8018 QQmlComponent component(&engine, testFileUrl(fileName: "propertyOverride.qml"));
8019 QScopedPointer<QObject> object(component.create());
8020 QVERIFY(object != nullptr);
8021
8022 QCOMPARE(object->property("success").toBool(), true);
8023}
8024
8025void tst_qqmlecmascript::sequenceSort_data()
8026{
8027 QTest::addColumn<QString>(name: "function");
8028 QTest::addColumn<bool>(name: "useComparer");
8029
8030 QTest::newRow(dataTag: "qtbug_25269") << "test_qtbug_25269" << false;
8031
8032 const char *types[] = { "alphabet", "numbers", "reals", "number_vector", "real_vector" };
8033 const char *sort[] = { "insertionSort", "quickSort" };
8034
8035 for (size_t t=0 ; t < sizeof(types)/sizeof(types[0]) ; ++t) {
8036 for (size_t s=0 ; s < sizeof(sort)/sizeof(sort[0]) ; ++s) {
8037 for (int c=0 ; c < 2 ; ++c) {
8038 QString testName = QLatin1String(types[t]) + QLatin1Char('_') + QLatin1String(sort[s]);
8039 QString fnName = QLatin1String("test_") + testName;
8040 bool useComparer = c != 0;
8041 testName += useComparer ? QLatin1String("[custom]") : QLatin1String("[default]");
8042 QTest::newRow(dataTag: testName.toLatin1().constData()) << fnName << useComparer;
8043 }
8044 }
8045 }
8046}
8047
8048void tst_qqmlecmascript::sequenceSort()
8049{
8050 QFETCH(QString, function);
8051 QFETCH(bool, useComparer);
8052
8053 QQmlEngine engine;
8054 QQmlComponent component(&engine, testFileUrl(fileName: "sequenceSort.qml"));
8055
8056 QObject *object = component.create();
8057 if (object == nullptr)
8058 qDebug() << component.errorString();
8059 QVERIFY(object != nullptr);
8060
8061 QVariant q;
8062 QMetaObject::invokeMethod(obj: object, member: function.toLatin1().constData(), Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, useComparer));
8063 QVERIFY(q.toBool());
8064
8065 delete object;
8066}
8067
8068void tst_qqmlecmascript::dateParse()
8069{
8070 QQmlEngine engine;
8071 QQmlComponent component(&engine, testFileUrl(fileName: "date.qml"));
8072
8073 QScopedPointer<QObject> object(component.create());
8074 if (object == nullptr)
8075 qDebug() << component.errorString();
8076 QVERIFY(object != nullptr);
8077
8078 QVariant q;
8079 QMetaObject::invokeMethod(obj: object.get(), member: "test_is_invalid_jsDateTime", Q_RETURN_ARG(QVariant, q));
8080 QVERIFY(q.toBool());
8081
8082 QMetaObject::invokeMethod(obj: object.get(), member: "test_is_invalid_qtDateTime", Q_RETURN_ARG(QVariant, q));
8083 QVERIFY(q.toBool());
8084
8085 QMetaObject::invokeMethod(obj: object.get(), member: "test_rfc2822_date", Q_RETURN_ARG(QVariant, q));
8086 QCOMPARE(q.toLongLong(), 1379512851000LL);
8087}
8088
8089void tst_qqmlecmascript::utcDate()
8090{
8091 QQmlEngine engine;
8092 QQmlComponent component(&engine, testFileUrl(fileName: "utcdate.qml"));
8093
8094 QScopedPointer<QObject> object(component.create());
8095 if (object == nullptr)
8096 qDebug() << component.errorString();
8097 QVERIFY(object != nullptr);
8098
8099 QVariant q;
8100 QVariant val = QString::fromLatin1(str: "2014-07-16T23:30:31");
8101 QMetaObject::invokeMethod(obj: object.get(), member: "check_utc", Q_RETURN_ARG(QVariant, q), Q_ARG(QVariant, val));
8102 QVERIFY(q.toBool());
8103}
8104
8105void tst_qqmlecmascript::negativeYear()
8106{
8107 QQmlEngine engine;
8108 QQmlComponent component(&engine, testFileUrl(fileName: "negativeyear.qml"));
8109
8110 QScopedPointer<QObject> object(component.create());
8111 if (object == nullptr)
8112 qDebug() << component.errorString();
8113 QVERIFY(object != nullptr);
8114
8115 QVariant q;
8116 QMetaObject::invokeMethod(obj: object.get(), member: "check_negative_tostring", Q_RETURN_ARG(QVariant, q));
8117
8118 // Only check for the year. We hope that every language writes the year in arabic numerals and
8119 // in relation to a specific dude's date of birth. We also hope that no language adds a "-2001"
8120 // junk string somewhere in the middle.
8121 QVERIFY(q.toString().indexOf(QStringLiteral("-2001")) != -1);
8122
8123 QMetaObject::invokeMethod(obj: object.get(), member: "check_negative_toisostring", Q_RETURN_ARG(QVariant, q));
8124 QCOMPARE(q.toString().left(16), QStringLiteral("result: -002000-"));
8125}
8126
8127void tst_qqmlecmascript::concatenatedStringPropertyAccess()
8128{
8129 QQmlEngine engine;
8130 QQmlComponent component(&engine, testFileUrl(fileName: "concatenatedStringPropertyAccess.qml"));
8131 QObject *object = component.create();
8132 QVERIFY(object);
8133 QVERIFY(object->property("success").toBool());
8134 delete object;
8135}
8136
8137void tst_qqmlecmascript::jsOwnedObjectsDeletedOnEngineDestroy()
8138{
8139 QQmlEngine *myEngine = new QQmlEngine;
8140
8141 MyDeleteObject deleteObject;
8142 deleteObject.setObjectName("deleteObject");
8143 QObject * const object1 = new QObject;
8144 QObject * const object2 = new QObject;
8145 object1->setObjectName("object1");
8146 object2->setObjectName("object2");
8147 deleteObject.setObject1(object1);
8148 deleteObject.setObject2(object2);
8149
8150 // Objects returned by function calls get marked as destructible, but objects returned by
8151 // property getters do not - therefore we explicitly set the object as destructible.
8152 QQmlEngine::setObjectOwnership(object2, QQmlEngine::JavaScriptOwnership);
8153
8154 myEngine->rootContext()->setContextProperty("deleteObject", &deleteObject);
8155 QQmlComponent component(myEngine, testFileUrl(fileName: "jsOwnedObjectsDeletedOnEngineDestroy.qml"));
8156 QObject *object = component.create();
8157 QVERIFY(object);
8158
8159 // Destroying the engine should delete all JS owned QObjects
8160 QSignalSpy spy1(object1, SIGNAL(destroyed()));
8161 QSignalSpy spy2(object2, SIGNAL(destroyed()));
8162 delete myEngine;
8163 QCOMPARE(spy1.count(), 1);
8164 QCOMPARE(spy2.count(), 1);
8165
8166 deleteObject.deleteNestedObject();
8167 delete object;
8168}
8169
8170void tst_qqmlecmascript::updateCall()
8171{
8172 // update is a slot on QQuickItem. Even though it's not
8173 // documented it can be called from within QML. Make sure
8174 // we don't crash when calling it.
8175 QString file("updateCall.qml");
8176 QQmlEngine engine;
8177 QQmlComponent component(&engine, testFileUrl(fileName: file));
8178 QScopedPointer<QObject> object(component.create());
8179 QVERIFY(object != nullptr);
8180}
8181
8182void tst_qqmlecmascript::numberParsing()
8183{
8184 QQmlEngine engine;
8185 for (int i = 1; i < 8; ++i) {
8186 QString file("numberParsing.%1.qml");
8187 file = file.arg(a: i);
8188 QQmlComponent component(&engine, testFileUrl(fileName: file));
8189 QScopedPointer<QObject> object(component.create());
8190 QVERIFY(object != nullptr);
8191 }
8192 for (int i = 1; i < 3; ++i) {
8193 QString file("numberParsing_error.%1.qml");
8194 file = file.arg(a: i);
8195 QQmlComponent component(&engine, testFileUrl(fileName: file));
8196 QVERIFY(!component.errors().isEmpty());
8197 }
8198}
8199
8200void tst_qqmlecmascript::stringParsing()
8201{
8202 QQmlEngine engine;
8203 for (int i = 1; i < 7; ++i) {
8204 QString file("stringParsing_error.%1.qml");
8205 file = file.arg(a: i);
8206 QQmlComponent component(&engine, testFileUrl(fileName: file));
8207 QObject *object = component.create();
8208 QVERIFY(!object);
8209 }
8210}
8211
8212void tst_qqmlecmascript::push_and_shift()
8213{
8214 QJSEngine e;
8215 const QString program =
8216 "var array = []; "
8217 "for (var i = 0; i < 10000; i++) {"
8218 " array.push(5); array.unshift(5); array.push(5);"
8219 "}"
8220 "array.length;";
8221 QCOMPARE(e.evaluate(program).toNumber(), double(30000));
8222}
8223
8224void tst_qqmlecmascript::qtbug_32801()
8225{
8226 QQmlEngine engine;
8227 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_32801.qml"));
8228
8229 QScopedPointer<QObject> obj(component.create());
8230 QVERIFY(obj != nullptr);
8231
8232 // do not crash when a QML signal is connected to a non-void slot
8233 connect(sender: obj.data(), SIGNAL(testSignal(QString)), receiver: obj.data(), SLOT(slotWithReturnValue(QString)));
8234 QVERIFY(QMetaObject::invokeMethod(obj.data(), "emitTestSignal"));
8235}
8236
8237void tst_qqmlecmascript::thisObject()
8238{
8239 QQmlEngine engine;
8240 QQmlComponent component(&engine, testFileUrl(fileName: "thisObject.qml"));
8241 QObject *object = component.create();
8242 QVERIFY(object);
8243 QCOMPARE(qvariant_cast<QObject*>(object->property("subObject"))->property("test").toInt(), 2);
8244 delete object;
8245}
8246
8247void tst_qqmlecmascript::qtbug_33754()
8248{
8249 QQmlEngine engine;
8250 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_33754.qml"));
8251
8252 QScopedPointer<QObject> obj(component.create());
8253 QVERIFY(obj != nullptr);
8254}
8255
8256void tst_qqmlecmascript::qtbug_34493()
8257{
8258 QQmlEngine engine;
8259 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_34493.qml"));
8260
8261 QScopedPointer<QObject> obj(component.create());
8262 if (component.errors().size())
8263 qDebug() << component.errors();
8264 QVERIFY(component.errors().isEmpty());
8265 QVERIFY(obj != nullptr);
8266 QVERIFY(QMetaObject::invokeMethod(obj.data(), "doIt"));
8267 QTRY_VERIFY(obj->property("prop").toString() == QLatin1String("Hello World!"));
8268}
8269
8270// Check that a Singleton can be passed from QML to C++
8271// as its type*, it's parent type* and as QObject*
8272void tst_qqmlecmascript::singletonFromQMLToCpp()
8273{
8274 QQmlEngine engine;
8275 QQmlComponent component(&engine, testFile(fileName: "singletonTest.qml"));
8276 QScopedPointer<QObject> obj(component.create());
8277 if (component.errors().size())
8278 qDebug() << component.errors();
8279 QVERIFY(component.errors().isEmpty());
8280 QVERIFY(obj != nullptr);
8281
8282 QCOMPARE(obj->property("qobjectTest"), QVariant(true));
8283 QCOMPARE(obj->property("myQmlObjectTest"), QVariant(true));
8284 QCOMPARE(obj->property("myInheritedQmlObjectTest"), QVariant(true));
8285}
8286
8287// Check that a Singleton can be passed from QML to C++
8288// as its type*, it's parent type* and as QObject*
8289// and correctly compares to itself
8290void tst_qqmlecmascript::singletonFromQMLAndBackAndCompare()
8291{
8292 QQmlEngine engine;
8293 QQmlComponent component(&engine, testFile(fileName: "singletonTest2.qml"));
8294 QScopedPointer<QObject> o(component.create());
8295 if (component.errors().size())
8296 qDebug() << component.errors();
8297 QVERIFY(component.errors().isEmpty());
8298 QVERIFY(o != nullptr);
8299
8300 QCOMPARE(o->property("myInheritedQmlObjectTest1"), QVariant(true));
8301 QCOMPARE(o->property("myInheritedQmlObjectTest2"), QVariant(true));
8302 QCOMPARE(o->property("myInheritedQmlObjectTest3"), QVariant(true));
8303
8304 QCOMPARE(o->property("myQmlObjectTest1"), QVariant(true));
8305 QCOMPARE(o->property("myQmlObjectTest2"), QVariant(true));
8306 QCOMPARE(o->property("myQmlObjectTest3"), QVariant(true));
8307
8308 QCOMPARE(o->property("qobjectTest1"), QVariant(true));
8309 QCOMPARE(o->property("qobjectTest2"), QVariant(true));
8310 QCOMPARE(o->property("qobjectTest3"), QVariant(true));
8311
8312 QCOMPARE(o->property("singletonEqualToItself"), QVariant(true));
8313}
8314
8315void tst_qqmlecmascript::setPropertyOnInvalid()
8316{
8317 QQmlEngine engine;
8318 {
8319 QQmlComponent component(&engine, testFileUrl(fileName: "setPropertyOnNull.qml"));
8320 QString warning = component.url().toString() + ":4: TypeError: Value is null and could not be converted to an object";
8321 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
8322 QObject *object = component.create();
8323 QVERIFY(object);
8324 delete object;
8325 }
8326
8327 {
8328 QQmlComponent component(&engine, testFileUrl(fileName: "setPropertyOnUndefined.qml"));
8329 QString warning = component.url().toString() + ":4: TypeError: Value is undefined and could not be converted to an object";
8330 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(warning));
8331 QObject *object = component.create();
8332 QVERIFY(object);
8333 delete object;
8334 }
8335}
8336
8337void tst_qqmlecmascript::miscTypeTest()
8338{
8339 QQmlEngine engine;
8340 QQmlComponent component(&engine, testFileUrl(fileName: "misctypetest.qml"));
8341
8342 QObject *object = component.create();
8343 if (object == nullptr)
8344 qDebug() << component.errorString();
8345 QVERIFY(object != nullptr);
8346
8347 QVariant q;
8348 QMetaObject::invokeMethod(obj: object, member: "test_invalid_url_equal", Q_RETURN_ARG(QVariant, q));
8349 QVERIFY(q.toBool());
8350 QMetaObject::invokeMethod(obj: object, member: "test_invalid_url_strictequal", Q_RETURN_ARG(QVariant, q));
8351 QVERIFY(q.toBool());
8352 QMetaObject::invokeMethod(obj: object, member: "test_valid_url_equal", Q_RETURN_ARG(QVariant, q));
8353 QVERIFY(q.toBool());
8354 QMetaObject::invokeMethod(obj: object, member: "test_valid_url_strictequal", Q_RETURN_ARG(QVariant, q));
8355 QVERIFY(q.toBool());
8356
8357 delete object;
8358}
8359
8360void tst_qqmlecmascript::stackLimits()
8361{
8362 QJSEngine engine;
8363 engine.evaluate(QStringLiteral("function foo() {foo();} try {foo()} catch(e) { }"));
8364}
8365
8366void tst_qqmlecmascript::idsAsLValues()
8367{
8368 QQmlEngine engine;
8369 QString err = QString(QLatin1String("%1:5: Error: left-hand side of assignment operator is not an lvalue")).arg(a: testFileUrl(fileName: "idAsLValue.qml").toString());
8370 QQmlComponent component(&engine, testFileUrl(fileName: "idAsLValue.qml"));
8371 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(err));
8372 QScopedPointer<QObject> object(component.create());
8373 QVERIFY(!qobject_cast<MyQmlObject*>(object.get()));
8374}
8375
8376void tst_qqmlecmascript::qtbug_34792()
8377{
8378 QQmlEngine engine;
8379 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug34792.qml"));
8380
8381 QObject *object = component.create();
8382 if (object == nullptr)
8383 qDebug() << component.errorString();
8384 QVERIFY(object != nullptr);
8385 delete object;
8386}
8387
8388void tst_qqmlecmascript::noCaptureWhenWritingProperty()
8389{
8390 QQmlEngine engine;
8391 QQmlComponent component(&engine, testFileUrl(fileName: "noCaptureWhenWritingProperty.qml"));
8392 QScopedPointer<QObject> obj(component.create());
8393 QVERIFY(!obj.isNull());
8394 QCOMPARE(obj->property("somePropertyEvaluated").toBool(), false);
8395}
8396
8397void tst_qqmlecmascript::singletonWithEnum()
8398{
8399 QQmlEngine engine;
8400 QQmlComponent component(&engine, testFileUrl(fileName: "singletontype/singletonWithEnum.qml"));
8401 QScopedPointer<QObject> obj(component.create());
8402 if (obj.isNull())
8403 qDebug() << component.errors().first().toString();
8404 QVERIFY(!obj.isNull());
8405 QVariant prop = obj->property(name: "testValue");
8406 QCOMPARE(prop.type(), QVariant::Int);
8407 QCOMPARE(prop.toInt(), int(SingletonWithEnum::TestValue));
8408
8409 {
8410 QQmlExpression expr(qmlContext(obj.data()), obj.data(), "SingletonWithEnum.TestValue_MinusOne");
8411 bool valueUndefined = false;
8412 QVariant result = expr.evaluate(valueIsUndefined: &valueUndefined);
8413 QVERIFY2(!expr.hasError(), qPrintable(expr.error().toString()));
8414 QVERIFY(!valueUndefined);
8415 QCOMPARE(result.toInt(), -1);
8416 }
8417}
8418
8419void tst_qqmlecmascript::lazyBindingEvaluation()
8420{
8421 QQmlEngine engine;
8422 QQmlComponent component(&engine, testFileUrl(fileName: "lazyBindingEvaluation.qml"));
8423 QScopedPointer<QObject> obj(component.create());
8424 if (obj.isNull())
8425 qDebug() << component.errors().first().toString();
8426 QVERIFY(!obj.isNull());
8427 QVariant prop = obj->property(name: "arrayLength");
8428 QCOMPARE(prop.type(), QVariant::Int);
8429 QCOMPARE(prop.toInt(), 2);
8430}
8431
8432void tst_qqmlecmascript::varPropertyAccessOnObjectWithInvalidContext()
8433{
8434 QQmlEngine engine;
8435 QQmlComponent component(&engine, testFileUrl(fileName: "varPropertyAccessOnObjectWithInvalidContext.qml"));
8436 QScopedPointer<QObject> obj(component.create());
8437 if (obj.isNull())
8438 qDebug() << component.errors().first().toString();
8439 QVERIFY(!obj.isNull());
8440 QVERIFY(obj->property("success").toBool());
8441}
8442
8443void tst_qqmlecmascript::importedScriptsAccessOnObjectWithInvalidContext()
8444{
8445 QQmlEngine engine;
8446 const QUrl url = testFileUrl(fileName: "importedScriptsAccessOnObjectWithInvalidContext.qml");
8447 QTest::ignoreMessage(type: QtWarningMsg, qPrintable(url.toString() + ":29: TypeError: Cannot read property 'Foo' of null"));
8448 QQmlComponent component(&engine, url);
8449 QScopedPointer<QObject> obj(component.create());
8450 if (obj.isNull())
8451 qDebug() << component.errors().first().toString();
8452 QVERIFY(!obj.isNull());
8453 QTRY_VERIFY(obj->property("success").toBool());
8454}
8455
8456void tst_qqmlecmascript::importedScriptsWithoutQmlMode()
8457{
8458 QQmlEngine engine;
8459 QQmlComponent component(&engine, testFileUrl(fileName: "importScriptsWithoutQmlMode.qml"));
8460 QScopedPointer<QObject> obj(component.create());
8461 if (obj.isNull())
8462 qDebug() << component.errors().first().toString();
8463 QVERIFY(!obj.isNull());
8464 QTRY_VERIFY(obj->property("success").toBool());
8465}
8466
8467void tst_qqmlecmascript::contextObjectOnLazyBindings()
8468{
8469 QQmlEngine engine;
8470 QQmlComponent component(&engine, testFileUrl(fileName: "contextObjectOnLazyBindings.qml"));
8471 QScopedPointer<QObject> obj(component.create());
8472 if (obj.isNull())
8473 qDebug() << component.errors().first().toString();
8474 QVERIFY(!obj.isNull());
8475 QObject *subObject = qvariant_cast<QObject*>(v: obj->property(name: "subObject"));
8476 QVERIFY(subObject);
8477 QCOMPARE(subObject->property("testValue").toInt(), int(42));
8478}
8479
8480void tst_qqmlecmascript::garbageCollectionDuringCreation()
8481{
8482 QQmlEngine engine;
8483 QQmlComponent component(&engine);
8484 component.setData("import Qt.test 1.0\n"
8485 "QObjectContainerWithGCOnAppend {\n"
8486 " objectName: \"root\"\n"
8487 " FloatingQObject {\n"
8488 " objectName: \"parentLessChild\"\n"
8489 " property var blah;\n" // Ensure we have JS wrapper
8490 " }\n"
8491 "}\n",
8492 baseUrl: QUrl());
8493
8494 QScopedPointer<QObject> object(component.create());
8495 QVERIFY(!object.isNull());
8496
8497 QObjectContainer *container = qobject_cast<QObjectContainer*>(object: object.data());
8498 QCOMPARE(container->dataChildren.count(), 1);
8499
8500 QObject *child = container->dataChildren.first();
8501 QQmlData *ddata = QQmlData::get(object: child);
8502 QVERIFY(!ddata->jsWrapper.isNullOrUndefined());
8503
8504 gc(engine);
8505 QCOMPARE(container->dataChildren.count(), 0);
8506}
8507
8508void tst_qqmlecmascript::qtbug_39520()
8509{
8510 QQmlEngine engine;
8511 QQmlComponent component(&engine);
8512 component.setData("import QtQuick 2.0\n"
8513 "Item {\n"
8514 " property string s\n"
8515 " Component.onCompleted: test()\n"
8516 " function test() {\n"
8517 " var count = 1 * 1000 * 1000\n"
8518 " var t = ''\n"
8519 " for (var i = 0; i < count; ++i)\n"
8520 " t += 'testtest ' + i + '\n'\n"
8521 " s = t\n"
8522 " }\n"
8523 "}\n",
8524 baseUrl: QUrl());
8525
8526 QScopedPointer<QObject> object(component.create());
8527 QVERIFY(!object.isNull());
8528
8529 QString s = object->property(name: "s").toString();
8530 QCOMPARE(s.count('\n'), 1 * 1000 * 1000);
8531}
8532
8533class ContainedObject1 : public QObject
8534{
8535 Q_OBJECT
8536};
8537
8538class ContainedObject2 : public QObject
8539{
8540 Q_OBJECT
8541};
8542
8543class ObjectContainer : public QObject
8544{
8545 Q_OBJECT
8546 Q_PROPERTY(ContainedObject1 *object1 READ object1 WRITE setObject1)
8547 Q_PROPERTY(ContainedObject2 *object2 READ object2 WRITE setObject2)
8548public:
8549 explicit ObjectContainer(QObject *parent = nullptr) :
8550 QObject(parent),
8551 mGetterCalled(false),
8552 mSetterCalled(false)
8553 {
8554 }
8555
8556 ContainedObject1 *object1()
8557 {
8558 mGetterCalled = true;
8559 return nullptr;
8560 }
8561
8562 void setObject1(ContainedObject1 *)
8563 {
8564 mSetterCalled = true;
8565 }
8566
8567 ContainedObject2 *object2()
8568 {
8569 mGetterCalled = true;
8570 return nullptr;
8571 }
8572
8573 void setObject2(ContainedObject2 *)
8574 {
8575 mSetterCalled = true;
8576 }
8577
8578public:
8579 bool mGetterCalled;
8580 bool mSetterCalled;
8581};
8582
8583void tst_qqmlecmascript::readUnregisteredQObjectProperty()
8584{
8585 qmlRegisterType<ObjectContainer>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "ObjectContainer");
8586 QQmlEngine engine;
8587 QQmlComponent component(&engine, testFileUrl(fileName: "accessUnregisteredQObjectProperty.qml"));
8588 QScopedPointer<QObject> root(component.create());
8589 QVERIFY(root);
8590
8591 QMetaObject::invokeMethod(obj: root.get(), member: "readProperty");
8592 QCOMPARE(root->property("container").value<ObjectContainer*>()->mGetterCalled, true);
8593}
8594
8595void tst_qqmlecmascript::writeUnregisteredQObjectProperty()
8596{
8597 qmlRegisterType<ObjectContainer>(uri: "Test", versionMajor: 1, versionMinor: 0, qmlName: "ObjectContainer");
8598 QQmlEngine engine;
8599 QQmlComponent component(&engine, testFileUrl(fileName: "accessUnregisteredQObjectProperty.qml"));
8600 QScopedPointer<QObject> root(component.create());
8601 QVERIFY(root);
8602
8603 QMetaObject::invokeMethod(obj: root.get(), member: "writeProperty");
8604 QCOMPARE(root->property("container").value<ObjectContainer*>()->mSetterCalled, true);
8605}
8606
8607void tst_qqmlecmascript::switchExpression()
8608{
8609 // verify that we evaluate the expression inside switch() exactly once
8610 QJSEngine engine;
8611 QJSValue v = engine.evaluate(program: QString::fromLatin1(
8612 str: "var num = 0\n"
8613 "var x = 0\n"
8614 "function f() { ++num; return (Math.random() > 0.5) ? 0 : 1; }\n"
8615 "for (var i = 0; i < 1000; ++i) {\n"
8616 " switch (f()) {\n"
8617 " case 0:\n"
8618 " case 1:\n"
8619 " break;\n"
8620 " default:\n"
8621 " ++x;\n"
8622 " }\n"
8623 "}\n"
8624 "(x == 0 && num == 1000) ? true : false\n"
8625 ));
8626 QVERIFY(!v.isError());
8627 QCOMPARE(v.toBool(), true);
8628}
8629
8630void tst_qqmlecmascript::qtbug_46022()
8631{
8632 QQmlEngine engine;
8633 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_46022.qml"));
8634
8635 QScopedPointer<QObject> obj(component.create());
8636 QVERIFY(obj != nullptr);
8637 QCOMPARE(obj->property("test1").toBool(), true);
8638 QCOMPARE(obj->property("test2").toBool(), true);
8639}
8640
8641void tst_qqmlecmascript::qtbug_52340()
8642{
8643 QQmlEngine engine;
8644 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_52340.qml"));
8645 QScopedPointer<QObject> object(component.create());
8646 QVERIFY(!object.isNull());
8647 QVariant returnValue;
8648 QVERIFY(QMetaObject::invokeMethod(object.data(), "testCall", Q_RETURN_ARG(QVariant, returnValue)));
8649 QVERIFY(returnValue.isValid());
8650 QVERIFY(returnValue.toBool());
8651}
8652
8653void tst_qqmlecmascript::qtbug_54589()
8654{
8655 QQmlEngine engine;
8656 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_54589.qml"));
8657
8658 QScopedPointer<QObject> obj(component.create());
8659 QVERIFY(obj != nullptr);
8660 QCOMPARE(obj->property("result").toBool(), true);
8661}
8662
8663void tst_qqmlecmascript::qtbug_54687()
8664{
8665 QJSEngine e;
8666 // it's simple: this shouldn't crash.
8667 e.evaluate(program: "12\n----12");
8668}
8669
8670void tst_qqmlecmascript::stringify_qtbug_50592()
8671{
8672 QQmlEngine engine;
8673 QQmlComponent component(&engine, testFileUrl(fileName: "stringify_qtbug_50592.qml"));
8674
8675 QScopedPointer<QObject> obj(component.create());
8676 QVERIFY(obj != nullptr);
8677 QCOMPARE(obj->property("source").toString(),
8678 QString::fromLatin1("\"http://example.org/some_nonexistant_image.png\""));
8679}
8680
8681// Tests for the JS-only instanceof. Tests for the QML extensions for
8682// instanceof belong in tst_qqmllanguage!
8683void tst_qqmlecmascript::instanceof_data()
8684{
8685 QTest::addColumn<QString>(name: "setupCode");
8686 QTest::addColumn<QVariant>(name: "expectedValue");
8687
8688 // so the way this works is that the name of the test tag defines the test
8689 // to run. the code in setupCode defines code run before the actual test
8690 // (e.g. to create vars).
8691 //
8692 // the expectedValue is either a boolean true or false for whether the two
8693 // operands are indeed an instanceof each other, or a string for the
8694 // expected error message.
8695 QTest::newRow(dataTag: "String instanceof String")
8696 << ""
8697 << QVariant(false);
8698 QTest::newRow(dataTag: "s instanceof String")
8699 << "var s = \"hello\""
8700 << QVariant(false);
8701 QTest::newRow(dataTag: "objectString instanceof String")
8702 << "var objectString = new String(\"hello\")"
8703 << QVariant(true);
8704 QTest::newRow(dataTag: "o instanceof Object")
8705 << "var o = new Object()"
8706 << QVariant(true);
8707 QTest::newRow(dataTag: "o instanceof String")
8708 << "var o = new Object()"
8709 << QVariant(false);
8710 QTest::newRow(dataTag: "true instanceof true")
8711 << ""
8712 << QVariant("TypeError: Type error");
8713 QTest::newRow(dataTag: "1 instanceof Math")
8714 << ""
8715 << QVariant("TypeError: Type error");
8716 QTest::newRow(dataTag: "date instanceof Date")
8717 << "var date = new Date"
8718 << QVariant(true);
8719 QTest::newRow(dataTag: "date instanceof Object")
8720 << "var date = new Date"
8721 << QVariant(true);
8722 QTest::newRow(dataTag: "date instanceof String")
8723 << "var date = new Date"
8724 << QVariant(false);
8725}
8726
8727void tst_qqmlecmascript::instanceof()
8728{
8729 QFETCH(QString, setupCode);
8730 QFETCH(QVariant, expectedValue);
8731
8732 QJSEngine engine;
8733 QJSValue ret = engine.evaluate(program: setupCode + ";\n" + QTest::currentDataTag());
8734
8735 if (expectedValue.type() == QVariant::Bool) {
8736 bool returnValue = ret.toBool();
8737 QVERIFY2(!ret.isError(), qPrintable(ret.toString()));
8738 QCOMPARE(returnValue, expectedValue.toBool());
8739 } else {
8740 QVERIFY2(ret.isError(), qPrintable(ret.toString()));
8741 QCOMPARE(ret.toString(), expectedValue.toString());
8742 }
8743}
8744
8745void tst_qqmlecmascript::constkw_data()
8746{
8747 QTest::addColumn<QString>(name: "sourceCode");
8748 QTest::addColumn<bool>(name: "exceptionExpected");
8749 QTest::addColumn<QVariant>(name: "expectedValue");
8750
8751 QTest::newRow(dataTag: "simpleconst")
8752 << "const v = 5\n"
8753 "v\n"
8754 << false
8755 << QVariant(5);
8756 QTest::newRow(dataTag: "twoconst")
8757 << "const v = 5, i = 10\n"
8758 "v + i\n"
8759 << false
8760 << QVariant(15);
8761 QTest::newRow(dataTag: "constandvar")
8762 << "const v = 5\n"
8763 "var i = 20\n"
8764 "v + i\n"
8765 << false
8766 << QVariant(25);
8767 QTest::newRow(dataTag: "const-multiple-scopes-same-var")
8768 << "const v = 3\n"
8769 "function f() { const v = 1; return v; }\n"
8770 "v + f()\n"
8771 << false
8772 << QVariant(4);
8773
8774 // error cases
8775 QTest::newRow(dataTag: "const-no-initializer")
8776 << "const v\n"
8777 << true
8778 << QVariant("SyntaxError: Missing initializer in const declaration");
8779 QTest::newRow(dataTag: "const-no-initializer-comma")
8780 << "const v = 1, i\n"
8781 << true
8782 << QVariant("SyntaxError: Missing initializer in const declaration");
8783 QTest::newRow(dataTag: "const-no-duplicate")
8784 << "const v = 1, v = 2\n"
8785 << true
8786 << QVariant("SyntaxError: Identifier v has already been declared");
8787 QTest::newRow(dataTag: "const-no-duplicate-2")
8788 << "const v = 1\n"
8789 "const v = 2\n"
8790 << true
8791 << QVariant("SyntaxError: Identifier v has already been declared");
8792 QTest::newRow(dataTag: "const-no-duplicate-var")
8793 << "const v = 1\n"
8794 "var v = 1\n"
8795 << true
8796 << QVariant("SyntaxError: Identifier v has already been declared");
8797 QTest::newRow(dataTag: "var-no-duplicate-const")
8798 << "var v = 1\n"
8799 "const v = 1\n"
8800 << true
8801 << QVariant("SyntaxError: Identifier v has already been declared");
8802 QTest::newRow(dataTag: "const-no-duplicate-let")
8803 << "const v = 1\n"
8804 "let v = 1\n"
8805 << true
8806 << QVariant("SyntaxError: Identifier v has already been declared");
8807 QTest::newRow(dataTag: "let-no-duplicate-const")
8808 << "let v = 1\n"
8809 "const v = 1\n"
8810 << true
8811 << QVariant("SyntaxError: Identifier v has already been declared");
8812}
8813
8814void tst_qqmlecmascript::constkw()
8815{
8816 QFETCH(QString, sourceCode);
8817 QFETCH(bool, exceptionExpected);
8818 QFETCH(QVariant, expectedValue);
8819
8820 QJSEngine engine;
8821 QJSValue ret = engine.evaluate(program: sourceCode);
8822
8823 if (!exceptionExpected) {
8824 QVERIFY2(!ret.isError(), qPrintable(ret.toString()));
8825 QCOMPARE(ret.toVariant(), expectedValue);
8826 } else {
8827 QVERIFY2(ret.isError(), qPrintable(ret.toString()));
8828 QCOMPARE(ret.toString(), expectedValue.toString());
8829 }
8830}
8831
8832// Redefine a property found on the global object. It shouldn't throw.
8833void tst_qqmlecmascript::redefineGlobalProp()
8834{
8835 {
8836 QJSEngine engine;
8837 QJSValue ret = engine.evaluate(program: "\"use strict\"\n var toString = 1;");
8838 QVERIFY2(!ret.isError(), qPrintable(ret.toString()));
8839 }
8840 {
8841 QJSEngine engine;
8842 QJSValue ret = engine.evaluate(program: "var toString = 1;");
8843 QVERIFY2(!ret.isError(), qPrintable(ret.toString()));
8844 }
8845}
8846
8847void tst_qqmlecmascript::freeze_empty_object()
8848{
8849 // this shouldn't crash
8850 QJSEngine engine;
8851 QJSValue v = engine.evaluate(program: QString::fromLatin1(
8852 str: "var obj = {};\n"
8853 "Object.freeze(obj);\n"
8854 ));
8855 QVERIFY(!v.isError());
8856 QCOMPARE(v.toBool(), true);
8857}
8858
8859void tst_qqmlecmascript::singleBlockLoops()
8860{
8861 QQmlEngine engine;
8862 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug_59012.qml"));
8863
8864 QScopedPointer<QObject> obj(component.create());
8865 QVERIFY(obj != nullptr);
8866 QVERIFY(!component.isError());
8867}
8868
8869// 'counter' was incorrectly resolved as a type rather than a variable.
8870// This fix ensures it looks up the right thing.
8871void tst_qqmlecmascript::qtbug_60547()
8872{
8873 QQmlEngine engine;
8874 QQmlComponent component(&engine, testFileUrl(fileName: "qtbug60547/main.qml"));
8875 QScopedPointer<QObject> object(component.create());
8876 QVERIFY2(!object.isNull(), qPrintable(component.errorString()));
8877 QCOMPARE(object->property("counter"), QVariant(int(1)));
8878}
8879
8880void tst_qqmlecmascript::anotherNaN()
8881{
8882 QQmlEngine engine;
8883 QQmlComponent component(&engine, testFileUrl(fileName: "nans.qml"));
8884 QScopedPointer<QObject> object(component.create());
8885 QVERIFY2(!object.isNull(), qPrintable(component.errorString()));
8886 object->setProperty(name: "prop", value: std::numeric_limits<double>::quiet_NaN()); // don't crash
8887
8888 std::uint64_t anotherNaN = 0xFFFFFF01000000F7ul;
8889 double d;
8890 std::memcpy(dest: &d, src: &anotherNaN, n: sizeof(d));
8891 QVERIFY(std::isnan(d));
8892 object->setProperty(name: "prop", value: d); // don't crash
8893}
8894
8895void tst_qqmlecmascript::delayLoadingArgs()
8896{
8897 QJSEngine engine;
8898 QJSValue ret = engine.evaluate(program: "(function(x){return x + (x+=2)})(20)");
8899 QCOMPARE(ret.toInt(), 42); // esp. not 44.
8900}
8901
8902void tst_qqmlecmascript::manyArguments()
8903{
8904 const char *testCase =
8905 "function x() { var sum; for (var i = 0; i < arguments.length; ++i) sum += arguments[i][0]; }"
8906 "x([0],[1],[2],[3],[4],[5],[6],[7],[8],[9], [0],[1],[2],[3],[4],[5],[6],[7],[8],[9], [0],[1],[2],[3],[4],[5],[6],[7],[8],[9])";
8907
8908 QJSEngine engine;
8909 engine.evaluate(program: testCase);
8910}
8911
8912void tst_qqmlecmascript::forInIterator()
8913{
8914 auto testCase =
8915 "(function(){\n"
8916 "var x = 'yoyo'\n"
8917 "var i\n"
8918 "for (i in x) {\n"
8919 "}\n"
8920 "return i\n"
8921 "})()";
8922 QJSEngine engine;
8923 QJSValue ret = engine.evaluate(program: testCase);
8924 QVERIFY(ret.isString());
8925 QCOMPARE(ret.toString(), QStringLiteral("3"));
8926}
8927
8928void tst_qqmlecmascript::localForInIterator()
8929{
8930 auto testCase =
8931 "(function(){\n"
8932 "var x = 'yoyo'\n"
8933 "for (var i in x) {\n"
8934 "}\n"
8935 "return i\n"
8936 "})()";
8937 QJSEngine engine;
8938 QJSValue ret = engine.evaluate(program: testCase);
8939 QVERIFY(ret.isString());
8940 QCOMPARE(ret.toString(), QStringLiteral("3"));
8941}
8942
8943void tst_qqmlecmascript::shadowedFunctionName()
8944{
8945 // verify that arguments shadow the function name
8946 QJSEngine engine;
8947 QJSValue v = engine.evaluate(program: QString::fromLatin1(
8948 str: "function f(f) { return f; }\n"
8949 "f(true)\n"
8950 ));
8951 QVERIFY(!v.isError());
8952 QVERIFY(v.isBool());
8953 QCOMPARE(v.toBool(), true);
8954}
8955
8956void tst_qqmlecmascript::callPropertyOnUndefined()
8957{
8958 QJSEngine engine;
8959 QJSValue v = engine.evaluate(program: QString::fromLatin1(
8960 str: "function f() {\n"
8961 " var base;\n"
8962 " base.push(1);"
8963 "}\n"
8964 ));
8965 QVERIFY(!v.isError()); // well, more importantly: this shouldn't fail on an assert.
8966}
8967
8968void tst_qqmlecmascript::jumpStrictNotEqualUndefined()
8969{
8970 QJSEngine engine;
8971 QJSValue v = engine.evaluate(program: QString::fromLatin1(
8972 str: "var ok = 0\n"
8973 "var foo = 0\n"
8974 "if (foo !== void 1)\n"
8975 " ++ok;\n"
8976 "else\n"
8977 " --ok;\n"
8978 "if (foo === void 1)\n"
8979 " --ok;\n"
8980 "else\n"
8981 " ++ok;\n"
8982 "ok\n"
8983 ));
8984 QVERIFY(!v.isError());
8985 QCOMPARE(v.toInt(), 2);
8986}
8987
8988void tst_qqmlecmascript::removeBindingsWithNoDependencies()
8989{
8990 QQmlEngine engine;
8991 QQmlComponent component(&engine, testFileUrl(fileName: "removeBindingsWithNoDependencies.qml"));
8992 QScopedPointer<QObject> object(component.create());
8993 QVERIFY(!object.isNull());
8994 QVariant rect = object->property(name: "placement");
8995 QCOMPARE(rect.toRectF(), QRectF(0, 0, 100, 100));
8996 const QMetaObject *metaObject = object->metaObject();
8997
8998 {
8999 const QMetaProperty prop = metaObject->property(index: metaObject->indexOfProperty(name: "placement"));
9000 QVERIFY(prop.isValid());
9001 QVERIFY(!QQmlPropertyPrivate::binding(object.data(), QQmlPropertyIndex(prop.propertyIndex())));
9002 }
9003
9004 {
9005 const QMetaProperty prop = metaObject->property(index: metaObject->indexOfProperty(name: "partialPlacement"));
9006 QVERIFY(prop.isValid());
9007 QQmlAbstractBinding *vtProxyBinding = QQmlPropertyPrivate::binding(object.data(), index: QQmlPropertyIndex(prop.propertyIndex()));
9008 QVERIFY(vtProxyBinding);
9009 QVERIFY(vtProxyBinding->isValueTypeProxy());
9010
9011 QQmlValueTypeProxyBinding *proxy = static_cast<QQmlValueTypeProxyBinding*>(vtProxyBinding);
9012 QVERIFY(!proxy->subBindings());
9013 }
9014
9015}
9016
9017void tst_qqmlecmascript::preserveBindingWithUnresolvedNames()
9018{
9019 QQmlEngine engine;
9020 QQmlComponent component(&engine, testFileUrl(fileName: "preserveBindingWithUnresolvedNames.qml"));
9021 QScopedPointer<QObject> object(component.create());
9022 QVERIFY(!object.isNull());
9023 QCOMPARE(object->property("testTypeOf").toString(), QString("undefined"));
9024 QObject obj;
9025 engine.rootContext()->setContextProperty("contextProp", &obj);
9026 QCOMPARE(object->property("testTypeOf").toString(), QString("object"));
9027}
9028
9029void tst_qqmlecmascript::temporaryDeadZone()
9030{
9031 QJSEngine engine;
9032 QJSValue v = engine.evaluate(program: QString::fromLatin1(str: "a; let a;"));
9033 QVERIFY(v.isError());
9034 v = engine.evaluate(program: QString::fromLatin1(str: "a.name; let a;"));
9035 QVERIFY(v.isError());
9036 v = engine.evaluate(program: QString::fromLatin1(str: "var a = {}; a[b]; let b;"));
9037 QVERIFY(v.isError());
9038 v = engine.evaluate(program: QString::fromLatin1(str: "class C { constructor() { super[x]; let x; } }; new C()"));
9039 QVERIFY(v.isError());
9040}
9041
9042void tst_qqmlecmascript::importLexicalVariables_data()
9043{
9044 QTest::addColumn<QUrl>(name: "testFile");
9045 QTest::addColumn<QString>(name: "expected");
9046
9047 QTest::newRow(dataTag: "script")
9048 << testFileUrl(fileName: "importLexicalVariables_script.qml")
9049 << QStringLiteral("000 100 210");
9050 QTest::newRow(dataTag: "pragmaLibrary")
9051 << testFileUrl(fileName: "importLexicalVariables_pragmaLibrary.qml")
9052 << QStringLiteral("000 100 210");
9053 QTest::newRow(dataTag: "module")
9054 << testFileUrl(fileName: "importLexicalVariables_module.qml")
9055 << QStringLiteral("000 000 110");
9056}
9057
9058void tst_qqmlecmascript::importLexicalVariables()
9059{
9060 QFETCH(QUrl, testFile);
9061 QFETCH(QString, expected);
9062
9063 QQmlEngine engine;
9064 QQmlComponent component(&engine, testFile);
9065 QScopedPointer<QObject> object(component.create());
9066 QVERIFY(object != nullptr);
9067 QVERIFY(!component.isError());
9068
9069 QVariant result;
9070 QMetaObject::invokeMethod(obj: object.data(), member: "runTest", Qt::DirectConnection, Q_RETURN_ARG(QVariant, result));
9071 QCOMPARE(result, QVariant(expected));
9072}
9073
9074void tst_qqmlecmascript::hugeObject()
9075{
9076 // mainly check that this doesn't crash
9077 QJSEngine engine;
9078 QJSValue v = engine.evaluate(program: QString::fromLatin1(
9079 str: "var known = {}, prefix = 'x'\n"
9080 "for (var i = 0; i < 150000; i++) known[prefix + i] = true;"
9081 ));
9082 QVERIFY(!v.isError());
9083}
9084
9085void tst_qqmlecmascript::templateStringTerminator()
9086{
9087 QJSEngine engine;
9088 const QJSValue value = engine.evaluate(program: "let a = 123; let b = `x${a}\ny^`; b;");
9089 QVERIFY(!value.isError());
9090 QCOMPARE(value.toString(), QLatin1String("x123\ny^"));
9091}
9092
9093void tst_qqmlecmascript::arrayAndException()
9094{
9095 QJSEngine engine;
9096 const QJSValue value = engine.evaluate(program: "[...[],[,,$]]");
9097 // Should not crash
9098 QVERIFY(value.isError());
9099}
9100
9101void tst_qqmlecmascript::numberToStringWithRadix()
9102{
9103 QJSEngine engine;
9104 {
9105 const QJSValue value = engine.evaluate(program: ".5.toString(5)");
9106 QVERIFY(!value.isError());
9107 QVERIFY(value.toString().startsWith("0.2222222222"));
9108 }
9109 {
9110 const QJSValue value = engine.evaluate(program: ".05.toString(5)");
9111 QVERIFY(!value.isError());
9112 QVERIFY(value.toString().startsWith("0.01111111111"));
9113 }
9114}
9115
9116void tst_qqmlecmascript::tailCallWithArguments()
9117{
9118 QJSEngine engine;
9119 const QJSValue value = engine.evaluate(
9120 program: "'use strict';\n"
9121 "[[1, 2]].map(function (a) {\n"
9122 " return (function() { return Math.min.apply(this, arguments); })(a[0], a[1]);\n"
9123 "})[0];");
9124 QVERIFY(!value.isError());
9125 QCOMPARE(value.toInt(), 1);
9126}
9127
9128void tst_qqmlecmascript::deleteSparseInIteration()
9129{
9130 QJSEngine engine;
9131 const QJSValue value = engine.evaluate(
9132 program: "(function() {\n"
9133 " var obj = { 1: null, 2: null, 4096: null };\n"
9134 " var iterated = [];\n"
9135 " for (var t in obj) {\n"
9136 " if (t == 2)\n"
9137 " delete obj[t];\n"
9138 " iterated.push(t);\n"
9139 " }\n"
9140 " return iterated;"
9141 "})()");
9142 QVERIFY(value.isArray());
9143 QCOMPARE(value.property("length").toInt(), 3);
9144 QCOMPARE(value.property("0").toInt(), 1);
9145 QCOMPARE(value.property("1").toInt(), 2);
9146 QCOMPARE(value.property("2").toInt(), 4096);
9147}
9148
9149void tst_qqmlecmascript::saveAccumulatorBeforeToInt32()
9150{
9151 QJSEngine engine;
9152
9153 // Infinite recursion produces a range error, but should not crash.
9154 // Also, any GC runs in between should not trash the temporary results of "a+a".
9155 const QJSValue value = engine.evaluate(program: "function a(){a(a&a+a)}a()");
9156 QVERIFY(value.isError());
9157 QCOMPARE(value.toString(), QLatin1String("RangeError: Maximum call stack size exceeded."));
9158}
9159
9160void tst_qqmlecmascript::intMinDividedByMinusOne()
9161{
9162 QQmlEngine engine;
9163 QQmlComponent component(&engine);
9164 component.setData(QByteArray("import QtQml 2.2\n"
9165 "QtObject {\n"
9166 " property int intMin: -2147483648\n"
9167 " property int minusOne: -1\n"
9168 " property double doesNotFitInInt: intMin / minusOne\n"
9169 "}"), baseUrl: QUrl());
9170 QVERIFY(component.isReady());
9171 QScopedPointer<QObject> object(component.create());
9172 QVERIFY(!object.isNull());
9173 QCOMPARE(object->property("doesNotFitInInt").toUInt(), 2147483648u);
9174}
9175
9176void tst_qqmlecmascript::undefinedPropertiesInObjectWrapper()
9177{
9178 QQmlEngine engine;
9179 QQmlComponent component(&engine, testFile(fileName: "undefinedPropertiesInObjectWrapper.qml"));
9180 QVERIFY(component.isReady());
9181 QScopedPointer<QObject> object(component.create());
9182 QVERIFY(!object.isNull());
9183}
9184
9185void tst_qqmlecmascript::hugeRegexpQuantifiers()
9186{
9187 QJSEngine engine;
9188 QJSValue value = engine.evaluate(program: "/({3072140529})?{3072140529}/");
9189
9190 // It's a regular expression, but it won't match anything.
9191 // The RegExp compiler also shouldn't crash.
9192 QVERIFY(value.isRegExp());
9193}
9194
9195struct CppSingleton1 : public QObject
9196{
9197 Q_OBJECT
9198 Q_PROPERTY(int testVar MEMBER testVar CONSTANT)
9199public:
9200 const int testVar = 0;
9201};
9202
9203struct CppSingleton2 : public QObject
9204{
9205 Q_OBJECT
9206 Q_PROPERTY(int testVar MEMBER testVar CONSTANT)
9207public:
9208 const int testVar = 1;
9209};
9210
9211void tst_qqmlecmascript::singletonTypeWrapperLookup()
9212{
9213 QQmlEngine engine;
9214
9215 auto singletonTypeId1 = qmlRegisterSingletonType<CppSingleton1>(uri: "Test.Singletons", versionMajor: 1, versionMinor: 0, typeName: "CppSingleton1",
9216 callback: [](QQmlEngine *, QJSEngine *) -> QObject * {
9217 return new CppSingleton1;
9218 });
9219
9220 auto singletonTypeId2 = qmlRegisterSingletonType<CppSingleton2>(uri: "Test.Singletons", versionMajor: 1, versionMinor: 0, typeName: "CppSingleton2",
9221 callback: [](QQmlEngine *, QJSEngine *) -> QObject * {
9222 return new CppSingleton2;
9223 });
9224
9225 auto cleanup = qScopeGuard(f: [&]() {
9226 QQmlMetaType::unregisterType(type: singletonTypeId1);
9227 QQmlMetaType::unregisterType(type: singletonTypeId2);
9228 });
9229
9230 QQmlComponent component(&engine, testFileUrl(fileName: "SingletonLookupTest.qml"));
9231 QScopedPointer<QObject> test(component.create());
9232 QVERIFY2(!test.isNull(), qPrintable(component.errorString()));
9233
9234 auto singleton1 = engine.singletonInstance<CppSingleton1*>(qmlTypeId: singletonTypeId1);
9235 QVERIFY(singleton1);
9236
9237 auto singleton2 = engine.singletonInstance<CppSingleton2*>(qmlTypeId: singletonTypeId2);
9238 QVERIFY(singleton2);
9239
9240 QCOMPARE(test->property("firstLookup").toInt(), singleton1->testVar);
9241 QCOMPARE(test->property("secondLookup").toInt(), singleton2->testVar);
9242}
9243
9244void tst_qqmlecmascript::getThisObject()
9245{
9246 QQmlEngine engine;
9247 QQmlComponent component(&engine, testFileUrl(fileName: "getThis.qml"));
9248 QVERIFY(component.isReady());
9249 QScopedPointer<QObject> test(component.create());
9250 QVERIFY(!test.isNull());
9251
9252 QTRY_COMPARE(qvariant_cast<QObject *>(test->property("self")), test.data());
9253}
9254
9255// QTBUG-77954
9256void tst_qqmlecmascript::semicolonAfterProperty()
9257{
9258 QQmlEngine engine;
9259 QQmlComponent component(&engine, testFileUrl(fileName: "semicolonAfterProperty.qml"));
9260 QVERIFY(component.isReady());
9261 QScopedPointer<QObject> test(component.create());
9262 QVERIFY(!test.isNull());
9263}
9264
9265void tst_qqmlecmascript::hugeStack()
9266{
9267 QQmlEngine engine;
9268 QQmlComponent component(&engine, testFileUrl(fileName: "hugeStack.qml"));
9269 QVERIFY(component.isReady());
9270 QScopedPointer<QObject> test(component.create());
9271 QVERIFY(!test.isNull());
9272
9273 QVariant huge = test->property(name: "longList");
9274 QCOMPARE(qvariant_cast<QJSValue>(huge).property(QLatin1String("length")).toInt(), 33059);
9275}
9276
9277void tst_qqmlecmascript::gcCrashRegressionTest()
9278{
9279 const QString qmljs = QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs";
9280 if (!QFile::exists(fileName: qmljs)) {
9281 QSKIP("Tets requires qmljs");
9282 }
9283 QProcess process;
9284
9285 QTemporaryFile infile;
9286 QVERIFY(infile.open());
9287 infile.write(data: R"js(
9288 function i_want_to_break_free() {
9289 var n = 400;
9290 var m = 10;
9291 var regex = new RegExp("(ab)".repeat(n), "g"); // g flag to trigger the vulnerable path
9292 var part = "ab".repeat(n); // matches have to be at least size 2 to prevent interning
9293 var s = (part + "|").repeat(m);
9294 var cnt = 0;
9295 var ary = [];
9296 s.replace(regex, function() {
9297 for (var i = 1; i < arguments.length-2; ++i) {
9298 if (typeof arguments[i] !== 'string') {
9299 i_am_free = arguments[i];
9300 throw "success";
9301 }
9302 ary[cnt++] = arguments[i]; // root everything to force GC
9303 }
9304 return "x";
9305 });
9306 }
9307 try { i_want_to_break_free(); } catch (e) {console.log("hi") }
9308 console.log(typeof(i_am_free)); // will print "object"
9309 )js");
9310 infile.close();
9311
9312 QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
9313 environment.insert(name: "QV4_GC_MAX_STACK_SIZE", value: "32768");
9314
9315 process.setProcessEnvironment(environment);
9316 process.start(program: qmljs, arguments: QStringList({infile.fileName()}));
9317 QVERIFY(process.waitForStarted());
9318 const qint64 pid = process.processId();
9319 QVERIFY(pid != 0);
9320 QVERIFY(process.waitForFinished());
9321 QCOMPARE(process.exitCode(), 0);
9322}
9323
9324void tst_qqmlecmascript::variantConversionMethod()
9325{
9326 QQmlEngine qmlengine;
9327
9328 VariantConvertObject obj;
9329 qmlengine.rootContext()->setContextProperty("variantObject", &obj);
9330
9331 QQmlComponent component(&qmlengine, testFileUrl(fileName: "variantConvert.qml"));
9332 QScopedPointer<QObject> o(component.create());
9333 QVERIFY(o != nullptr);
9334 QCOMPARE(obj.funcCalled, QLatin1String("QModelIndex"));
9335}
9336
9337void tst_qqmlecmascript::proxyIteration()
9338{
9339 QQmlEngine engine;
9340 QQmlComponent component(&engine, testFileUrl(fileName: "proxyIteration.qml"));
9341 QScopedPointer<QObject> root(component.create());
9342 QVERIFY2(root != nullptr, qPrintable(component.errorString()));
9343 QCOMPARE(root->property("sum").toInt(), 6);
9344}
9345
9346void tst_qqmlecmascript::proxyHandlerTraps()
9347{
9348 const QString expression = QStringLiteral(R"SNIPPET(
9349 (function(){
9350 const target = {
9351 prop: 47
9352 };
9353 const handler = {
9354 getOwnPropertyDescriptor(target, prop) {
9355 return { configurable: true, enumerable: true, value: 47 };
9356 }
9357 };
9358 const proxy = new Proxy(target, handler);
9359
9360 // QTBUG-88786
9361 if (!proxy.propertyIsEnumerable("prop"))
9362 throw Error("FAIL: propertyisEnumerable");
9363 if (!proxy.hasOwnProperty("prop"))
9364 throw Error("FAIL: hasOwnProperty");
9365
9366 return "SUCCESS";
9367 })()
9368 )SNIPPET");
9369
9370 QJSEngine engine;
9371 QJSValue value = engine.evaluate(program: expression);
9372 QVERIFY(value.isString() && value.toString() == QStringLiteral("SUCCESS"));
9373}
9374
9375void tst_qqmlecmascript::functionAsDefaultArgument()
9376{
9377 QQmlEngine engine;
9378 QQmlComponent component(&engine, testFileUrl(fileName: "functionAsDefaultArgument.qml"));
9379 QScopedPointer<QObject> root(component.create());
9380 QVERIFY(root);
9381 QCOMPARE(root->objectName(), "didRun");
9382}
9383
9384QTEST_MAIN(tst_qqmlecmascript)
9385
9386#include "tst_qqmlecmascript.moc"
9387

source code of qtdeclarative/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp