From 208e8e87d338e8956f4dbb7243a7cc5ec6553f13 Mon Sep 17 00:00:00 2001 From: Sami Merila Date: Wed, 26 Jan 2011 14:27:35 +0200 Subject: QComboBox popup incorrectly opened in Sym^3 QCombobox popup opens into incorrect position in Sym^3. This is due to that there is a new layout in use, where softkeys are positioned at the bottom. The current implementation just checks if native stacon component is in use and if not, puts the popup to left/right border of the application area (depending on UI direction). Task-number: QTBUG-16886 Reviewed-by: Miikka Heikkinen --- src/gui/widgets/qcombobox.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp index 3e5f1b3..dbbf49a 100644 --- a/src/gui/widgets/qcombobox.cpp +++ b/src/gui/widgets/qcombobox.cpp @@ -2476,10 +2476,18 @@ void QComboBox::showPopup() listRect.setWidth(listRect.height()); //by default popup is centered on screen in landscape listRect.moveCenter(screen.center()); - if (staConTopRect.IsEmpty() && AknLayoutUtils::CbaLocation() != AknLayoutUtils::EAknCbaLocationBottom) { - // landscape without stacon, menu should be at the right - (opt.direction == Qt::LeftToRight) ? listRect.setRight(screen.right()) : - listRect.setLeft(screen.left()); + if (staConTopRect.IsEmpty()) { + TRect cbaRect = TRect(); + AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EControlPane, cbaRect); + AknLayoutUtils::TAknCbaLocation cbaLocation = AknLayoutUtils::CbaLocation(); + switch (cbaLocation) { + case AknLayoutUtils::EAknCbaLocationRight: + listRect.setRight(screen.right()); + break; + case AknLayoutUtils::EAknCbaLocationLeft: + listRect.setLeft(screen.left()); + break; + } } } #endif -- cgit v0.12 From 121e2b39043a4ffc6583f250aebb9a3a746076c1 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 13 Dec 2010 15:38:47 +0100 Subject: Improve timer ID safety by using a serial counter per ID. The high bits of the counter are not used, so we store a serial counter there. This has nothing to do with the serial counter used in nextFreeTimerId, which is there for ABA protection. Reviewed-by: Bradley T. Hughes --- src/corelib/kernel/qabstracteventdispatcher.cpp | 30 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp index e79f87a..cc08f092 100644 --- a/src/corelib/kernel/qabstracteventdispatcher.cpp +++ b/src/corelib/kernel/qabstracteventdispatcher.cpp @@ -137,6 +137,12 @@ void QAbstractEventDispatcherPrivate::init() // free list). As an added protection, we use the cell to store an invalid // (negative) value that we can later check for integrity. // +// ABA prevention simply adds a value to 7 of the top 8 bits when resetting +// nextFreeTimerId. +// +// The extra code is the bucket allocation which allows us to start with a +// very small bucket size and grow as needed. +// // (continues below). int QAbstractEventDispatcherPrivate::allocateTimerId() { @@ -164,6 +170,8 @@ int QAbstractEventDispatcherPrivate::allocateTimerId() newTimerId = prepareNewValueWithSerialNumber(timerId, b[at]); } while (!nextFreeTimerId.testAndSetRelaxed(timerId, newTimerId)); + timerId &= TimerIdMask; + timerId |= b[at] & TimerSerialMask; b[at] = -timerId; return timerId; @@ -174,12 +182,13 @@ int QAbstractEventDispatcherPrivate::allocateTimerId() // X[timerId] = nextFreeTimerId; // then we update nextFreeTimerId to the timer we've just released // -// The extra code in allocateTimerId and releaseTimerId are ABA prevention -// and bucket memory. The buckets are simply to make sure we allocate only -// the necessary number of timers. See above. -// // ABA prevention simply adds a value to 7 of the top 8 bits when resetting // nextFreeTimerId. +// +// In addition to that, we update the same 7 bits in each entry in the bucket +// as a counter. That way, a timer ID allocated and released will always be +// returned with a different ID. This reduces the chances of timers being released +// erroneously by application code. void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId) { int which = timerId & TimerIdMask; @@ -187,12 +196,21 @@ void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId) int at = bucketIndex(bucket, which); int *b = timerIds[bucket]; - Q_ASSERT(b[at] == -timerId); +#ifndef QT_NO_DEBUG + // debug code + Q_ASSERT_X(timerId == -b[at], "QAbstractEventDispatcher::releaseTimerId", "Timer ID was not found, fix application"); +#else + if (timerId != -b[at]) { + // release code + qWarning("Timer ID %d was not found, fix application", timerId); + return; + } +#endif int freeId, newTimerId; do { freeId = nextFreeTimerId;//.loadAcquire(); // ### FIXME Proper memory ordering semantics - b[at] = freeId & TimerIdMask; + b[at] = prepareNewValueWithSerialNumber(-b[at], freeId); newTimerId = prepareNewValueWithSerialNumber(freeId, timerId); } while (!nextFreeTimerId.testAndSetRelease(freeId, newTimerId)); -- cgit v0.12 From 861333040c252fa0f53894b604f7cb768c085281 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 26 Jan 2011 14:06:11 +0100 Subject: Check if the interpolators have already been deleted. During application destruction, the order in which static destructors is run is undetermined. So avoid a null-pointer dereference. Task-number: QTBUG-16855 Reviewed-by: Robin Burchell Patch by task reporter --- src/corelib/animation/qvariantanimation.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index 212e85d..c76cb89 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -431,12 +431,17 @@ void QVariantAnimation::registerInterpolator(QVariantAnimation::Interpolator fun { // will override any existing interpolators QInterpolatorVector *interpolators = registeredInterpolators(); + // When built on solaris with GCC, the destructors can be called + // in such an order that we get here with interpolators == NULL, + // to continue causes the app to crash on exit with a SEGV + if (interpolators) { #ifndef QT_NO_THREAD - QMutexLocker locker(QMutexPool::globalInstanceGet(interpolators)); + QMutexLocker locker(QMutexPool::globalInstanceGet(interpolators)); #endif - if (int(interpolationType) >= interpolators->count()) - interpolators->resize(int(interpolationType) + 1); - interpolators->replace(interpolationType, func); + if (int(interpolationType) >= interpolators->count()) + interpolators->resize(int(interpolationType) + 1); + interpolators->replace(interpolationType, func); + } } -- cgit v0.12 From db3d9b67ac543ddfdac649bec2c1287982d50a4e Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 20 Dec 2010 19:38:11 +0100 Subject: Change the D-Bus signal filter to return NOT_YET_HANDLED for signals --- src/dbus/qdbusintegrator.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index adc3408..2e7b052 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -558,8 +558,9 @@ bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) switch (amsg.type()) { case QDBusMessage::SignalMessage: handleSignal(amsg); - return true; - break; + // if there are any other filters in this DBusConnection, + // let them see the signal too + return false; case QDBusMessage::MethodCallMessage: handleObjectCall(amsg); return true; -- cgit v0.12 From 7012b6697df8472df5d772394edb0fbf7219da42 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 14 Jan 2011 20:15:37 +0100 Subject: QNAM FTP: switch to binary mode before sending a SIZE command With some servers, SIZE is not allowed in ASCII mode or it may return different sizes. Since we transfer in binary anyway, better get the size in binary too. Reviewed-by: Peter Hartmann --- src/network/access/qnetworkaccessftpbackend.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/network/access/qnetworkaccessftpbackend.cpp b/src/network/access/qnetworkaccessftpbackend.cpp index 42d2955..b0303aa 100644 --- a/src/network/access/qnetworkaccessftpbackend.cpp +++ b/src/network/access/qnetworkaccessftpbackend.cpp @@ -307,8 +307,10 @@ void QNetworkAccessFtpBackend::ftpDone() // logged in successfully, send the stat requests (if supported) QString command = url().path(); command.prepend(QLatin1String("%1 ")); - if (supportsSize) + if (supportsSize) { + ftp->rawCommand(QLatin1String("TYPE I")); sizeId = ftp->rawCommand(command.arg(QLatin1String("SIZE"))); // get size + } if (supportsMdtm) mdtmId = ftp->rawCommand(command.arg(QLatin1String("MDTM"))); // get modified time if (!supportsSize && !supportsMdtm) -- cgit v0.12 From b7de076b698a1e0a61c6b1597db860c88dd3ee86 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 21 Jan 2011 23:04:33 +0100 Subject: Fix warning in ODBC driver: passing NULL to non-pointer. Warning was: passing NULL to non-pointer argument 7 of 'SQLRETURN SQLGetDiagRecW(SQLSMALLINT, void*, SQLSMALLINT, SQLWCHAR*, SQLINTEGER*, SQLWCHAR*, SQLSMALLINT, SQLSMALLINT*)' --- src/sql/drivers/odbc/qsql_odbc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sql/drivers/odbc/qsql_odbc.cpp b/src/sql/drivers/odbc/qsql_odbc.cpp index c91aa54..8cff61b 100644 --- a/src/sql/drivers/odbc/qsql_odbc.cpp +++ b/src/sql/drivers/odbc/qsql_odbc.cpp @@ -223,7 +223,7 @@ static QString qWarnODBCHandle(int handleType, SQLHANDLE handle, int *nativeCode state_, &nativeCode_, 0, - NULL, + 0, &msgLen); if ((r == SQL_SUCCESS || r == SQL_SUCCESS_WITH_INFO) && msgLen > 0) description_.resize(msgLen+1); @@ -400,7 +400,7 @@ static QString qGetStringData(SQLHANDLE hStmt, int column, int colSize, bool uni // colSize-1: remove 0 termination when there is more data to fetch int rSize = (r == SQL_SUCCESS_WITH_INFO) ? colSize : lengthIndicator/sizeof(SQLTCHAR); fieldVal += fromSQLTCHAR(buf, rSize); - if (lengthIndicator < (unsigned int)colSize*sizeof(SQLTCHAR)) { + if ((unsigned)lengthIndicator < colSize*sizeof(SQLTCHAR)) { // workaround for Drivermanagers that don't return SQL_NO_DATA break; } -- cgit v0.12 From f34e2b62b03e35015b0eaa6b96dc982caf0f230d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 21 Jan 2011 23:05:37 +0100 Subject: Fix warnings in PSQL driver: handle VersionUnknown in switch --- src/sql/drivers/psql/qsql_psql.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sql/drivers/psql/qsql_psql.cpp b/src/sql/drivers/psql/qsql_psql.cpp index 0a7d5bf..a044c7f 100644 --- a/src/sql/drivers/psql/qsql_psql.cpp +++ b/src/sql/drivers/psql/qsql_psql.cpp @@ -1021,6 +1021,9 @@ QSqlIndex QPSQLDriver::primaryIndex(const QString& tablename) const stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from " "pg_namespace where pg_namespace.nspname = '%1') AND ").arg(schema)); break; + case QPSQLDriver::VersionUnknown: + qFatal("PSQL version is unknown"); + break; } i.exec(stmt.arg(tbl)); @@ -1110,6 +1113,9 @@ QSqlRecord QPSQLDriver::record(const QString& tablename) const stmt = stmt.arg(QString::fromLatin1("pg_class.relnamespace = (select oid from " "pg_namespace where pg_namespace.nspname = '%1')").arg(schema)); break; + case QPSQLDriver::VersionUnknown: + qFatal("PSQL version is unknown"); + break; } QSqlQuery query(createResult()); -- cgit v0.12 From 1d85e1ce92e6df1af97bde7542467f40f7003975 Mon Sep 17 00:00:00 2001 From: Zeno Albisser Date: Thu, 20 Jan 2011 14:54:06 +0100 Subject: Fix: define hotSpot for Gestures on Mac If there is no hotSpot defined for a gesture it cannot be propagated propperly within GraphicsView. Task-number: QTBUG-16618 Reviewed-by: Denis Dzyubenko --- examples/gestures/imagegestures/imagewidget.cpp | 1 + src/gui/kernel/qmacgesturerecognizer_mac.mm | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/examples/gestures/imagegestures/imagewidget.cpp b/examples/gestures/imagegestures/imagewidget.cpp index 12e6216..8bbb965 100644 --- a/examples/gestures/imagegestures/imagewidget.cpp +++ b/examples/gestures/imagegestures/imagewidget.cpp @@ -1,3 +1,4 @@ + /**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). diff --git a/src/gui/kernel/qmacgesturerecognizer_mac.mm b/src/gui/kernel/qmacgesturerecognizer_mac.mm index 0e432f3..6a4f0bb 100644 --- a/src/gui/kernel/qmacgesturerecognizer_mac.mm +++ b/src/gui/kernel/qmacgesturerecognizer_mac.mm @@ -69,6 +69,7 @@ QMacSwipeGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e case QNativeGestureEvent::Swipe: { QSwipeGesture *g = static_cast(gesture); g->setSwipeAngle(ev->angle); + g->setHotSpot(ev->position); return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; break; } default: @@ -110,6 +111,7 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e g->setCenterPoint(g->startCenterPoint()); g->setChangeFlags(QPinchGesture::CenterPointChanged); g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); return QGestureRecognizer::MayBeGesture | QGestureRecognizer::ConsumeEventHint; case QNativeGestureEvent::Rotate: { g->setLastScaleFactor(g->scaleFactor()); @@ -117,6 +119,7 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e g->setRotationAngle(g->rotationAngle() + ev->percentage); g->setChangeFlags(QPinchGesture::RotationAngleChanged); g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; } case QNativeGestureEvent::Zoom: @@ -125,6 +128,7 @@ QMacPinchGestureRecognizer::recognize(QGesture *gesture, QObject *obj, QEvent *e g->setScaleFactor(g->scaleFactor() * (1 + ev->percentage)); g->setChangeFlags(QPinchGesture::ScaleFactorChanged); g->setTotalChangeFlags(g->totalChangeFlags() | g->changeFlags()); + g->setHotSpot(ev->position); return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; case QNativeGestureEvent::GestureEnd: return QGestureRecognizer::FinishGesture | QGestureRecognizer::ConsumeEventHint; @@ -221,6 +225,7 @@ QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent * const QPointF posOffset = p - _startPos; g->setLastOffset(g->offset()); g->setOffset(QPointF(posOffset.x(), posOffset.y())); + g->setHotSpot(_startPos); return QGestureRecognizer::TriggerGesture; } } else if (_panTimer.isActive()) { @@ -239,6 +244,7 @@ QMacPanGestureRecognizer::recognize(QGesture *gesture, QObject *target, QEvent * break; // Begin new pan session! _startPos = QCursor::pos(); + g->setHotSpot(_startPos); return QGestureRecognizer::TriggerGesture | QGestureRecognizer::ConsumeEventHint; } break; } -- cgit v0.12 From bd6c9225328b6042ff14dfddb28e2e1279ba0e46 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 27 Jan 2011 10:01:48 +0100 Subject: Make syncqt not complain about missing header macros. qsharedpointer_impl.h contains class definitions that are also mirrored in qsharedpointer.h (which is there for qdoc3), so we have a macro to stop processing. Task-number: QTBUG-16912 Reviewed-by: Olivier Goffart --- src/corelib/tools/qsharedpointer_impl.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 2404d2a..0337c1f 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -44,7 +44,17 @@ #ifndef QSHAREDPOINTER_H #error Do not include qsharedpointer_impl.h directly #endif + #if 0 +// These macros are duplicated here to make syncqt not complain a about +// this header, as we have a "qt_sync_stop_processing" below, which in turn +// is here because this file contains a template mess and duplicates the +// classes found in qsharedpointer.h +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE +QT_MODULE(Core) +QT_END_NAMESPACE +QT_END_HEADER #pragma qt_sync_stop_processing #endif -- cgit v0.12 From 4ec245a3e75470186557d00b2383af3872a720b0 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 27 Jan 2011 12:40:33 +0200 Subject: No longer replace dash and dot in TARGET with underscore in Symbian There is no fundamental reason to not have dash or dot in binary names in Symbian, so do not replace them with underscore. One thing that doesn't work with a dot in the filename is launching an application via resources, so automatic resource generation is suppressed for applications that have a dot in filename portion of the TARGET value. Task-number: QTBUG-16888 Reviewed-by: axis --- mkspecs/common/symbian/symbian.conf | 9 +-- mkspecs/features/symbian/application_icon.prf | 106 ++++++++++++++------------ qmake/generators/symbian/symbiancommon.cpp | 23 ++---- qmake/generators/symbian/symbiancommon.h | 1 - qmake/generators/symbian/symmake.cpp | 31 ++++++-- 5 files changed, 89 insertions(+), 81 deletions(-) diff --git a/mkspecs/common/symbian/symbian.conf b/mkspecs/common/symbian/symbian.conf index 5619d4f..0bb3a29 100644 --- a/mkspecs/common/symbian/symbian.conf +++ b/mkspecs/common/symbian/symbian.conf @@ -215,19 +215,14 @@ default_deployment.pkg_prerules += pkg_depends_webkit pkg_depends_qt pkg_platfor DEPLOYMENT += default_deployment default_bin_deployment default_resource_deployment default_reg_deployment defineReplace(symbianRemoveSpecialCharacters) { - # Produce identical string to what SymbianCommonGenerator::removeSpecialCharacters and - # SymbianCommonGenerator::removeEpocSpecialCharacters produce + # Produce identical string to what SymbianCommonGenerator::removeSpecialCharacters fixedStr = $$1 fixedStr = $$replace(fixedStr, /,_) fixedStr = $$replace(fixedStr, \\\\,_) fixedStr = $$replace(fixedStr, " ",_) - symbian-abld|symbian-sbsv2 { - fixedStr = $$replace(fixedStr, -,_) - fixedStr = $$replace(fixedStr, \\.,_) - fixedStr = $$replace(fixedStr, :,_) - } + fixedStr = $$replace(fixedStr, :,_) return ($$fixedStr) } diff --git a/mkspecs/features/symbian/application_icon.prf b/mkspecs/features/symbian/application_icon.prf index 6e1aa8e..b6be89b 100644 --- a/mkspecs/features/symbian/application_icon.prf +++ b/mkspecs/features/symbian/application_icon.prf @@ -11,68 +11,74 @@ contains(CONFIG, no_icon) { !contains(CONFIG, no_icon) { baseTarget = $$symbianRemoveSpecialCharacters($$basename(TARGET)) - symbian-abld|symbian-sbsv2 { - resourceZDir = $$EPOCROOT$$HW_ZDIR$$APP_RESOURCE_DIR - regZDir = $$EPOCROOT$$HW_ZDIR$$REG_RESOURCE_IMPORT_DIR + contains(baseTarget, "^.*\\..*$") { + CONFIG += no_icon + ICON = + warning("Symbian resources do not support '.' character in TARGET, skipping resource generation.") } else { - contains(DESTDIR, "/.*") { - resourceZDir = $$DESTDIR - } else:isEmpty(DESTDIR) { - resourceZDir = $$OUT_PWD + symbian-abld|symbian-sbsv2 { + resourceZDir = $$EPOCROOT$$HW_ZDIR$$APP_RESOURCE_DIR + regZDir = $$EPOCROOT$$HW_ZDIR$$REG_RESOURCE_IMPORT_DIR } else { - resourceZDir = $$OUT_PWD/$$DESTDIR + contains(DESTDIR, "/.*") { + resourceZDir = $$DESTDIR + } else:isEmpty(DESTDIR) { + resourceZDir = $$OUT_PWD + } else { + resourceZDir = $$OUT_PWD/$$DESTDIR + } + regZDir = $$resourceZDir } - regZDir = $$resourceZDir - } - default_resource_deployment.sources += $$resourceZDir/$${baseTarget}.rsc - default_resource_deployment.path = $$APP_RESOURCE_DIR - default_reg_deployment.sources += $$regZDir/$${baseTarget}_reg.rsc - default_reg_deployment.path = $$REG_RESOURCE_IMPORT_DIR + default_resource_deployment.sources += $$resourceZDir/$${baseTarget}.rsc + default_resource_deployment.path = $$APP_RESOURCE_DIR + default_reg_deployment.sources += $$regZDir/$${baseTarget}_reg.rsc + default_reg_deployment.path = $$REG_RESOURCE_IMPORT_DIR - !isEmpty(ICON) { - !count(ICON, 1) { - ICON = $$first(ICON) - warning("Only first icon specified in ICON variable is used: $$ICON") - } + !isEmpty(ICON) { + !count(ICON, 1) { + ICON = $$first(ICON) + warning("Only first icon specified in ICON variable is used: $$ICON") + } - # Note: symbian-sbsv2 builds can't utilize extra compiler for mifconv, so ICON handling is done in code - !symbian-sbsv2 { - # Absolute path required for shadow builds. - # However, in older Symbian environments abld toolchain can't handle even moderately long - # paths, so don't force absolute there. - !symbian-abld:!contains(ICON, "^(/|\\\\|.:).*"):ICON = $$_PRO_FILE_PWD_/$$ICON + # Note: symbian-sbsv2 builds can't utilize extra compiler for mifconv, so ICON handling is done in code + !symbian-sbsv2 { + # Absolute path required for shadow builds. + # However, in older Symbian environments abld toolchain can't handle even moderately long + # paths, so don't force absolute there. + !symbian-abld:!contains(ICON, "^(/|\\\\|.:).*"):ICON = $$_PRO_FILE_PWD_/$$ICON - #Makefile: requires paths with backslash - ICON_backslashed = $$ICON + #Makefile: requires paths with backslash + ICON_backslashed = $$ICON - symbian-abld { - # ${ZDIR} is defined in Makefile - mifIconZDir = ${ZDIR}$$APP_RESOURCE_DIR - } else { - mifIconZDir = $$resourceZDir - } + symbian-abld { + # ${ZDIR} is defined in Makefile + mifIconZDir = ${ZDIR}$$APP_RESOURCE_DIR + } else { + mifIconZDir = $$resourceZDir + } - # Extra compiler rules for mifconv - mifconv.target = $$mifIconZDir/$${baseTarget}.mif - contains(QMAKE_HOST.os, "Windows") { - ICON_backslashed = $$replace(ICON_backslashed, /, \\) - mifconv.target = $$replace(mifconv.target, /, \\) + # Extra compiler rules for mifconv + mifconv.target = $$mifIconZDir/$${baseTarget}.mif + contains(QMAKE_HOST.os, "Windows") { + ICON_backslashed = $$replace(ICON_backslashed, /, \\) + mifconv.target = $$replace(mifconv.target, /, \\) + } + # Based on: http://www.forum.nokia.com/document/Cpp_Developers_Library + # svg-t icons should always use /c32 depth + mifconv.commands = mifconv $$mifconv.target /c32 $$ICON_backslashed + + mifconv.depends = $$ICON + PRE_TARGETDEPS += $$mifconv.target + QMAKE_EXTRA_TARGETS += mifconv + QMAKE_DISTCLEAN += $$mifconv.target } - # Based on: http://www.forum.nokia.com/document/Cpp_Developers_Library - # svg-t icons should always use /c32 depth - mifconv.commands = mifconv $$mifconv.target /c32 $$ICON_backslashed + # Rules to use generated MIF file from symbian resources + RSS_RULES.number_of_icons = $$size(ICON_backslashed) + RSS_RULES.icon_file = $$APP_RESOURCE_DIR/$${baseTarget}.mif - mifconv.depends = $$ICON - PRE_TARGETDEPS += $$mifconv.target - QMAKE_EXTRA_TARGETS += mifconv - QMAKE_DISTCLEAN += $$mifconv.target + default_resource_deployment.sources += $$resourceZDir/$${baseTarget}.mif } - # Rules to use generated MIF file from symbian resources - RSS_RULES.number_of_icons = $$size(ICON_backslashed) - RSS_RULES.icon_file = $$APP_RESOURCE_DIR/$${baseTarget}.mif - - default_resource_deployment.sources += $$resourceZDir/$${baseTarget}.mif } } diff --git a/qmake/generators/symbian/symbiancommon.cpp b/qmake/generators/symbian/symbiancommon.cpp index 2270c2e..96d7725 100644 --- a/qmake/generators/symbian/symbiancommon.cpp +++ b/qmake/generators/symbian/symbiancommon.cpp @@ -79,11 +79,7 @@ void SymbianCommonGenerator::init() fixedTarget = project->first("TARGET"); fixedTarget = generator->unescapeFilePath(fixedTarget); fixedTarget = removePathSeparators(fixedTarget); - if (project->first("MAKEFILE_GENERATOR") == "SYMBIAN_ABLD" - || project->first("MAKEFILE_GENERATOR") == "SYMBIAN_SBSV2") - removeEpocSpecialCharacters(fixedTarget); - else - removeSpecialCharacters(fixedTarget); + removeSpecialCharacters(fixedTarget); // This should not be empty since the mkspecs are supposed to set it if missing. uid3 = project->first("TARGET.UID3").trimmed(); @@ -131,18 +127,11 @@ bool SymbianCommonGenerator::containsStartWithItem(const QChar &c, const QString void SymbianCommonGenerator::removeSpecialCharacters(QString& str) { // When modifying this method check also symbianRemoveSpecialCharacters in symbian.conf - str.replace(QString("/"), QString("_")); - str.replace(QString("\\"), QString("_")); - str.replace(QString(" "), QString("_")); -} - -void SymbianCommonGenerator::removeEpocSpecialCharacters(QString& str) -{ - // When modifying this method check also symbianRemoveSpecialCharacters in symbian.conf - str.replace(QString("-"), QString("_")); - str.replace(QString(":"), QString("_")); - str.replace(QString("."), QString("_")); - removeSpecialCharacters(str); + QString underscore = QLatin1String("_"); + str.replace(QLatin1String("/"), underscore); + str.replace(QLatin1String("\\"), underscore); + str.replace(QLatin1String(" "), underscore); + str.replace(QLatin1String(":"), underscore); } QString romPath(const QString& path) diff --git a/qmake/generators/symbian/symbiancommon.h b/qmake/generators/symbian/symbiancommon.h index 0b5f53d..5182021 100644 --- a/qmake/generators/symbian/symbiancommon.h +++ b/qmake/generators/symbian/symbiancommon.h @@ -82,7 +82,6 @@ protected: QString removePathSeparators(QString &file); void removeSpecialCharacters(QString& str); - void removeEpocSpecialCharacters(QString& str); void generatePkgFile(const QString &iconFile, bool epocBuild, const SymbianLocalizationList &symbianLocalizationList); diff --git a/qmake/generators/symbian/symmake.cpp b/qmake/generators/symbian/symmake.cpp index a2b567d..4f9f22d 100644 --- a/qmake/generators/symbian/symmake.cpp +++ b/qmake/generators/symbian/symmake.cpp @@ -83,6 +83,8 @@ #define VAR_CFLAGS "QMAKE_CFLAGS" #define VAR_LFLAGS "QMAKE_LFLAGS" +#define DEFINE_REPLACE_REGEXP "[^A-Z0-9_]" + QString SymbianMakefileGenerator::fixPathForMmp(const QString& origPath, const QDir& parentDir) { static QString epocRootStr; @@ -165,11 +167,15 @@ void SymbianMakefileGenerator::writeHeader(QTextStream &t) QString bldinfDefine = shortProFilename; bldinfDefine.append("_"); bldinfDefine.append(generate_uid(project->projectFile())); + bldinfDefine = bldinfDefine.toUpper(); + + // replace anything not alphanumeric with underscore + QRegExp replacementMask(DEFINE_REPLACE_REGEXP); + bldinfDefine.replace(replacementMask, QLatin1String("_")); bldinfDefine.prepend("BLD_INF_"); - removeEpocSpecialCharacters(bldinfDefine); - t << "#define " << bldinfDefine.toUpper() << endl << endl; + t << "#define " << bldinfDefine << endl << endl; } bool SymbianMakefileGenerator::writeMakefile(QTextStream &t) @@ -902,13 +908,17 @@ void SymbianMakefileGenerator::writeBldInfContent(QTextStream &t, bool addDeploy const QStringList &subdirs = project->values("SUBDIRS"); foreach(QString item, subdirs) { + bool fromFile = false; QString fixedItem; if (!project->isEmpty(item + ".file")) { fixedItem = project->first(item + ".file"); + fromFile = true; } else if (!project->isEmpty(item + ".subdir")) { fixedItem = project->first(item + ".subdir"); + fromFile = false; } else { fixedItem = item; + fromFile = item.endsWith(Option::pro_ext); } QString condition; @@ -917,9 +927,15 @@ void SymbianMakefileGenerator::writeBldInfContent(QTextStream &t, bool addDeploy QFileInfo subdir(fileInfo(fixedItem)); QString relativePath = directory.relativeFilePath(fixedItem); - QString subdirFileName = subdir.completeBaseName(); - QString fullProName = subdir.absoluteFilePath();; + QString fullProName = subdir.absoluteFilePath(); QString bldinfFilename; + QString subdirFileName; + + if (fromFile) { + subdirFileName = subdir.completeBaseName(); + } else { + subdirFileName = subdir.fileName(); + } if (subdir.isDir()) { // Subdir is a regular project @@ -941,7 +957,10 @@ void SymbianMakefileGenerator::writeBldInfContent(QTextStream &t, bool addDeploy QString uid = generate_uid(fullProName); QString bldinfDefine = QString("BLD_INF_") + subdirFileName + QString("_") + uid; bldinfDefine = bldinfDefine.toUpper(); - removeEpocSpecialCharacters(bldinfDefine); + + // replace anything not alphanumeric with underscore + QRegExp replacementMask(DEFINE_REPLACE_REGEXP); + bldinfDefine.replace(replacementMask, QLatin1String("_")); if (!condition.isEmpty()) t << "#if defined(" << condition << ")" << endl; @@ -1124,4 +1143,4 @@ QString SymbianMakefileGenerator::generateLocFileTarget(QTextStream& t, const QS } return locFile; -} \ No newline at end of file +} -- cgit v0.12 From 2e72a8b19ea6c674fb4777860dac50faa5d387e6 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 27 Jan 2011 11:49:49 +0100 Subject: Restore Qt 4.6 behaviour: exec() always enters the event loop. In Qt 4.6 as well as 4.7's QCoreApplication and QEventLoop, calling exec() always enters the event loop, even if you had tried to quit()/exit() it before entering, with one exception (noted in the unit tests; this difference has been in Qt since at least Qt 4.2). Add unit tests to ensure all of the three classes have the same behaviour. Decide if we want to match the behaviours in Qt 4.8. Reviewed-by: Bradley T. Hughes --- src/corelib/thread/qthread.cpp | 5 +- .../auto/qcoreapplication/tst_qcoreapplication.cpp | 45 +++++ tests/auto/qeventloop/tst_qeventloop.cpp | 41 ++++- tests/auto/qthread/tst_qthread.cpp | 191 +++++++++++++++------ 4 files changed, 221 insertions(+), 61 deletions(-) diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index f368192..f4bfa5d 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -482,10 +482,7 @@ int QThread::exec() Q_D(QThread); QMutexLocker locker(&d->mutex); d->data->quitNow = false; - if (d->exited) { - d->exited = false; - return d->returnCode; - } + d->exited = false; locker.unlock(); QEventLoop eventLoop; diff --git a/tests/auto/qcoreapplication/tst_qcoreapplication.cpp b/tests/auto/qcoreapplication/tst_qcoreapplication.cpp index 95055d1..bc69461 100644 --- a/tests/auto/qcoreapplication/tst_qcoreapplication.cpp +++ b/tests/auto/qcoreapplication/tst_qcoreapplication.cpp @@ -59,6 +59,9 @@ private slots: void applicationPid(); void globalPostedEventsCount(); void processEventsAlwaysSendsPostedEvents(); + void reexec(); + void execAfterExit(); + void eventLoopExecAfterExit(); }; class EventSpy : public QObject @@ -524,5 +527,47 @@ void tst_QCoreApplication::processEventsAlwaysSendsPostedEvents() } while (t.elapsed() < 3000); } +void tst_QCoreApplication::reexec() +{ + int argc = 1; + char *argv[] = { "tst_qcoreapplication" }; + QCoreApplication app(argc, argv); + + // exec once + QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); + QCOMPARE(app.exec(), 0); + + // and again + QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); + QCOMPARE(app.exec(), 0); +} + +void tst_QCoreApplication::execAfterExit() +{ + int argc = 1; + char *argv[] = { "tst_qcoreapplication" }; + QCoreApplication app(argc, argv); + + app.exit(1); + QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); + QCOMPARE(app.exec(), 0); +} + +void tst_QCoreApplication::eventLoopExecAfterExit() +{ + int argc = 1; + char *argv[] = { "tst_qcoreapplication" }; + QCoreApplication app(argc, argv); + + // exec once and exit + QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); + QCOMPARE(app.exec(), 0); + + // and again, but this time using a QEventLoop + QEventLoop loop; + QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection); + QCOMPARE(loop.exec(), 0); +} + QTEST_APPLESS_MAIN(tst_QCoreApplication) #include "tst_qcoreapplication.moc" diff --git a/tests/auto/qeventloop/tst_qeventloop.cpp b/tests/auto/qeventloop/tst_qeventloop.cpp index 7af722f..6860f19 100644 --- a/tests/auto/qeventloop/tst_qeventloop.cpp +++ b/tests/auto/qeventloop/tst_qeventloop.cpp @@ -112,6 +112,10 @@ signals: public: QMutex mutex; QWaitCondition cond; + volatile int result1; + volatile int result2; + MultipleExecThread() : result1(0xdead), result2(0xbeef) {} + void run() { QMutexLocker locker(&mutex); @@ -124,13 +128,13 @@ public: connect(&timer, SIGNAL(timeout()), SLOT(quit()), Qt::DirectConnection); timer.setInterval(1000); timer.start(); - (void) exec(); + result1 = exec(); // this should return immediately, since exit() has been called cond.wakeOne(); cond.wait(&mutex); QEventLoop eventLoop; - (void) eventLoop.exec(); + result2 = eventLoop.exec(); } }; @@ -197,7 +201,9 @@ private slots: void symbianNestedActiveSchedulerLoop(); void processEvents(); void exec(); + void reexec(); void exit(); + void execAfterExit(); void wakeUp(); void quit(); void processEventsExcludeSocket(); @@ -398,7 +404,9 @@ void tst_QEventLoop::exec() } { - // calling exec() after exit()/quit() should return immediately + // calling QEventLoop::exec() after a thread loop has exit()ed should return immediately + // Note: this behaviour differs from QCoreApplication and QEventLoop + // see tst_QCoreApplication::eventLoopExecAfterExit, tst_QEventLoop::reexec MultipleExecThread thread; // start thread and wait for checkpoint @@ -411,6 +419,8 @@ void tst_QEventLoop::exec() thread.cond.wakeOne(); thread.cond.wait(&thread.mutex); QVERIFY(spy.count() > 0); + int v = thread.result1; + QCOMPARE(v, 0); // exec should return immediately spy.clear(); @@ -418,6 +428,8 @@ void tst_QEventLoop::exec() thread.mutex.unlock(); thread.wait(); QCOMPARE(spy.count(), 0); + v = thread.result2; + QCOMPARE(v, -1); } { @@ -462,9 +474,32 @@ void tst_QEventLoop::exec() #endif } +void tst_QEventLoop::reexec() +{ + QEventLoop loop; + + // exec once + QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection); + QCOMPARE(loop.exec(), 0); + + // and again + QMetaObject::invokeMethod(&loop, "quit", Qt::QueuedConnection); + QCOMPARE(loop.exec(), 0); +} + void tst_QEventLoop::exit() { DEPENDS_ON(exec()); } +void tst_QEventLoop::execAfterExit() +{ + QEventLoop loop; + EventLoopExiter obj(&loop); + + QMetaObject::invokeMethod(&obj, "exit", Qt::QueuedConnection); + loop.exit(1); + QCOMPARE(loop.exec(), 0); +} + void tst_QEventLoop::wakeUp() { EventLoopThread thread; diff --git a/tests/auto/qthread/tst_qthread.cpp b/tests/auto/qthread/tst_qthread.cpp index e6bf9ce..c7036e4 100644 --- a/tests/auto/qthread/tst_qthread.cpp +++ b/tests/auto/qthread/tst_qthread.cpp @@ -86,6 +86,7 @@ private slots: void start(); void terminate(); void quit(); + void execAfterQuit(); void wait(); void started(); void finished(); @@ -265,6 +266,34 @@ public: } }; +class ExecAfterQuitThreadHelper: public QObject +{ + Q_OBJECT + QThread *thr; +public: + ExecAfterQuitThreadHelper(QThread *thr) : thr(thr) {} +public slots: + void doIt() { thr->exit(0); } +}; + +class ExecAfterQuitThread: public QThread +{ +public: + int returnValue; + void run() + { + ExecAfterQuitThreadHelper obj(this); + + QMetaObject::invokeMethod(&obj, "doIt", Qt::QueuedConnection); + exit(1); + + // returnValue will be either 0 or 1, depending on which of the two + // above take effect. The correct value is 0, since exit(1) before + // exec() should have no effect + returnValue = exec(); + } +}; + tst_QThread::tst_QThread() { @@ -424,34 +453,52 @@ void tst_QThread::stackSize() void tst_QThread::exit() { - Exit_Thread thread; - thread.object = new Exit_Object; - thread.object->moveToThread(&thread); - thread.code = 42; - thread.result = 0; - QVERIFY(!thread.isFinished()); - QVERIFY(!thread.isRunning()); - QMutexLocker locker(&thread.mutex); - thread.start(); - QVERIFY(thread.isRunning()); - QVERIFY(!thread.isFinished()); - thread.cond.wait(locker.mutex()); - QVERIFY(thread.wait(five_minutes)); - QVERIFY(thread.isFinished()); - QVERIFY(!thread.isRunning()); - QCOMPARE(thread.result, thread.code); - delete thread.object; + { + Exit_Thread thread; + thread.object = new Exit_Object; + thread.object->moveToThread(&thread); + thread.code = 42; + thread.result = 0; + QVERIFY(!thread.isFinished()); + QVERIFY(!thread.isRunning()); - Exit_Thread thread2; - thread2.object = 0; - thread2.code = 53; - thread2.result = 0; - QMutexLocker locker2(&thread2.mutex); - thread2.start(); - thread2.exit(thread2.code); - thread2.cond.wait(locker2.mutex()); - QVERIFY(thread2.wait(five_minutes)); - QCOMPARE(thread2.result, thread2.code); + QMutexLocker locker(&thread.mutex); + thread.start(); + QVERIFY(thread.isRunning()); + QVERIFY(!thread.isFinished()); + // but the thread is not running the event loop yet (the mutex is locked) + + // start the event loop + thread.cond.wait(locker.mutex()); + + // the Exit_Object above will cause the thread to exit + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.isFinished()); + QVERIFY(!thread.isRunning()); + QCOMPARE(thread.result, thread.code); + delete thread.object; + } + + { + Exit_Thread thread2; + thread2.object = 0; + thread2.code = 53; + thread2.result = 0; + QMutexLocker locker2(&thread2.mutex); + thread2.start(); + + // the mutex is locked, so the thread has *not* started running the event loop yet + // this will do nothing: + thread2.exit(thread2.code); + + // the thread will now start running + thread2.cond.wait(locker2.mutex()); + + // this will cause it to exit now + thread2.exit(++thread2.code); + QVERIFY(thread2.wait(five_minutes)); + QCOMPARE(thread2.result, thread2.code); + } } void tst_QThread::start() @@ -498,32 +545,59 @@ void tst_QThread::terminate() void tst_QThread::quit() { - Quit_Thread thread; - thread.object = new Quit_Object; - thread.object->moveToThread(&thread); - thread.result = -1; - QVERIFY(!thread.isFinished()); - QVERIFY(!thread.isRunning()); - QMutexLocker locker(&thread.mutex); - thread.start(); - QVERIFY(thread.isRunning()); - QVERIFY(!thread.isFinished()); - thread.cond.wait(locker.mutex()); - QVERIFY(thread.wait(five_minutes)); - QVERIFY(thread.isFinished()); - QVERIFY(!thread.isRunning()); - QCOMPARE(thread.result, 0); - delete thread.object; + // very similar to exit() above + { + Quit_Thread thread; + thread.object = new Quit_Object; + thread.object->moveToThread(&thread); + thread.result = -1; + QVERIFY(!thread.isFinished()); + QVERIFY(!thread.isRunning()); - Quit_Thread thread2; - thread2.object = 0; - thread2.result = -1; - QMutexLocker locker2(&thread2.mutex); - thread2.start(); - thread2.quit(); - thread2.cond.wait(locker2.mutex()); - QVERIFY(thread2.wait(five_minutes)); - QCOMPARE(thread2.result, 0); + // start the thread, but keep the event loop from starting + // (while the mutex is locked) + QMutexLocker locker(&thread.mutex); + thread.start(); + QVERIFY(thread.isRunning()); + QVERIFY(!thread.isFinished()); + + // unlock the mutex and let the event loop run + // the Quit_Object above will cause the thread to quit + thread.cond.wait(locker.mutex()); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.isFinished()); + QVERIFY(!thread.isRunning()); + QCOMPARE(thread.result, 0); + delete thread.object; + } + + { + Quit_Thread thread2; + thread2.object = 0; + thread2.result = -1; + + // start the thread, but keep the event loop from starting + // (while the mutex is locked) + QMutexLocker locker2(&thread2.mutex); + thread2.start(); + thread2.quit(); // does nothing, the event loop is not running! + + // unlock the mutex and let the event loop run + thread2.cond.wait(locker2.mutex()); + + // there's no Quit_Object so it won't quit on its own + thread2.quit(); + QVERIFY(thread2.wait(five_minutes)); + QCOMPARE(thread2.result, 0); + } +} + +void tst_QThread::execAfterQuit() +{ + ExecAfterQuitThread thread; + thread.start(); + QVERIFY(thread.wait()); + QCOMPARE(thread.returnValue, 0); } void tst_QThread::wait() @@ -994,8 +1068,17 @@ void tst_QThread::QTBUG15378_exitAndExec() Thread thread; thread.value = 0; thread.start(); - thread.exit(556); - thread.sem1.release(); //should exit the first loop + thread.exit(42); // will do nothing, this value should not appear + thread.sem1.release(); //should enter the first loop + + Exit_Object *exit_object = new Exit_Object; + exit_object->code = 556; + exit_object->thread = &thread; + QMetaObject::invokeMethod(exit_object, "slot", Qt::QueuedConnection); + exit_object->deleteLater(); + exit_object->moveToThread(&thread); // should exit the first loop + exit_object = 0; + thread.sem2.acquire(); int v = thread.value; QCOMPARE(v, 556); -- cgit v0.12 From e0489b905d6a31c7a904ca2b62a1e60cd12dba4f Mon Sep 17 00:00:00 2001 From: mread Date: Tue, 25 Jan 2011 14:29:41 +0000 Subject: Orientation control implementation for Symbian This used the orientation control QWidget attributes API from maemo5, and provides a simple implementation for Symbian. The essense of the implementation is that the latest setting of one of these QWidget orientation attributes will set the orientation for the whole app. Testing the attributes will return only the last attribute set, it will not return the app orientation state. A new task, QTBUG-16972, has been created to provide a more comprehensive implementation in the future. This may provide a more effective emulation of the maemo5 behaviour, or may incorporate further reaching concepts for QML rotations. Task-number: QTBUG-11785 Reviewed-by: Shane Kearns --- src/corelib/global/qnamespace.h | 13 ++++++++++--- src/gui/kernel/qt_s60_p.h | 2 ++ src/gui/kernel/qwidget.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 4f3a742..e492345 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -511,9 +511,16 @@ public: #if 0 // these values are reserved for Maemo5 - do not re-use them WA_Maemo5NonComposited = 126, WA_Maemo5StackedWindow = 127, - WA_Maemo5PortraitOrientation = 128, - WA_Maemo5LandscapeOrientation = 129, - WA_Maemo5AutoOrientation = 130, +#endif + + WA_LockPortraitOrientation = 128, + WA_LockLandscapeOrientation = 129, + WA_AutoOrientation = 130, + +#if 0 // these values are reserved for Maemo5 - do not re-use them + WA_Maemo5PortraitOrientation = WA_LockPortraitOrientation, + WA_Maemo5LandscapeOrientation = WA_LockLandscapeOrientation, + WA_Maemo5AutoOrientation = WA_AutoOrientation, WA_Maemo5ShowProgressIndicator = 131, #endif diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index fdb35d5..40697bf 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -141,6 +141,7 @@ public: int supportsPremultipliedAlpha : 1; int avkonComponentsSupportTransparency : 1; int menuBeingConstructed : 1; + int orientationSet : 1; QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type static CEikButtonGroupContainer *cba; @@ -295,6 +296,7 @@ inline QS60Data::QS60Data() supportsPremultipliedAlpha(0), avkonComponentsSupportTransparency(0), menuBeingConstructed(0), + orientationSet(0), s60ApplicationFactory(0) #ifdef Q_OS_SYMBIAN ,s60InstalledTrapHandler(0) diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 9a76b0a..e542a59 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -124,6 +124,10 @@ #include "qtabwidget.h" // Needed in inTabWidget() #endif // QT_KEYPAD_NAVIGATION +#ifdef Q_WS_S60 +#include +#endif + // widget/widget data creation count //#define QWIDGET_EXTRA_DEBUG //#define ALIEN_DEBUG @@ -10810,6 +10814,42 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) d->registerTouchWindow(); #endif break; + case Qt::WA_LockPortraitOrientation: + case Qt::WA_LockLandscapeOrientation: + case Qt::WA_AutoOrientation: { + const Qt::WidgetAttribute orientations[3] = { + Qt::WA_LockPortraitOrientation, + Qt::WA_LockLandscapeOrientation, + Qt::WA_AutoOrientation + }; + + if (on) { + // We can only have one of these set at a time + for (int i = 0; i < 3; ++i) { + if (orientations[i] != attribute) + setAttribute_internal(orientations[i], false, data, d); + } + } + +#ifdef Q_WS_S60 + CAknAppUiBase* appUi = static_cast(CEikonEnv::Static()->EikAppUi()); + const CAknAppUiBase::TAppUiOrientation s60orientations[] = { + CAknAppUiBase::EAppUiOrientationPortrait, + CAknAppUiBase::EAppUiOrientationLandscape, + CAknAppUiBase::EAppUiOrientationAutomatic + }; + CAknAppUiBase::TAppUiOrientation s60orientation = CAknAppUiBase::EAppUiOrientationUnspecified; + for (int i = 0; i < 3; ++i) { + if (testAttribute(orientations[i])) { + s60orientation = s60orientations[i]; + break; + } + } + QT_TRAP_THROWING(appUi->SetOrientationL(s60orientation)); + S60->orientationSet = true; +#endif + break; + } default: break; } -- cgit v0.12 From ead20f4c1edc2e1c5c39f47bf7c9e56600d6362b Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Thu, 27 Jan 2011 16:29:52 +0100 Subject: Fix alignment issue causing crash in QtScript/JavaScriptCore When creating a substring, JSC::UStringImpl required that the base string pointer was 8-byte aligned. However, on platforms where FastMalloc isn't enabled (such as Symbian), it's possible that the system malloc() returns a pointer that is only 4-byte aligned. (On Symbian, this can happen if the argument to malloc() itself isn't a multiple of 8.) Cherry-picked http://trac.webkit.org/changeset/54743 from WebKit trunk, which fixes this issue. (The commit happened shortly after we rebased QtScript/JSC for 4.7, so it applies cleanly to our copy.) Task-number: QTBUG-16828 Reviewed-by: Simon Hausmann --- .../javascriptcore/JavaScriptCore/ChangeLog | 25 +++++ .../JavaScriptCore/runtime/UStringImpl.cpp | 14 +-- .../JavaScriptCore/runtime/UStringImpl.h | 117 ++++++++------------- src/3rdparty/javascriptcore/VERSION | 4 +- 4 files changed, 76 insertions(+), 84 deletions(-) diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/ChangeLog b/src/3rdparty/javascriptcore/JavaScriptCore/ChangeLog index c2b1155..9cbf0c1 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/ChangeLog +++ b/src/3rdparty/javascriptcore/JavaScriptCore/ChangeLog @@ -358,6 +358,31 @@ * wtf/AlwaysInline.h: +2010-02-12 Gavin Barraclough + + Reviewed by Darin Adler. + + https://bugs.webkit.org/show_bug.cgi?id=33731 + Many false leaks in release builds due to PtrAndFlags + + Remove UntypedPtrAndBitfield (similar to PtrAndFlags) in UStringImpl, + and steal bits from the refCount instead. + + * runtime/UStringImpl.cpp: + (JSC::UStringImpl::baseSharedBuffer): + (JSC::UStringImpl::~UStringImpl): + * runtime/UStringImpl.h: + (JSC::UStringImpl::cost): + (JSC::UStringImpl::isIdentifier): + (JSC::UStringImpl::setIsIdentifier): + (JSC::UStringImpl::ref): + (JSC::UStringImpl::deref): + (JSC::UStringImpl::UStringImpl): + (JSC::UStringImpl::bufferOwnerString): + (JSC::UStringImpl::bufferOwnership): + (JSC::UStringImpl::isStatic): + (JSC::UStringImpl::): + 2010-02-12 Kwang Yul Seo Reviewed by Adam Barth. diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.cpp b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.cpp index 4b0d1c9..4fde49e 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.cpp +++ b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.cpp @@ -38,12 +38,14 @@ namespace JSC { SharedUChar* UStringImpl::baseSharedBuffer() { ASSERT((bufferOwnership() == BufferShared) - || ((bufferOwnership() == BufferOwned) && !m_dataBuffer.asPtr())); + || ((bufferOwnership() == BufferOwned) && !m_buffer)); - if (bufferOwnership() != BufferShared) - m_dataBuffer = UntypedPtrAndBitfield(SharedUChar::create(new OwnFastMallocPtr(m_data)).releaseRef(), BufferShared); + if (bufferOwnership() != BufferShared) { + m_refCountAndFlags = (m_refCountAndFlags & ~s_refCountMaskBufferOwnership) | BufferShared; + m_bufferShared = SharedUChar::create(new OwnFastMallocPtr(m_data)).releaseRef(); + } - return m_dataBuffer.asPtr(); + return m_bufferShared; } SharedUChar* UStringImpl::sharedBuffer() @@ -71,10 +73,10 @@ UStringImpl::~UStringImpl() if (bufferOwnership() == BufferOwned) fastFree(m_data); else if (bufferOwnership() == BufferSubstring) - m_dataBuffer.asPtr()->deref(); + m_bufferSubstring->deref(); else { ASSERT(bufferOwnership() == BufferShared); - m_dataBuffer.asPtr()->deref(); + m_bufferShared->deref(); } } } diff --git a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.h b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.h index 4e1ddc7..e6d1a8a 100644 --- a/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.h +++ b/src/3rdparty/javascriptcore/JavaScriptCore/runtime/UStringImpl.h @@ -40,48 +40,6 @@ class IdentifierTable; typedef CrossThreadRefCounted > SharedUChar; -class UntypedPtrAndBitfield { -public: - UntypedPtrAndBitfield() {} - - UntypedPtrAndBitfield(void* ptrValue, uintptr_t bitValue) - : m_value(reinterpret_cast(ptrValue) | bitValue) -#ifndef NDEBUG - , m_leaksPtr(ptrValue) -#endif - { - ASSERT(ptrValue == asPtr()); - ASSERT((*this & ~s_alignmentMask) == bitValue); - } - - template - T asPtr() const { return reinterpret_cast(m_value & s_alignmentMask); } - - UntypedPtrAndBitfield& operator&=(uintptr_t bits) - { - m_value &= bits | s_alignmentMask; - return *this; - } - - UntypedPtrAndBitfield& operator|=(uintptr_t bits) - { - m_value |= bits & ~s_alignmentMask; - return *this; - } - - uintptr_t operator&(uintptr_t mask) const - { - return m_value & mask & ~s_alignmentMask; - } - -private: - static const uintptr_t s_alignmentMask = ~static_cast(0x7); - uintptr_t m_value; -#ifndef NDEBUG - void* m_leaksPtr; // Only used to allow tools like leaks on OSX to detect that the memory is referenced. -#endif -}; - class UStringImpl : Noncopyable { public: template @@ -151,21 +109,27 @@ public: { // For substrings, return the cost of the base string. if (bufferOwnership() == BufferSubstring) - return m_dataBuffer.asPtr()->cost(); + return m_bufferSubstring->cost(); - if (m_dataBuffer & s_reportedCostBit) + if (m_refCountAndFlags & s_refCountFlagHasReportedCost) return 0; - m_dataBuffer |= s_reportedCostBit; + m_refCountAndFlags |= s_refCountFlagHasReportedCost; return m_length; } unsigned hash() const { if (!m_hash) m_hash = computeHash(data(), m_length); return m_hash; } unsigned existingHash() const { ASSERT(m_hash); return m_hash; } // fast path for Identifiers void setHash(unsigned hash) { ASSERT(hash == computeHash(data(), m_length)); m_hash = hash; } // fast path for Identifiers - bool isIdentifier() const { return m_isIdentifier; } - void setIsIdentifier(bool isIdentifier) { m_isIdentifier = isIdentifier; } + bool isIdentifier() const { return m_refCountAndFlags & s_refCountFlagIsIdentifier; } + void setIsIdentifier(bool isIdentifier) + { + if (isIdentifier) + m_refCountAndFlags |= s_refCountFlagIsIdentifier; + else + m_refCountAndFlags &= ~s_refCountFlagIsIdentifier; + } - UStringImpl* ref() { m_refCount += s_refCountIncrement; return this; } - ALWAYS_INLINE void deref() { if (!(m_refCount -= s_refCountIncrement)) delete this; } + UStringImpl* ref() { m_refCountAndFlags += s_refCountIncrement; return this; } + ALWAYS_INLINE void deref() { m_refCountAndFlags -= s_refCountIncrement; if (!(m_refCountAndFlags & s_refCountMask)) delete this; } static void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) { @@ -205,11 +169,10 @@ private: // Used to construct normal strings with an internal or external buffer. UStringImpl(UChar* data, int length, BufferOwnership ownership) : m_data(data) + , m_buffer(0) , m_length(length) - , m_refCount(s_refCountIncrement) + , m_refCountAndFlags(s_refCountIncrement | ownership) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(0, ownership) { ASSERT((ownership == BufferInternal) || (ownership == BufferOwned)); checkConsistency(); @@ -221,11 +184,10 @@ private: enum StaticStringConstructType { ConstructStaticString }; UStringImpl(UChar* data, int length, StaticStringConstructType) : m_data(data) + , m_buffer(0) , m_length(length) - , m_refCount(s_staticRefCountInitialValue) + , m_refCountAndFlags(s_refCountFlagStatic | BufferOwned) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(0, BufferOwned) { checkConsistency(); } @@ -233,28 +195,26 @@ private: // Used to create new strings that are a substring of an existing string. UStringImpl(UChar* data, int length, PassRefPtr base) : m_data(data) + , m_bufferSubstring(base.releaseRef()) , m_length(length) - , m_refCount(s_refCountIncrement) + , m_refCountAndFlags(s_refCountIncrement | BufferSubstring) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(base.releaseRef(), BufferSubstring) { // Do use static strings as a base for substrings; UntypedPtrAndBitfield assumes // that all pointers will be at least 8-byte aligned, we cannot guarantee that of // UStringImpls that are not heap allocated. - ASSERT(m_dataBuffer.asPtr()->size()); - ASSERT(!m_dataBuffer.asPtr()->isStatic()); + ASSERT(m_bufferSubstring->size()); + ASSERT(!m_bufferSubstring->isStatic()); checkConsistency(); } // Used to construct new strings sharing an existing shared buffer. UStringImpl(UChar* data, int length, PassRefPtr sharedBuffer) : m_data(data) + , m_bufferShared(sharedBuffer.releaseRef()) , m_length(length) - , m_refCount(s_refCountIncrement) + , m_refCountAndFlags(s_refCountIncrement | BufferShared) , m_hash(0) - , m_isIdentifier(false) - , m_dataBuffer(sharedBuffer.releaseRef(), BufferShared) { checkConsistency(); } @@ -277,26 +237,31 @@ private: // This number must be at least 2 to avoid sharing empty, null as well as 1 character strings from SmallStrings. static const int s_minLengthToShare = 10; static const unsigned s_copyCharsInlineCutOff = 20; - static const uintptr_t s_bufferOwnershipMask = 3; - static const uintptr_t s_reportedCostBit = 4; // We initialize and increment/decrement the refCount for all normal (non-static) strings by the value 2. // We initialize static strings with an odd number (specifically, 1), such that the refCount cannot reach zero. - static const int s_refCountIncrement = 2; - static const int s_staticRefCountInitialValue = 1; - - UStringImpl* bufferOwnerString() { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr() : this; } - const UStringImpl* bufferOwnerString() const { return (bufferOwnership() == BufferSubstring) ? m_dataBuffer.asPtr() : this; } + static const unsigned s_refCountMask = 0xFFFFFFF0; + static const int s_refCountIncrement = 0x20; + static const int s_refCountFlagStatic = 0x10; + static const unsigned s_refCountFlagHasReportedCost = 0x8; + static const unsigned s_refCountFlagIsIdentifier = 0x4; + static const unsigned s_refCountMaskBufferOwnership = 0x3; + + UStringImpl* bufferOwnerString() { return (bufferOwnership() == BufferSubstring) ? m_bufferSubstring : this; } + const UStringImpl* bufferOwnerString() const { return (bufferOwnership() == BufferSubstring) ? m_bufferSubstring : this; } SharedUChar* baseSharedBuffer(); - unsigned bufferOwnership() const { return m_dataBuffer & s_bufferOwnershipMask; } - bool isStatic() const { return m_refCount & 1; } + unsigned bufferOwnership() const { return m_refCountAndFlags & s_refCountMaskBufferOwnership; } + bool isStatic() const { return m_refCountAndFlags & s_refCountFlagStatic; } // unshared data UChar* m_data; + union { + void* m_buffer; + UStringImpl* m_bufferSubstring; + SharedUChar* m_bufferShared; + }; int m_length; - unsigned m_refCount; - mutable unsigned m_hash : 31; - mutable unsigned m_isIdentifier : 1; - UntypedPtrAndBitfield m_dataBuffer; + unsigned m_refCountAndFlags; + mutable unsigned m_hash; JS_EXPORTDATA static UStringImpl* s_null; JS_EXPORTDATA static UStringImpl* s_empty; diff --git a/src/3rdparty/javascriptcore/VERSION b/src/3rdparty/javascriptcore/VERSION index b4744b7..13943b2 100644 --- a/src/3rdparty/javascriptcore/VERSION +++ b/src/3rdparty/javascriptcore/VERSION @@ -4,8 +4,8 @@ This is a snapshot of JavaScriptCore from The commit imported was from the - javascriptcore-snapshot-24012011 branch/tag + javascriptcore-snapshot-27012011 branch/tag and has the sha1 checksum - d143bde5ae8cff229aebd43487a2fce5e713e990 + 3ab0f621048fbeb480b687a28ed31d92d8506150 -- cgit v0.12 From b127b1036ec75c625920a6c029b64a95e3702bf9 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 28 Jan 2011 10:10:31 +0100 Subject: Invalidate QScriptPrograms when engine is destroyed If the engine is destroyed before the program, the program must be invalidated; otherwise the program destructor will access a stale engine pointer, which can cause a crash (it crashes on Symbian, but "only" gives a Valgrind warning on Linux for our autotests). We need to keep track of all associated programs, just like we already do for values and strings. This fix follows the exact same pattern, but uses a QSet to keep the patch minimal. No new tests, but the evaluateProgram() test runs successfully on Symbian now, and there are no more Valgrind warnings. Task-number: QTBUG-16987 Reviewed-by: Olivier Goffart --- src/script/api/qscriptengine.cpp | 9 +++++++++ src/script/api/qscriptengine_p.h | 18 ++++++++++++++++++ src/script/api/qscriptprogram.cpp | 21 ++++++++++++++------- src/script/api/qscriptprogram_p.h | 1 + 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp index 9e338d4..54039c0 100644 --- a/src/script/api/qscriptengine.cpp +++ b/src/script/api/qscriptengine.cpp @@ -1022,6 +1022,7 @@ QScriptEnginePrivate::~QScriptEnginePrivate() while (!ownedAgents.isEmpty()) delete ownedAgents.takeFirst(); + detachAllRegisteredScriptPrograms(); detachAllRegisteredScriptValues(); detachAllRegisteredScriptStrings(); qDeleteAll(m_qobjectData); @@ -1576,6 +1577,14 @@ bool QScriptEnginePrivate::scriptDisconnect(JSC::JSValue signal, JSC::JSValue re #endif +void QScriptEnginePrivate::detachAllRegisteredScriptPrograms() +{ + QSet::const_iterator it; + for (it = registeredScriptPrograms.constBegin(); it != registeredScriptPrograms.constEnd(); ++it) + (*it)->detachFromEngine(); + registeredScriptPrograms.clear(); +} + void QScriptEnginePrivate::detachAllRegisteredScriptValues() { QScriptValuePrivate *it; diff --git a/src/script/api/qscriptengine_p.h b/src/script/api/qscriptengine_p.h index 05a8901..f8144e9 100644 --- a/src/script/api/qscriptengine_p.h +++ b/src/script/api/qscriptengine_p.h @@ -87,6 +87,7 @@ class QScriptEngineAgent; class QScriptEnginePrivate; class QScriptSyntaxCheckResult; class QScriptEngine; +class QScriptProgramPrivate; namespace QScript { @@ -273,6 +274,10 @@ public: static QScriptSyntaxCheckResult checkSyntax(const QString &program); static bool canEvaluate(const QString &program); + inline void registerScriptProgram(QScriptProgramPrivate *program); + inline void unregisterScriptProgram(QScriptProgramPrivate *program); + void detachAllRegisteredScriptPrograms(); + inline QScriptValuePrivate *allocateScriptValuePrivate(size_t); inline void freeScriptValuePrivate(QScriptValuePrivate *p); @@ -368,6 +373,7 @@ public: static const int maxFreeScriptValues = 256; int freeScriptValuesCount; QScriptStringPrivate *registeredScriptStrings; + QSet registeredScriptPrograms; QHash m_typeInfos; int processEventsInterval; QScriptValue abortResult; @@ -566,6 +572,18 @@ inline QByteArray convertToLatin1(const JSC::UString &str) } // namespace QScript +inline void QScriptEnginePrivate::registerScriptProgram(QScriptProgramPrivate *program) +{ + Q_ASSERT(!registeredScriptPrograms.contains(program)); + registeredScriptPrograms.insert(program); +} + +inline void QScriptEnginePrivate::unregisterScriptProgram(QScriptProgramPrivate *program) +{ + Q_ASSERT(registeredScriptPrograms.contains(program)); + registeredScriptPrograms.remove(program); +} + inline QScriptValuePrivate *QScriptEnginePrivate::allocateScriptValuePrivate(size_t size) { if (freeScriptValues) { diff --git a/src/script/api/qscriptprogram.cpp b/src/script/api/qscriptprogram.cpp index da103bb..31af9a0 100644 --- a/src/script/api/qscriptprogram.cpp +++ b/src/script/api/qscriptprogram.cpp @@ -64,6 +64,7 @@ QScriptProgramPrivate::~QScriptProgramPrivate() if (engine) { QScript::APIShim shim(engine); _executable.clear(); + engine->unregisterScriptProgram(this); } } @@ -78,7 +79,10 @@ JSC::EvalExecutable *QScriptProgramPrivate::executable(JSC::ExecState *exec, if (_executable) { if (eng == engine) return _executable.get(); - _executable = 0; + // "Migrating" to another engine; clean up old state + QScript::APIShim shim(engine); + _executable.clear(); + engine->unregisterScriptProgram(this); } WTF::PassRefPtr provider = QScript::UStringSourceProviderWithFeedback::create(sourceCode, fileName, firstLineNumber, eng); @@ -86,10 +90,19 @@ JSC::EvalExecutable *QScriptProgramPrivate::executable(JSC::ExecState *exec, JSC::SourceCode source(provider, firstLineNumber); //after construction of SourceCode provider variable will be null. _executable = JSC::EvalExecutable::create(exec, source); engine = eng; + engine->registerScriptProgram(this); isCompiled = false; return _executable.get(); } +void QScriptProgramPrivate::detachFromEngine() +{ + _executable.clear(); + sourceId = -1; + isCompiled = false; + engine = 0; +} + /*! Constructs a null QScriptProgram. */ @@ -122,9 +135,6 @@ QScriptProgram::QScriptProgram(const QScriptProgram &other) */ QScriptProgram::~QScriptProgram() { - // Q_D(QScriptProgram); - // if (d->engine && (d->ref == 1)) - // d->engine->unregisterScriptProgram(d); } /*! @@ -132,9 +142,6 @@ QScriptProgram::~QScriptProgram() */ QScriptProgram &QScriptProgram::operator=(const QScriptProgram &other) { - // if (d_func() && d_func()->engine && (d_func()->ref == 1)) - // d_func()->engine->unregisterScriptProgram(d_func()); - // } d_ptr = other.d_ptr; return *this; } diff --git a/src/script/api/qscriptprogram_p.h b/src/script/api/qscriptprogram_p.h index d2fd234..e7809ab 100644 --- a/src/script/api/qscriptprogram_p.h +++ b/src/script/api/qscriptprogram_p.h @@ -61,6 +61,7 @@ public: JSC::EvalExecutable *executable(JSC::ExecState *exec, QScriptEnginePrivate *engine); + void detachFromEngine(); QBasicAtomicInt ref; -- cgit v0.12 From 298b54b4fa99db47d62ec37eacefb4f419b79a69 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Fri, 28 Jan 2011 11:12:50 +0100 Subject: Revert "Improve timer ID safety by using a serial counter per ID." This reverts commit 121e2b39043a4ffc6583f250aebb9a3a746076c1. It was considered too dangerous for 4.7. --- src/corelib/kernel/qabstracteventdispatcher.cpp | 30 +++++-------------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp index cc08f092..e79f87a 100644 --- a/src/corelib/kernel/qabstracteventdispatcher.cpp +++ b/src/corelib/kernel/qabstracteventdispatcher.cpp @@ -137,12 +137,6 @@ void QAbstractEventDispatcherPrivate::init() // free list). As an added protection, we use the cell to store an invalid // (negative) value that we can later check for integrity. // -// ABA prevention simply adds a value to 7 of the top 8 bits when resetting -// nextFreeTimerId. -// -// The extra code is the bucket allocation which allows us to start with a -// very small bucket size and grow as needed. -// // (continues below). int QAbstractEventDispatcherPrivate::allocateTimerId() { @@ -170,8 +164,6 @@ int QAbstractEventDispatcherPrivate::allocateTimerId() newTimerId = prepareNewValueWithSerialNumber(timerId, b[at]); } while (!nextFreeTimerId.testAndSetRelaxed(timerId, newTimerId)); - timerId &= TimerIdMask; - timerId |= b[at] & TimerSerialMask; b[at] = -timerId; return timerId; @@ -182,13 +174,12 @@ int QAbstractEventDispatcherPrivate::allocateTimerId() // X[timerId] = nextFreeTimerId; // then we update nextFreeTimerId to the timer we've just released // +// The extra code in allocateTimerId and releaseTimerId are ABA prevention +// and bucket memory. The buckets are simply to make sure we allocate only +// the necessary number of timers. See above. +// // ABA prevention simply adds a value to 7 of the top 8 bits when resetting // nextFreeTimerId. -// -// In addition to that, we update the same 7 bits in each entry in the bucket -// as a counter. That way, a timer ID allocated and released will always be -// returned with a different ID. This reduces the chances of timers being released -// erroneously by application code. void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId) { int which = timerId & TimerIdMask; @@ -196,21 +187,12 @@ void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId) int at = bucketIndex(bucket, which); int *b = timerIds[bucket]; -#ifndef QT_NO_DEBUG - // debug code - Q_ASSERT_X(timerId == -b[at], "QAbstractEventDispatcher::releaseTimerId", "Timer ID was not found, fix application"); -#else - if (timerId != -b[at]) { - // release code - qWarning("Timer ID %d was not found, fix application", timerId); - return; - } -#endif + Q_ASSERT(b[at] == -timerId); int freeId, newTimerId; do { freeId = nextFreeTimerId;//.loadAcquire(); // ### FIXME Proper memory ordering semantics - b[at] = prepareNewValueWithSerialNumber(-b[at], freeId); + b[at] = freeId & TimerIdMask; newTimerId = prepareNewValueWithSerialNumber(freeId, timerId); } while (!nextFreeTimerId.testAndSetRelease(freeId, newTimerId)); -- cgit v0.12 From 97503dfd7fad9c8023b2f9632e6ef865ccd0cad8 Mon Sep 17 00:00:00 2001 From: Jani Hautakangas Date: Fri, 28 Jan 2011 13:12:47 +0200 Subject: Fix typo in qglthreads auto test. Reviewed-by: TRUSTME --- tests/auto/qglthreads/tst_qglthreads.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/qglthreads/tst_qglthreads.cpp b/tests/auto/qglthreads/tst_qglthreads.cpp index 0bcc4f6..65bebb0 100644 --- a/tests/auto/qglthreads/tst_qglthreads.cpp +++ b/tests/auto/qglthreads/tst_qglthreads.cpp @@ -216,7 +216,7 @@ public: // That's why we create only small textures. width = 50; height = 20; -#else +#endif QImage image(width, height, QImage::Format_RGB32); QPainter p(&image); p.fillRect(image.rect(), QColor(rand() % 256, rand() % 256, rand() % 256)); -- cgit v0.12 From bdf3782b40b0fc2ebfda960be08c90b549cfd970 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 28 Jan 2011 12:23:08 +0100 Subject: Fix potential networking crash due to null-pointer dereference An internal bug report suggests that we unconditionally dereference the backend pointer in QNetworkReplyImpl when checking for the synchronity of the originating request. The dereferencing code was introduced in commit ad1e82323225e996720136e8b2d669166b8d8441. Unfortunately the report does not detail where/how the crash happened, but it appears plausible that the backend pointer became null, and the surrounding code that has extra checks suggests this, too. In an attempt of defensive programming this patch introduces the missing check in the reported line 112 as well as in other places where it seems appropriate. Reviewed-by: Peter Hartmann --- src/network/access/qnetworkreplyimpl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 9d7082c..343f344 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -109,7 +109,7 @@ void QNetworkReplyImplPrivate::_q_startOperation() } #endif - if (backend->isSynchronous()) { + if (backend && backend->isSynchronous()) { state = Finished; } else { if (state != Finished) { @@ -296,7 +296,7 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const // in QtWebKit. QVariant synchronousHttpAttribute = req.attribute( static_cast(QNetworkRequest::DownloadBufferAttribute + 1)); - if (synchronousHttpAttribute.toBool()) { + if (backend && synchronousHttpAttribute.toBool()) { backend->setSynchronous(true); if (outgoingData && outgoingData->isSequential()) { outgoingDataBuffer = new QRingBuffer(); @@ -351,7 +351,7 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); } #else - if (backend->isSynchronous()) + if (backend && backend->isSynchronous()) _q_startOperation(); else QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); -- cgit v0.12 From be4d3182e48dcae70837f26d76dd449d914e8cb3 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 28 Jan 2011 16:32:33 +0200 Subject: Fix generated mif file cleaning in symbian-abld Mif file cleaning was incorrectly done at distclean phase. Task-number: QTBUG-16893 Reviewed-by: axis --- mkspecs/features/symbian/application_icon.prf | 2 +- qmake/generators/symbian/symmake_abld.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mkspecs/features/symbian/application_icon.prf b/mkspecs/features/symbian/application_icon.prf index b6be89b..06f5b31 100644 --- a/mkspecs/features/symbian/application_icon.prf +++ b/mkspecs/features/symbian/application_icon.prf @@ -71,7 +71,7 @@ contains(CONFIG, no_icon) { mifconv.depends = $$ICON PRE_TARGETDEPS += $$mifconv.target QMAKE_EXTRA_TARGETS += mifconv - QMAKE_DISTCLEAN += $$mifconv.target + QMAKE_CLEAN += $$mifconv.target } # Rules to use generated MIF file from symbian resources RSS_RULES.number_of_icons = $$size(ICON_backslashed) diff --git a/qmake/generators/symbian/symmake_abld.cpp b/qmake/generators/symbian/symmake_abld.cpp index 5729e26..b582257 100644 --- a/qmake/generators/symbian/symmake_abld.cpp +++ b/qmake/generators/symbian/symmake_abld.cpp @@ -190,6 +190,7 @@ void SymbianAbldMakefileGenerator::writeWrapperMakefile(QFile& wrapperFile, bool t << "QMAKE = " << var("QMAKE_QMAKE") << endl; t << "DEL_FILE = " << var("QMAKE_DEL_FILE") << endl; t << "DEL_DIR = " << var("QMAKE_DEL_DIR") << endl; + t << "DEL_TREE = " << var("QMAKE_DEL_TREE") << endl; t << "MOVE = " << var("QMAKE_MOVE") << endl; t << "CHK_DIR_EXISTS = " << var("QMAKE_CHK_DIR_EXISTS") << endl; t << "MKDIR = " << var("QMAKE_MKDIR") << endl; @@ -329,7 +330,8 @@ void SymbianAbldMakefileGenerator::writeWrapperMakefile(QFile& wrapperFile, bool // Note: EXTENSION_CLEAN will get called many times when doing reallyclean // This is why the "2> NUL" gets appended to generated clean targets in makefile.cpp. t << EXTENSION_CLEAN ": " COMPILER_CLEAN_TARGET << endl; - generateCleanCommands(t, dirsToClean, var("QMAKE_DEL_TREE"), "", "", ""); + generateCleanCommands(t, dirsToClean, "$(DEL_TREE)", "", "", ""); + generateCleanCommands(t, project->values("QMAKE_CLEAN"), "$(DEL_FILE)", "", "", ""); t << endl; t << PRE_TARGETDEPS_TARGET ":" -- cgit v0.12 From 0e456bf74dfea10c0f5c164eb5a920e4661a91b2 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 1 Feb 2011 13:15:28 +0100 Subject: Fix a crash when undoing form layout In a rare case when breaking a layout might make widgets overlap, the internal heuristic failed when recreating an original layout during undo. Some widgets were removed from the grid. The patch fixes this heuristic (makes sure we don't remove other widgets). Creating a form layout from overlapping widgets works better now. Reviewed-by: Friedemann Kleint Task-number: QTCREATORBUG-3616 --- tools/designer/src/lib/shared/layout.cpp | 22 ++++++++++++++-------- tools/designer/src/lib/shared/qlayout_widget.cpp | 3 +-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tools/designer/src/lib/shared/layout.cpp b/tools/designer/src/lib/shared/layout.cpp index c9ffe71..9fe438b 100644 --- a/tools/designer/src/lib/shared/layout.cpp +++ b/tools/designer/src/lib/shared/layout.cpp @@ -996,13 +996,23 @@ bool Grid::shrinkFormLayoutSpans() for (WidgetSet::const_iterator it = widgets.constBegin(); it != cend ; ++it) { QWidget *w = *it; int row, col, rowspan, colspan; - locateWidget(w, row, col, rowspan, colspan); + if (!locateWidget(w, row, col, rowspan, colspan)) + qDebug("ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData()); const int maxColSpan = col == 0 ? 2 : 1; const int newColSpan = qMin(colspan, maxColSpan); const int newRowSpan = qMin(rowspan, maxRowSpan); if (newColSpan != colspan || newRowSpan != rowspan) { - setCells(QRect(col, row, colspan, rowspan), 0); - setCells(QRect(col, row, newColSpan, newRowSpan), w); + // in case like this: + // W1 W1 + // W1 W2 + // do: + // W1 0 + // 0 W2 + for (int i = row; i < row + rowspan - 1; i++) + for (int j = col; j < col + colspan - 1; j++) + if (i > row + newColSpan - 1 || j > col + newRowSpan - 1) + if (cell(i, j) == w) + setCell(i, j, 0); shrunk = true; } } @@ -1177,11 +1187,7 @@ void GridLayout::doLayout() if (const Spacer *spacer = qobject_cast(w)) alignment = spacer->alignment(); - if (rs * cs == 1) { - addWidgetToGrid(layout, w, r, c, 1, 1, alignment); - } else { - addWidgetToGrid(layout, w, r, c, rs, cs, alignment); - } + addWidgetToGrid(layout, w, r, c, rs, cs, alignment); w->show(); } else { diff --git a/tools/designer/src/lib/shared/qlayout_widget.cpp b/tools/designer/src/lib/shared/qlayout_widget.cpp index 20db606..4debb5e 100644 --- a/tools/designer/src/lib/shared/qlayout_widget.cpp +++ b/tools/designer/src/lib/shared/qlayout_widget.cpp @@ -1181,8 +1181,7 @@ QRect LayoutHelper::itemInfo(QLayout *lt, const QWidget *widget) const } else { for (int c = 0; c < FormLayoutColumns; c++) { const QFormLayout::ItemRole role = c == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole; - if (widgets[c]) { - Q_ASSERT(BoxLayoutHelper::findItemOfWidget(items, widgets[c])); + if (widgets[c] && BoxLayoutHelper::findItemOfWidget(items, widgets[c])) { formLayout->setWidget(r, role, widgets[c]); } else { formLayout->setItem(r, role, createFormSpacer()); -- cgit v0.12 From bb84c5ef4f620af659395b66e6ed792a380b9a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 1 Feb 2011 15:17:53 +0100 Subject: Don't ignore source-text when generating qsTrId translations for QML Reviewed-by: ossi --- tests/auto/linguist/lupdate/testdata/good/parseqml/main.qml | 3 +++ tests/auto/linguist/lupdate/testdata/good/parseqml/project.ts.result | 5 +++++ tools/linguist/lupdate/qdeclarative.cpp | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/auto/linguist/lupdate/testdata/good/parseqml/main.qml b/tests/auto/linguist/lupdate/testdata/good/parseqml/main.qml index 768a4e2..c966fa1 100644 --- a/tests/auto/linguist/lupdate/testdata/good/parseqml/main.qml +++ b/tests/auto/linguist/lupdate/testdata/good/parseqml/main.qml @@ -93,5 +93,8 @@ QtObject { //: qsTrId() with comment, meta-data and plurals. //~ well-tested True qsTrId("qtn_bar_baz", 10); + + //% "Source text" + qsTrId("qtn_baz_biz"); } } diff --git a/tests/auto/linguist/lupdate/testdata/good/parseqml/project.ts.result b/tests/auto/linguist/lupdate/testdata/good/parseqml/project.ts.result index 7dac8cb..4843902 100644 --- a/tests/auto/linguist/lupdate/testdata/good/parseqml/project.ts.result +++ b/tests/auto/linguist/lupdate/testdata/good/parseqml/project.ts.result @@ -27,6 +27,11 @@ True + + + Source text + + BarContext diff --git a/tools/linguist/lupdate/qdeclarative.cpp b/tools/linguist/lupdate/qdeclarative.cpp index b85b1a7..1c1e9ad 100644 --- a/tools/linguist/lupdate/qdeclarative.cpp +++ b/tools/linguist/lupdate/qdeclarative.cpp @@ -225,7 +225,7 @@ protected: const QString id = literal->value->asString(); bool plural = node->arguments->next; - TranslatorMessage msg(QString(), QString(), + TranslatorMessage msg(QString(), sourcetext, QString(), QString(), m_fileName, node->firstSourceLocation().startLine, QStringList(), TranslatorMessage::Unfinished, plural); -- cgit v0.12 From 9fa392cdb528db0bb5073892484c809e99ae27b3 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 2 Feb 2011 09:27:53 +0100 Subject: Revert "Restore Qt 4.6 behaviour: exec() always enters the event loop." This reverts commit 2e72a8b19ea6c674fb4777860dac50faa5d387e6. The behavour in Qt 4.6 was wrong. And even if it was not documented, it is too late to change the behaviour back at this point. The tests for QEventLoop and QCoreApplication have not been reverted Reviewed-by: Brad --- src/corelib/thread/qthread.cpp | 5 +- tests/auto/qthread/tst_qthread.cpp | 191 +++++++++++-------------------------- 2 files changed, 58 insertions(+), 138 deletions(-) diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index f4bfa5d..f368192 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -482,7 +482,10 @@ int QThread::exec() Q_D(QThread); QMutexLocker locker(&d->mutex); d->data->quitNow = false; - d->exited = false; + if (d->exited) { + d->exited = false; + return d->returnCode; + } locker.unlock(); QEventLoop eventLoop; diff --git a/tests/auto/qthread/tst_qthread.cpp b/tests/auto/qthread/tst_qthread.cpp index c7036e4..e6bf9ce 100644 --- a/tests/auto/qthread/tst_qthread.cpp +++ b/tests/auto/qthread/tst_qthread.cpp @@ -86,7 +86,6 @@ private slots: void start(); void terminate(); void quit(); - void execAfterQuit(); void wait(); void started(); void finished(); @@ -266,34 +265,6 @@ public: } }; -class ExecAfterQuitThreadHelper: public QObject -{ - Q_OBJECT - QThread *thr; -public: - ExecAfterQuitThreadHelper(QThread *thr) : thr(thr) {} -public slots: - void doIt() { thr->exit(0); } -}; - -class ExecAfterQuitThread: public QThread -{ -public: - int returnValue; - void run() - { - ExecAfterQuitThreadHelper obj(this); - - QMetaObject::invokeMethod(&obj, "doIt", Qt::QueuedConnection); - exit(1); - - // returnValue will be either 0 or 1, depending on which of the two - // above take effect. The correct value is 0, since exit(1) before - // exec() should have no effect - returnValue = exec(); - } -}; - tst_QThread::tst_QThread() { @@ -453,52 +424,34 @@ void tst_QThread::stackSize() void tst_QThread::exit() { - { - Exit_Thread thread; - thread.object = new Exit_Object; - thread.object->moveToThread(&thread); - thread.code = 42; - thread.result = 0; - QVERIFY(!thread.isFinished()); - QVERIFY(!thread.isRunning()); - - QMutexLocker locker(&thread.mutex); - thread.start(); - QVERIFY(thread.isRunning()); - QVERIFY(!thread.isFinished()); - // but the thread is not running the event loop yet (the mutex is locked) - - // start the event loop - thread.cond.wait(locker.mutex()); - - // the Exit_Object above will cause the thread to exit - QVERIFY(thread.wait(five_minutes)); - QVERIFY(thread.isFinished()); - QVERIFY(!thread.isRunning()); - QCOMPARE(thread.result, thread.code); - delete thread.object; - } + Exit_Thread thread; + thread.object = new Exit_Object; + thread.object->moveToThread(&thread); + thread.code = 42; + thread.result = 0; + QVERIFY(!thread.isFinished()); + QVERIFY(!thread.isRunning()); + QMutexLocker locker(&thread.mutex); + thread.start(); + QVERIFY(thread.isRunning()); + QVERIFY(!thread.isFinished()); + thread.cond.wait(locker.mutex()); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.isFinished()); + QVERIFY(!thread.isRunning()); + QCOMPARE(thread.result, thread.code); + delete thread.object; - { - Exit_Thread thread2; - thread2.object = 0; - thread2.code = 53; - thread2.result = 0; - QMutexLocker locker2(&thread2.mutex); - thread2.start(); - - // the mutex is locked, so the thread has *not* started running the event loop yet - // this will do nothing: - thread2.exit(thread2.code); - - // the thread will now start running - thread2.cond.wait(locker2.mutex()); - - // this will cause it to exit now - thread2.exit(++thread2.code); - QVERIFY(thread2.wait(five_minutes)); - QCOMPARE(thread2.result, thread2.code); - } + Exit_Thread thread2; + thread2.object = 0; + thread2.code = 53; + thread2.result = 0; + QMutexLocker locker2(&thread2.mutex); + thread2.start(); + thread2.exit(thread2.code); + thread2.cond.wait(locker2.mutex()); + QVERIFY(thread2.wait(five_minutes)); + QCOMPARE(thread2.result, thread2.code); } void tst_QThread::start() @@ -545,59 +498,32 @@ void tst_QThread::terminate() void tst_QThread::quit() { - // very similar to exit() above - { - Quit_Thread thread; - thread.object = new Quit_Object; - thread.object->moveToThread(&thread); - thread.result = -1; - QVERIFY(!thread.isFinished()); - QVERIFY(!thread.isRunning()); - - // start the thread, but keep the event loop from starting - // (while the mutex is locked) - QMutexLocker locker(&thread.mutex); - thread.start(); - QVERIFY(thread.isRunning()); - QVERIFY(!thread.isFinished()); - - // unlock the mutex and let the event loop run - // the Quit_Object above will cause the thread to quit - thread.cond.wait(locker.mutex()); - QVERIFY(thread.wait(five_minutes)); - QVERIFY(thread.isFinished()); - QVERIFY(!thread.isRunning()); - QCOMPARE(thread.result, 0); - delete thread.object; - } - - { - Quit_Thread thread2; - thread2.object = 0; - thread2.result = -1; - - // start the thread, but keep the event loop from starting - // (while the mutex is locked) - QMutexLocker locker2(&thread2.mutex); - thread2.start(); - thread2.quit(); // does nothing, the event loop is not running! - - // unlock the mutex and let the event loop run - thread2.cond.wait(locker2.mutex()); - - // there's no Quit_Object so it won't quit on its own - thread2.quit(); - QVERIFY(thread2.wait(five_minutes)); - QCOMPARE(thread2.result, 0); - } -} - -void tst_QThread::execAfterQuit() -{ - ExecAfterQuitThread thread; + Quit_Thread thread; + thread.object = new Quit_Object; + thread.object->moveToThread(&thread); + thread.result = -1; + QVERIFY(!thread.isFinished()); + QVERIFY(!thread.isRunning()); + QMutexLocker locker(&thread.mutex); thread.start(); - QVERIFY(thread.wait()); - QCOMPARE(thread.returnValue, 0); + QVERIFY(thread.isRunning()); + QVERIFY(!thread.isFinished()); + thread.cond.wait(locker.mutex()); + QVERIFY(thread.wait(five_minutes)); + QVERIFY(thread.isFinished()); + QVERIFY(!thread.isRunning()); + QCOMPARE(thread.result, 0); + delete thread.object; + + Quit_Thread thread2; + thread2.object = 0; + thread2.result = -1; + QMutexLocker locker2(&thread2.mutex); + thread2.start(); + thread2.quit(); + thread2.cond.wait(locker2.mutex()); + QVERIFY(thread2.wait(five_minutes)); + QCOMPARE(thread2.result, 0); } void tst_QThread::wait() @@ -1068,17 +994,8 @@ void tst_QThread::QTBUG15378_exitAndExec() Thread thread; thread.value = 0; thread.start(); - thread.exit(42); // will do nothing, this value should not appear - thread.sem1.release(); //should enter the first loop - - Exit_Object *exit_object = new Exit_Object; - exit_object->code = 556; - exit_object->thread = &thread; - QMetaObject::invokeMethod(exit_object, "slot", Qt::QueuedConnection); - exit_object->deleteLater(); - exit_object->moveToThread(&thread); // should exit the first loop - exit_object = 0; - + thread.exit(556); + thread.sem1.release(); //should exit the first loop thread.sem2.acquire(); int v = thread.value; QCOMPARE(v, 556); -- cgit v0.12 From 216b29f0c976405c718c7bb8e24699d368baac7e Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 2 Feb 2011 10:03:51 +0100 Subject: document that QThread::exit will exit future event loops Reviewed-by: Brad --- src/corelib/thread/qthread.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp index f368192..8223cff 100644 --- a/src/corelib/thread/qthread.cpp +++ b/src/corelib/thread/qthread.cpp @@ -509,10 +509,12 @@ int QThread::exec() Note that unlike the C library function of the same name, this function \e does return to the caller -- it is event processing - that stops. - - This function does nothing if the thread does not have an event - loop. + that stops. + + No QEventLoops will be started anymore in this thread until + QThread::exec() has been called again. If the eventloop in QThread::exec() + is not running then the next call to QThread::exec() will also return + immediately. \sa quit() QEventLoop */ -- cgit v0.12 From 186a9f967394d554ce99b8a3313cbc679b54c7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Tue, 1 Feb 2011 17:12:11 +0100 Subject: Fixed es2 configure when both EGL/egl.h and GLES/egl.h are present. Change e0c2861976e06658a1d651941310407c15b0bcde fixed es1 configure by only picking EGL/egl.h if GLES/egl.h is not found. This broke es2 configure on platforms where both EGL/egl.h and GLES/egl.h are present, by picking GLES/egl.h instead of EGL/egl.h, which implicitly pulled in the GLES/gl.h header as well, causing inconsistent declarations due to both GLES2/gl2.h and GLES/gl.h getting included. Instead, we need to prioritize depending on whether we're using es1 or es2. For es1, we prefer GLES/egl.h, and for es2, we prefer EGL/egl.h. If only one of the headers is found we still use it unconditionally, to preserve the existing behaviour and minimize the risk of breaking other platforms. Reviewed-by: Ritt Konstantin Reviewed-by: Marius Storm-Olsen --- configure | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/configure b/configure index dfbf9bd..5e8ef17 100755 --- a/configure +++ b/configure @@ -6056,19 +6056,28 @@ if [ "$PLATFORM_QWS" = "yes" ]; then fi # QWS +EGL_VARIANT=none # EGL Support if [ "$PLATFORM_X11" = "yes" -o "$PLATFORM_QWS" = "yes" ]; then if [ "$CFG_EGL" != "no" ]; then # detect EGL support - if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" "config.tests/unix/egl4gles1" "EGL (GLES/egl.h)" $L_FLAGS $I_FLAGS $l_FLAGS; then - # EGL specified by QMAKE_*_EGL, included with - CFG_EGL=yes - CFG_EGL_GLES_INCLUDES=yes - elif "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" "config.tests/unix/egl" "EGL (EGL/egl.h)" $L_FLAGS $I_FLAGS $l_FLAGS; then + if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" "config.tests/unix/egl" "EGL (EGL/egl.h)" $L_FLAGS $I_FLAGS $l_FLAGS; then # EGL specified by QMAKE_*_EGL, included with + EGL_VARIANT=regular CFG_EGL=yes - CFG_EGL_GLES_INCLUDES=no - else + fi + + # Prefer this variant for ES1 + if [ "$CFG_OPENGL" = "es1" -o "$EGL_VARIANT" = "none" ]; then + if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" "config.tests/unix/egl4gles1" "EGL (GLES/egl.h)" $L_FLAGS $I_FLAGS $l_FLAGS; then + # EGL specified by QMAKE_*_EGL, included with + EGL_VARIANT=gles + CFG_EGL=yes + CFG_EGL_GLES_INCLUDES=yes + fi + fi + + if [ "$EGL_VARIANT" = "none" ]; then if [ "$CFG_EGL" = "yes" ]; then echo "The EGL functionality test failed!" echo " EGL is required for OpenGL ES to manage contexts & surfaces." -- cgit v0.12 From 5ad8cd48a1ed3be183c0af8698491b039f0054e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Tue, 1 Feb 2011 17:59:54 +0100 Subject: Prevent recursion when creating window surface. If we can't access the qt_gl_share_widget() we should just create a raster window surface. This might happen when creating the share widget itself leads to creation of a window surface (which isn't really going to be used anyways). Reviewed-by: Michael Dominic K --- src/plugins/graphicssystems/meego/qmeegographicssystem.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp b/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp index 20b092e..a70d232 100644 --- a/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp +++ b/src/plugins/graphicssystems/meego/qmeegographicssystem.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -75,7 +76,12 @@ QMeeGoGraphicsSystem::~QMeeGoGraphicsSystem() QWindowSurface* QMeeGoGraphicsSystem::createWindowSurface(QWidget *widget) const { - QGLShareContextScope ctx(qt_gl_share_widget()->context()); + QGLWidget *shareWidget = qt_gl_share_widget(); + + if (!shareWidget) + return new QRasterWindowSurface(widget); + + QGLShareContextScope ctx(shareWidget->context()); QMeeGoGraphicsSystem::surfaceWasCreated = true; QWindowSurface *surface = new QGLWindowSurface(widget); -- cgit v0.12 From 4d38013cfc3058e36de1b6a6c20653ef2688a92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Tue, 1 Feb 2011 17:57:10 +0100 Subject: Fixed missing text when using static text items in GL 2 engine. When the context is destroyed and recreated, we end up with a new glyph cache, but we only recreate the vertex arrays for the very first static text item. We need to keep track of the glyph cache in each text item, so that we can recreate the vertex arrays and re-populate the cache accordingly. As the pointer might be the same after the glyph cache is recreated, we need to use serial numbers instead. We also need to re-create the cache when the context pointer has been invalidated, so that the static text items also get invalidated, and the texture glyph cache gets repopulated. Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 13 ++++++++----- src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp | 3 +++ src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h | 3 +++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index ad2852e..cda31e5 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1489,7 +1489,7 @@ namespace { { public: QOpenGLStaticTextUserData() - : QStaticTextUserData(OpenGLUserData), cacheSize(0, 0) + : QStaticTextUserData(OpenGLUserData), cacheSize(0, 0), cacheSerialNumber(0) { } @@ -1501,6 +1501,7 @@ namespace { QGL2PEXVertexArray vertexCoordinateArray; QGL2PEXVertexArray textureCoordinateArray; QFontEngineGlyphCache::Type glyphType; + int cacheSerialNumber; }; } @@ -1518,12 +1519,10 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp QGLTextureGlyphCache *cache = (QGLTextureGlyphCache *) staticTextItem->fontEngine()->glyphCache(ctx, glyphType, QTransform()); - if (!cache || cache->cacheType() != glyphType) { + if (!cache || cache->cacheType() != glyphType || cache->context() == 0) { cache = new QGLTextureGlyphCache(ctx, glyphType, QTransform()); staticTextItem->fontEngine()->setGlyphCache(ctx, cache); recreateVertexArrays = true; - } else if (cache->context() == 0) { // Old context has been destroyed, new context has same ptr value - cache->setContext(ctx); } if (staticTextItem->userDataNeedsUpdate) { @@ -1534,8 +1533,11 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp recreateVertexArrays = true; } else { QOpenGLStaticTextUserData *userData = static_cast(staticTextItem->userData()); - if (userData->glyphType != glyphType) + if (userData->glyphType != glyphType) { recreateVertexArrays = true; + } else if (userData->cacheSerialNumber != cache->serialNumber()) { + recreateVertexArrays = true; + } } // We only need to update the cache with new glyphs if we are actually going to recreate the vertex arrays. @@ -1580,6 +1582,7 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(QFontEngineGlyphCache::Type glyp } userData->glyphType = glyphType; + userData->cacheSerialNumber = cache->serialNumber(); // Use cache if backend optimizations is turned on vertexCoordinates = &userData->vertexCoordinateArray; diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp index 58c78ec..312d66f 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl.cpp @@ -52,12 +52,15 @@ QT_BEGIN_NAMESPACE extern Q_GUI_EXPORT bool qt_cleartype_enabled; #endif +QBasicAtomicInt qgltextureglyphcache_serial_number = Q_BASIC_ATOMIC_INITIALIZER(1); + QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyphCache::Type type, const QTransform &matrix) : QImageTextureGlyphCache(type, matrix) , ctx(0) , m_width(0) , m_height(0) , m_filterMode(Nearest) + , m_serialNumber(qgltextureglyphcache_serial_number.fetchAndAddRelaxed(1)) { setContext(context); } diff --git a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h index 1c1b7c4..2eb4e65 100644 --- a/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h +++ b/src/opengl/gl2paintengineex/qtextureglyphcache_gl_p.h @@ -83,6 +83,8 @@ public: inline void setPaintEnginePrivate(QGL2PaintEngineExPrivate *p) { pex = p; } + inline int serialNumber() const { return m_serialNumber; } + enum FilterMode { Nearest, Linear @@ -140,6 +142,7 @@ private: QGLShaderProgram *m_program; FilterMode m_filterMode; + int m_serialNumber; }; QT_END_NAMESPACE -- cgit v0.12 From 31492cc049a8dfe7f52748922a88f117499d62a3 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 26 Jan 2011 08:43:34 +0100 Subject: Fixes memory leaks in QX11Embed We should XFree the values returned from XGetWindowProperty. Task-number: QTBUG-16597 Reviewed-by: Ritt Konstantin --- src/gui/kernel/qx11embed_x11.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qx11embed_x11.cpp b/src/gui/kernel/qx11embed_x11.cpp index aba42f2..710f607 100644 --- a/src/gui/kernel/qx11embed_x11.cpp +++ b/src/gui/kernel/qx11embed_x11.cpp @@ -415,8 +415,9 @@ static Bool functor(Display *display, XEvent *event, XPointer arg) status = XGetWindowProperty(display, data->id, ATOM(WM_STATE), 0, 2, False, ATOM(WM_STATE), &ret, &format, &nitems, &after, &retval ); if (status == Success && ret == ATOM(WM_STATE) && format == 32 && nitems > 0) { - long *state = (long *)retval; - if (state[0] == WithdrawnState) { + long state = *(long *)retval; + XFree(retval); + if (state == WithdrawnState) { data->clearedWmState = true; return true; } @@ -833,6 +834,8 @@ bool QX11EmbedWidget::x11Event(XEvent *event) XUnmapWindow(x11Info().display(), internalWinId()); } } + if (prop_return) + XFree(prop_return); } } -- cgit v0.12 From 17ee918b2cd2bb012a538b721bbd888677072614 Mon Sep 17 00:00:00 2001 From: Denis Dzyubenko Date: Wed, 2 Feb 2011 12:54:20 +0100 Subject: Carefull free the data from XGetWindowProperty on X11. Make sure we free the data correctly in case of error. Task-number: QTBUG-16616 Patch-by: Juuso Pakarinen Reviewed-by: Ritt Konstantin --- src/gui/kernel/qwidget_x11.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index 9893478..28eb3f0 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -444,6 +444,7 @@ static QVector getNetWmState(QWidget *w) && actualType == XA_ATOM && actualFormat == 32) { returnValue.resize(bytesLeft / 4); XFree((char*) propertyData); + propertyData = 0; // fetch all data if (XGetWindowProperty(X11->display, w->internalWinId(), ATOM(_NET_WM_STATE), 0, @@ -458,7 +459,8 @@ static QVector getNetWmState(QWidget *w) if (!returnValue.isEmpty()) { memcpy(returnValue.data(), propertyData, returnValue.size() * sizeof(Atom)); } - XFree((char*) propertyData); + if (propertyData) + XFree((char*) propertyData); } return returnValue; -- cgit v0.12 From 7987d4cfd3ce86c20a55b5661a5221f12246b27e Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 2 Feb 2011 11:31:20 +0100 Subject: Fix QMutex can deadlock when calling tryLock in the unix code, if the QMutexPrivate::wait() with a timeout expires in the same moment that the mutex is released, wakeup would be set, but would be then ignored. (reset to false quickly after) If we waken up between the timeout and the re-aquisition of the internal mutex, we consider that the mutex has been locked. Reviewed-by: brad Task-number: QTBUG-16115 --- src/corelib/thread/qmutex_unix.cpp | 5 ++++- tests/auto/qmutex/tst_qmutex.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/corelib/thread/qmutex_unix.cpp b/src/corelib/thread/qmutex_unix.cpp index 41d87a7..dd7e5d1 100644 --- a/src/corelib/thread/qmutex_unix.cpp +++ b/src/corelib/thread/qmutex_unix.cpp @@ -94,8 +94,11 @@ bool QMutexPrivate::wait(int timeout) errorCode = pthread_cond_timedwait(&cond, &mutex, &ti); } if (errorCode) { - if (errorCode == ETIMEDOUT) + if (errorCode == ETIMEDOUT) { + if (wakeup) + errorCode = 0; break; + } report_error(errorCode, "QMutex::lock()", "cv wait"); } } diff --git a/tests/auto/qmutex/tst_qmutex.cpp b/tests/auto/qmutex/tst_qmutex.cpp index 3c4c767..ea983cb 100644 --- a/tests/auto/qmutex/tst_qmutex.cpp +++ b/tests/auto/qmutex/tst_qmutex.cpp @@ -67,6 +67,7 @@ private slots: void lock_unlock_locked_tryLock(); void stressTest(); void tryLockRace(); + void qtbug16115_trylock(); }; static const int iterations = 100; @@ -464,5 +465,42 @@ void tst_QMutex::tryLockRace() TryLockRaceThread::mutex.unlock(); } +static volatile int qtbug16115_trylock_counter; + +void tst_QMutex::qtbug16115_trylock() +{ + //Used to deadlock on unix + struct TrylockThread : QThread { + TrylockThread(QMutex &mut) : mut(mut) {} + QMutex &mut; + void run() { + for (int i = 0; i < 1000000; ++i) { + if (mut.tryLock(0)) { + Q_ASSERT((++qtbug16115_trylock_counter) == 1); + Q_ASSERT((--qtbug16115_trylock_counter) == 0); + mut.unlock(); + } + } + } + }; + QMutex mut; + TrylockThread t1(mut); + TrylockThread t2(mut); + TrylockThread t3(mut); + t1.start(); + t2.start(); + t3.start(); + + for (int i = 0; i < 1000000; ++i) { + mut.lock(); + Q_ASSERT((++qtbug16115_trylock_counter) == 1); + Q_ASSERT((--qtbug16115_trylock_counter) == 0); + mut.unlock(); + } + t1.wait(); + t2.wait(); + t3.wait(); +} + QTEST_MAIN(tst_QMutex) #include "tst_qmutex.moc" -- cgit v0.12 From bb6d0f75d4a5d3dadb5e0134aa0270bcab61f205 Mon Sep 17 00:00:00 2001 From: kranthi Date: Wed, 2 Feb 2011 16:57:45 +0200 Subject: Fix for QTBUG-17035 Reviewed-by: Aaron McCarthy Task-number: QTBUG-17035 --- src/plugins/bearer/icd/qnetworksession_impl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/bearer/icd/qnetworksession_impl.cpp b/src/plugins/bearer/icd/qnetworksession_impl.cpp index 6d578d3..af5d85e 100644 --- a/src/plugins/bearer/icd/qnetworksession_impl.cpp +++ b/src/plugins/bearer/icd/qnetworksession_impl.cpp @@ -125,9 +125,6 @@ static QString get_network_interface(); void QNetworkSessionPrivateImpl::iapStateChanged(const QString& iapid, uint icd_connection_state) { - if ((publicConfig.type() == QNetworkConfiguration::UserChoice) && opened) { - updateIdentifier(iapid); - } if (((publicConfig.type() == QNetworkConfiguration::UserChoice) && (activeConfig.identifier() == iapid)) || @@ -149,6 +146,9 @@ void QNetworkSessionPrivateImpl::iapStateChanged(const QString& iapid, uint icd_ break; } } + if (publicConfig.type() == QNetworkConfiguration::UserChoice) { + updateIdentifier(iapid); + } } void QNetworkSessionPrivateImpl::cleanupSession(void) -- cgit v0.12 From a97a36aac550c175a2e2045ead55ad4d263a24ed Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 2 Feb 2011 08:02:04 +0100 Subject: Split QScriptClass autotest into smaller functions Task-number: QTBUG-16746 Reviewed-by: Jedrzej Nowacki --- tests/auto/qscriptclass/tst_qscriptclass.cpp | 446 ++++++++++++++------------- 1 file changed, 236 insertions(+), 210 deletions(-) diff --git a/tests/auto/qscriptclass/tst_qscriptclass.cpp b/tests/auto/qscriptclass/tst_qscriptclass.cpp index 16f09b6..2d6daec 100644 --- a/tests/auto/qscriptclass/tst_qscriptclass.cpp +++ b/tests/auto/qscriptclass/tst_qscriptclass.cpp @@ -65,10 +65,15 @@ public: private slots: void newInstance(); - void getAndSetProperty(); + void setScriptClassOfExistingObject(); + void setScriptClassOfNonQtScriptObject(); + void getAndSetPropertyFromCpp(); void getProperty_invalidValue(); void enumerate(); - void extension(); + void extension_None(); + void extension_Callable(); + void extension_Callable_construct(); + void extension_HasInstance(); void originalProperties1(); void originalProperties2(); void originalProperties3(); @@ -611,7 +616,12 @@ void tst_QScriptClass::newInstance() QCOMPARE(obj2.scriptClass(), (QScriptClass*)&cls); QVERIFY(!obj2.equals(obj1)); QVERIFY(!obj2.strictlyEquals(obj1)); +} +void tst_QScriptClass::setScriptClassOfExistingObject() +{ + QScriptEngine eng; + TestClass cls(&eng); QScriptValue obj3 = eng.newObject(); QCOMPARE(obj3.scriptClass(), (QScriptClass*)0); obj3.setScriptClass(&cls); @@ -625,7 +635,12 @@ void tst_QScriptClass::newInstance() TestClass cls2(&eng); obj3.setScriptClass(&cls2); QCOMPARE(obj3.scriptClass(), (QScriptClass*)&cls2); +} +void tst_QScriptClass::setScriptClassOfNonQtScriptObject() +{ + QScriptEngine eng; + TestClass cls(&eng); // undefined behavior really, but shouldn't crash QScriptValue arr = eng.newArray(); QVERIFY(arr.isArray()); @@ -639,7 +654,7 @@ void tst_QScriptClass::newInstance() QVERIFY(arr.isObject()); } -void tst_QScriptClass::getAndSetProperty() +void tst_QScriptClass::getAndSetPropertyFromCpp() { QScriptEngine eng; @@ -651,7 +666,9 @@ void tst_QScriptClass::getAndSetProperty() QScriptString bar = eng.toStringHandle("bar"); QScriptValue num(&eng, 123); - // should behave just like normal + // Initially our TestClass instances have no custom properties, + // and queryProperty() will always return false. + // Hence, the properties will be created as normal JS properties. for (int x = 0; x < 2; ++x) { QScriptValue &o = (x == 0) ? obj1 : obj2; for (int y = 0; y < 2; ++y) { @@ -828,229 +845,238 @@ void tst_QScriptClass::enumerate() } } -void tst_QScriptClass::extension() +void tst_QScriptClass::extension_None() { QScriptEngine eng; + TestClass cls(&eng); + cls.setCallableMode(TestClass::NotCallable); + QVERIFY(!cls.supportsExtension(QScriptClass::Callable)); + QVERIFY(!cls.supportsExtension(QScriptClass::HasInstance)); + QScriptValue obj = eng.newObject(&cls); + QVERIFY(!obj.call().isValid()); + QCOMPARE((int)cls.lastExtensionType(), -1); + QVERIFY(!obj.instanceOf(obj)); + QCOMPARE((int)cls.lastExtensionType(), -1); + QVERIFY(!obj.construct().isValid()); +} + +void tst_QScriptClass::extension_Callable() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.setCallableMode(TestClass::CallableReturnsSum); + QVERIFY(cls.supportsExtension(QScriptClass::Callable)); + + QScriptValue obj = eng.newObject(&cls); + eng.globalObject().setProperty("obj", obj); + obj.setProperty("one", QScriptValue(&eng, 1)); + obj.setProperty("two", QScriptValue(&eng, 2)); + obj.setProperty("three", QScriptValue(&eng, 3)); + // From C++ + cls.clearReceivedArgs(); { - TestClass cls(&eng); - cls.setCallableMode(TestClass::NotCallable); - QVERIFY(!cls.supportsExtension(QScriptClass::Callable)); - QVERIFY(!cls.supportsExtension(QScriptClass::HasInstance)); - QScriptValue obj = eng.newObject(&cls); - QVERIFY(!obj.call().isValid()); - QCOMPARE((int)cls.lastExtensionType(), -1); - QVERIFY(!obj.instanceOf(obj)); - QCOMPARE((int)cls.lastExtensionType(), -1); - QVERIFY(!obj.construct().isValid()); + QScriptValueList args; + args << QScriptValue(&eng, 4) << QScriptValue(&eng, 5); + QScriptValue ret = obj.call(obj, args); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), qsreal(1+2+3+4+5)); } - // Callable + // From JS + cls.clearReceivedArgs(); { - TestClass cls(&eng); - cls.setCallableMode(TestClass::CallableReturnsSum); - QVERIFY(cls.supportsExtension(QScriptClass::Callable)); - - QScriptValue obj = eng.newObject(&cls); - eng.globalObject().setProperty("obj", obj); - obj.setProperty("one", QScriptValue(&eng, 1)); - obj.setProperty("two", QScriptValue(&eng, 2)); - obj.setProperty("three", QScriptValue(&eng, 3)); - // From C++ - cls.clearReceivedArgs(); - { - QScriptValueList args; - args << QScriptValue(&eng, 4) << QScriptValue(&eng, 5); - QScriptValue ret = obj.call(obj, args); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); - QVERIFY(ret.isNumber()); - QCOMPARE(ret.toNumber(), qsreal(15)); - } - // From JS - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("obj(4, 5)"); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); - QVERIFY(ret.isNumber()); - QCOMPARE(ret.toNumber(), qsreal(15)); - } + QScriptValue ret = eng.evaluate("obj(4, 5)"); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), qsreal(1+2+3+4+5)); + } - cls.setCallableMode(TestClass::CallableReturnsArgument); - // From C++ - cls.clearReceivedArgs(); - { - QScriptValue ret = obj.call(obj, QScriptValueList() << 123); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); - QVERIFY(ret.isNumber()); - QCOMPARE(ret.toInt32(), 123); - } - cls.clearReceivedArgs(); - { - QScriptValue ret = obj.call(obj, QScriptValueList() << true); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); - QVERIFY(ret.isBoolean()); - QCOMPARE(ret.toBoolean(), true); - } - { - QScriptValue ret = obj.call(obj, QScriptValueList() << QString::fromLatin1("ciao")); - QVERIFY(ret.isString()); - QCOMPARE(ret.toString(), QString::fromLatin1("ciao")); - } - { - QScriptValue objobj = eng.newObject(); - QScriptValue ret = obj.call(obj, QScriptValueList() << objobj); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(objobj)); - } - { - QScriptValue ret = obj.call(obj, QScriptValueList() << QScriptValue()); - QVERIFY(ret.isUndefined()); - } - // From JS - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("obj(123)"); - QVERIFY(ret.isNumber()); - QCOMPARE(ret.toInt32(), 123); - } + cls.setCallableMode(TestClass::CallableReturnsArgument); + // From C++ + cls.clearReceivedArgs(); + { + QScriptValue ret = obj.call(obj, QScriptValueList() << 123); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + cls.clearReceivedArgs(); + { + QScriptValue ret = obj.call(obj, QScriptValueList() << true); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QVERIFY(ret.isBoolean()); + QCOMPARE(ret.toBoolean(), true); + } + { + QScriptValue ret = obj.call(obj, QScriptValueList() << QString::fromLatin1("ciao")); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("ciao")); + } + { + QScriptValue objobj = eng.newObject(); + QScriptValue ret = obj.call(obj, QScriptValueList() << objobj); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(objobj)); + } + { + QScriptValue ret = obj.call(obj, QScriptValueList() << QScriptValue()); + QVERIFY(ret.isUndefined()); + } + // From JS + cls.clearReceivedArgs(); + { + QScriptValue ret = eng.evaluate("obj(123)"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } - cls.setCallableMode(TestClass::CallableReturnsInvalidVariant); - { - QScriptValue ret = obj.call(obj); - QVERIFY(ret.isUndefined()); - } + cls.setCallableMode(TestClass::CallableReturnsInvalidVariant); + { + QScriptValue ret = obj.call(obj); + QVERIFY(ret.isUndefined()); + } - cls.setCallableMode(TestClass::CallableReturnsThisObject); - // From C++ - { - QScriptValue ret = obj.call(obj); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(obj)); - } - // From JS - { - QScriptValue ret = eng.evaluate("obj()"); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(eng.globalObject())); - } + cls.setCallableMode(TestClass::CallableReturnsThisObject); + // From C++ + { + QScriptValue ret = obj.call(obj); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(obj)); + } + // From JS + { + QScriptValue ret = eng.evaluate("obj()"); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(eng.globalObject())); + } - cls.setCallableMode(TestClass::CallableReturnsCallee); - // From C++ - { - QScriptValue ret = obj.call(); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(obj)); - } - // From JS - { - QScriptValue ret = eng.evaluate("obj()"); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(obj)); - } + cls.setCallableMode(TestClass::CallableReturnsCallee); + // From C++ + { + QScriptValue ret = obj.call(); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(obj)); + } + // From JS + { + QScriptValue ret = eng.evaluate("obj()"); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(obj)); + } - cls.setCallableMode(TestClass::CallableReturnsArgumentsObject); - // From C++ - { - QScriptValue ret = obj.call(obj, QScriptValueList() << 123); - QVERIFY(ret.isObject()); - QVERIFY(ret.property("length").isNumber()); - QCOMPARE(ret.property("length").toInt32(), 1); - QVERIFY(ret.property(0).isNumber()); - QCOMPARE(ret.property(0).toInt32(), 123); - } - // From JS - { - QScriptValue ret = eng.evaluate("obj(123)"); - QVERIFY(ret.isObject()); - QVERIFY(ret.property("length").isNumber()); - QCOMPARE(ret.property("length").toInt32(), 1); - QVERIFY(ret.property(0).isNumber()); - QCOMPARE(ret.property(0).toInt32(), 123); - } + cls.setCallableMode(TestClass::CallableReturnsArgumentsObject); + // From C++ + { + QScriptValue ret = obj.call(obj, QScriptValueList() << 123); + QVERIFY(ret.isObject()); + QVERIFY(ret.property("length").isNumber()); + QCOMPARE(ret.property("length").toInt32(), 1); + QVERIFY(ret.property(0).isNumber()); + QCOMPARE(ret.property(0).toInt32(), 123); + } + // From JS + { + QScriptValue ret = eng.evaluate("obj(123)"); + QVERIFY(ret.isObject()); + QVERIFY(ret.property("length").isNumber()); + QCOMPARE(ret.property("length").toInt32(), 1); + QVERIFY(ret.property(0).isNumber()); + QCOMPARE(ret.property(0).toInt32(), 123); + } +} - // construct() - // From C++ - cls.clearReceivedArgs(); - cls.setCallableMode(TestClass::CallableReturnsGlobalObject); - { - QScriptValue ret = obj.construct(); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(eng.globalObject())); - } - // From JS - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("new obj()"); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); - QVERIFY(ret.isObject()); - QVERIFY(ret.strictlyEquals(eng.globalObject())); - } - // From C++ - cls.clearReceivedArgs(); - cls.setCallableMode(TestClass::CallableInitializesThisObject); - { - QScriptValue ret = obj.construct(); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); - QVERIFY(ret.isQObject()); - QCOMPARE(ret.toQObject(), (QObject*)&eng); - } - // From JS - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("new obj()"); - QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); - QVERIFY(ret.isQObject()); - QCOMPARE(ret.toQObject(), (QObject*)&eng); - } +void tst_QScriptClass::extension_Callable_construct() +{ + QScriptEngine eng; + TestClass cls(&eng); + QScriptValue obj = eng.newObject(&cls); + eng.globalObject().setProperty("obj", obj); + + // From C++ + cls.clearReceivedArgs(); + cls.setCallableMode(TestClass::CallableReturnsGlobalObject); + { + QScriptValue ret = obj.construct(); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(eng.globalObject())); } - // HasInstance + // From JS + cls.clearReceivedArgs(); { - TestClass cls(&eng); - cls.setHasInstance(true); - QVERIFY(cls.supportsExtension(QScriptClass::HasInstance)); + QScriptValue ret = eng.evaluate("new obj()"); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QVERIFY(ret.isObject()); + QVERIFY(ret.strictlyEquals(eng.globalObject())); + } + // From C++ + cls.clearReceivedArgs(); + cls.setCallableMode(TestClass::CallableInitializesThisObject); + { + QScriptValue ret = obj.construct(); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QVERIFY(ret.isQObject()); + QCOMPARE(ret.toQObject(), (QObject*)&eng); + } + // From JS + cls.clearReceivedArgs(); + { + QScriptValue ret = eng.evaluate("new obj()"); + QCOMPARE(cls.lastExtensionType(), QScriptClass::Callable); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QVERIFY(ret.isQObject()); + QCOMPARE(ret.toQObject(), (QObject*)&eng); + } +} + +void tst_QScriptClass::extension_HasInstance() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.setHasInstance(true); + QVERIFY(cls.supportsExtension(QScriptClass::HasInstance)); - QScriptValue obj = eng.newObject(&cls); - obj.setProperty("foo", QScriptValue(&eng, 123)); - QScriptValue plain = eng.newObject(); - QVERIFY(!plain.instanceOf(obj)); + QScriptValue obj = eng.newObject(&cls); + obj.setProperty("foo", QScriptValue(&eng, 123)); + QScriptValue plain = eng.newObject(); + QVERIFY(!plain.instanceOf(obj)); - eng.globalObject().setProperty("HasInstanceTester", obj); - eng.globalObject().setProperty("hasInstanceValue", plain); - cls.clearReceivedArgs(); - { - QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); - QCOMPARE(cls.lastExtensionType(), QScriptClass::HasInstance); - QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); - QScriptValueList lst = qvariant_cast(cls.lastExtensionArgument()); - QCOMPARE(lst.size(), 2); - QVERIFY(lst.at(0).strictlyEquals(obj)); - QVERIFY(lst.at(1).strictlyEquals(plain)); - QVERIFY(ret.isBoolean()); - QVERIFY(!ret.toBoolean()); - } + eng.globalObject().setProperty("HasInstanceTester", obj); + eng.globalObject().setProperty("hasInstanceValue", plain); + cls.clearReceivedArgs(); + { + QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); + QCOMPARE(cls.lastExtensionType(), QScriptClass::HasInstance); + QCOMPARE(cls.lastExtensionArgument().userType(), qMetaTypeId()); + QScriptValueList lst = qvariant_cast(cls.lastExtensionArgument()); + QCOMPARE(lst.size(), 2); + QVERIFY(lst.at(0).strictlyEquals(obj)); + QVERIFY(lst.at(1).strictlyEquals(plain)); + QVERIFY(ret.isBoolean()); + QVERIFY(!ret.toBoolean()); + } - plain.setProperty("foo", QScriptValue(&eng, 456)); - QVERIFY(!plain.instanceOf(obj)); - { - QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); - QVERIFY(ret.isBoolean()); - QVERIFY(!ret.toBoolean()); - } + plain.setProperty("foo", QScriptValue(&eng, 456)); + QVERIFY(!plain.instanceOf(obj)); + { + QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); + QVERIFY(ret.isBoolean()); + QVERIFY(!ret.toBoolean()); + } - plain.setProperty("foo", obj.property("foo")); - QVERIFY(plain.instanceOf(obj)); - { - QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); - QVERIFY(ret.isBoolean()); - QVERIFY(ret.toBoolean()); - } + plain.setProperty("foo", obj.property("foo")); + QVERIFY(plain.instanceOf(obj)); + { + QScriptValue ret = eng.evaluate("hasInstanceValue instanceof HasInstanceTester"); + QVERIFY(ret.isBoolean()); + QVERIFY(ret.toBoolean()); } } -- cgit v0.12 From 48f8d0cf20f8bcdb35bfc49057ad1fa768e66825 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 2 Feb 2011 08:11:32 +0100 Subject: Bring back "classic" iteration order of QScriptClass properties In the old back-end, normal JS properties were iterated before QScriptClass properties. There wasn't any good reason to change that. Now we can get rid of that left-over XFAIL as well. Task-number: QTBUG-16746 Reviewed-by: Jedrzej Nowacki --- src/script/bridge/qscriptclassobject.cpp | 5 ++++- tests/auto/qscriptclass/tst_qscriptclass.cpp | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/script/bridge/qscriptclassobject.cpp b/src/script/bridge/qscriptclassobject.cpp index 9285883..2085756 100644 --- a/src/script/bridge/qscriptclassobject.cpp +++ b/src/script/bridge/qscriptclassobject.cpp @@ -183,6 +183,10 @@ void ClassObjectDelegate::getOwnPropertyNames(QScriptObject* object, JSC::ExecSt JSC::PropertyNameArray &propertyNames, JSC::EnumerationMode mode) { + // For compatibility with the old back-end, normal JS properties + // are added first. + QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, mode); + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); QScript::SaveFrameHelper saveFrame(engine, exec); QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); @@ -195,7 +199,6 @@ void ClassObjectDelegate::getOwnPropertyNames(QScriptObject* object, JSC::ExecSt } delete it; } - QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, mode); } JSC::CallType ClassObjectDelegate::getCallData(QScriptObject*, JSC::CallData &callData) diff --git a/tests/auto/qscriptclass/tst_qscriptclass.cpp b/tests/auto/qscriptclass/tst_qscriptclass.cpp index 2d6daec..00fdd35 100644 --- a/tests/auto/qscriptclass/tst_qscriptclass.cpp +++ b/tests/auto/qscriptclass/tst_qscriptclass.cpp @@ -823,10 +823,12 @@ void tst_QScriptClass::enumerate() cls.setIterationEnabled(true); QScriptValueIterator it(obj); + // This test relies on the order in which properties are enumerated, + // which we don't guarantee. However, for compatibility's sake we prefer + // that normal JS properties come before QScriptClass properties. for (int x = 0; x < 2; ++x) { QVERIFY(it.hasNext()); it.next(); - QEXPECT_FAIL("", "", Abort); QVERIFY(it.scriptName() == foo); QVERIFY(it.hasNext()); it.next(); -- cgit v0.12 From a7027e7ecc0bfc97030110105436fd5406256108 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 2 Feb 2011 08:28:59 +0100 Subject: Improve QScriptClass test coverage Add tests for various kinds of property access from JS. Add tests for null-engine & using the same script class in two different engines. Task-number: QTBUG-16746 Reviewed-by: Jedrzej Nowacki --- tests/auto/qscriptclass/tst_qscriptclass.cpp | 149 ++++++++++++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) diff --git a/tests/auto/qscriptclass/tst_qscriptclass.cpp b/tests/auto/qscriptclass/tst_qscriptclass.cpp index 00fdd35..5286a5a 100644 --- a/tests/auto/qscriptclass/tst_qscriptclass.cpp +++ b/tests/auto/qscriptclass/tst_qscriptclass.cpp @@ -68,6 +68,10 @@ private slots: void setScriptClassOfExistingObject(); void setScriptClassOfNonQtScriptObject(); void getAndSetPropertyFromCpp(); + void getAndSetPropertyFromJS(); + void deleteUndeletableProperty(); + void writeReadOnlyProperty(); + void writePropertyWithoutWriteAccess(); void getProperty_invalidValue(); void enumerate(); void extension_None(); @@ -79,6 +83,9 @@ private slots: void originalProperties3(); void originalProperties4(); void defaultImplementations(); + void scriptClassObjectInPrototype(); + void scriptClassWithNullEngine(); + void scriptClassInOtherEngine(); }; tst_QScriptClass::tst_QScriptClass() @@ -311,7 +318,12 @@ void TestClass::setProperty(QScriptValue &object, const QScriptString &name, CustomProperty *prop = findCustomProperty(name); if (!prop) return; - prop->value = value; + if (prop->pflags & QScriptValue::ReadOnly) + return; + if (!value.isValid()) // deleteProperty() requested + removeCustomProperty(name); + else + prop->value = value; } QScriptValue::PropertyFlags TestClass::propertyFlags( @@ -774,6 +786,80 @@ void tst_QScriptClass::getAndSetPropertyFromCpp() QVERIFY(!obj1.property(bar).isValid()); } +void tst_QScriptClass::getAndSetPropertyFromJS() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.addCustomProperty(eng.toStringHandle("x"), + QScriptClass::HandlesReadAccess + | QScriptClass::HandlesWriteAccess, + /*id=*/1, /*flags=*/0, /*value=*/123); + eng.globalObject().setProperty("o", eng.newObject(&cls)); + + // Accessing a custom property + QCOMPARE(eng.evaluate("o.x").toInt32(), 123); + QCOMPARE(eng.evaluate("o.x = 456; o.x").toInt32(), 456); + + // Accessing a new JS property + QVERIFY(eng.evaluate("o.y").isUndefined()); + QCOMPARE(eng.evaluate("o.y = 789; o.y").toInt32(), 789); + + // Deleting custom property + QVERIFY(eng.evaluate("delete o.x").toBool()); + QVERIFY(eng.evaluate("o.x").isUndefined()); + + // Deleting JS property + QVERIFY(eng.evaluate("delete o.y").toBool()); + QVERIFY(eng.evaluate("o.y").isUndefined()); +} + +void tst_QScriptClass::deleteUndeletableProperty() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.addCustomProperty(eng.toStringHandle("x"), QScriptClass::HandlesWriteAccess, + /*id=*/0, QScriptValue::Undeletable, QScriptValue()); + eng.globalObject().setProperty("o", eng.newObject(&cls)); + QVERIFY(!eng.evaluate("delete o.x").toBool()); +} + +void tst_QScriptClass::writeReadOnlyProperty() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.addCustomProperty(eng.toStringHandle("x"), + QScriptClass::HandlesReadAccess + | QScriptClass::HandlesWriteAccess, + /*id=*/0, QScriptValue::ReadOnly, 123); + eng.globalObject().setProperty("o", eng.newObject(&cls)); + // Note that if a property is read-only, the setProperty() + // reimplementation will still get called; it's up to that + // function to respect the ReadOnly flag. + QCOMPARE(eng.evaluate("o.x = 456; o.x").toInt32(), 123); +} + +void tst_QScriptClass::writePropertyWithoutWriteAccess() +{ + QScriptEngine eng; + TestClass cls(&eng); + cls.addCustomProperty(eng.toStringHandle("x"), + QScriptClass::HandlesReadAccess, + /*id=*/0, /*flags=*/0, 123); + eng.globalObject().setProperty("o", eng.newObject(&cls)); + QCOMPARE(eng.evaluate("o.x").toInt32(), 123); + + // This will create a JS property on the instance that + // shadows the custom property. + // This behavior is not documented. It might be more + // intuitive to treat a property that only handles read + // access as a read-only, non-shadowable property. + QCOMPARE(eng.evaluate("o.x = 456; o.x").toInt32(), 456); + + QVERIFY(eng.evaluate("delete o.x").toBool()); + // Now the custom property is seen again. + QCOMPARE(eng.evaluate("o.x").toInt32(), 123); +} + void tst_QScriptClass::getProperty_invalidValue() { QScriptEngine eng; @@ -1352,5 +1438,66 @@ void tst_QScriptClass::defaultImplementations() QVERIFY(!defaultClass.extension(QScriptClass::HasInstance).isValid()); } +void tst_QScriptClass::scriptClassObjectInPrototype() +{ + QScriptEngine eng; + TestClass cls(&eng); + QScriptValue plainObject = eng.newObject(); + QScriptValue classObject = eng.newObject(&cls); + plainObject.setPrototype(classObject); + QVERIFY(plainObject.prototype().equals(classObject)); + eng.globalObject().setProperty("plainObject", plainObject); + eng.globalObject().setProperty("classObject", classObject); + + QScriptString name = eng.toStringHandle("x"); + cls.addCustomProperty(name, QScriptClass::HandlesReadAccess, /*id=*/1, /*flags=*/0, /*value=*/123); + QVERIFY(plainObject.property(name).equals(classObject.property(name))); + QVERIFY(eng.evaluate("plainObject.x == classObject.x").toBool()); + + // Add a property that shadows the one in the script class. + plainObject.setProperty(name, 456); + QVERIFY(!plainObject.property(name).equals(classObject.property(name))); + QVERIFY(eng.evaluate("plainObject.x != classObject.x").toBool()); + + QVERIFY(eng.evaluate("delete plainObject.x").toBool()); + QVERIFY(eng.evaluate("plainObject.x == classObject.x").toBool()); +} + +void tst_QScriptClass::scriptClassWithNullEngine() +{ + QScriptClass cls(0); + QCOMPARE(cls.engine(), (QScriptEngine*)0); + QScriptEngine eng; + QScriptValue obj = eng.newObject(&cls); + QVERIFY(obj.isObject()); + QCOMPARE(obj.scriptClass(), &cls); + // The class could have been "bound" to the engine at this point, + // but it's currently not. + // This behavior is not documented and is subject to change. + QCOMPARE(cls.engine(), (QScriptEngine*)0); + // The engine pointer stored in the QScriptClass is not actually used + // during property access, so this still works. + obj.setProperty("x", 123); + QVERIFY(obj.property("x").isNumber()); +} + +void tst_QScriptClass::scriptClassInOtherEngine() +{ + QScriptEngine eng; + TestClass cls(&eng); + QScriptEngine eng2; + // We don't check that the class is associated with another engine, so + // we only get a warning when trying to set the prototype of the new + // instance. + // This behavior is not documented and is subject to change. + QTest::ignoreMessage(QtWarningMsg, "QScriptValue::setPrototype() failed: cannot set a prototype created in a different engine"); + QScriptValue obj = eng2.newObject(&cls); + QVERIFY(obj.isObject()); + QCOMPARE(obj.scriptClass(), &cls); + + obj.setProperty("x", 123); + QVERIFY(obj.property("x").isNumber()); +} + QTEST_MAIN(tst_QScriptClass) #include "tst_qscriptclass.moc" -- cgit v0.12 From 74c91e9f6de56728de5472b9d7c2372e480f37b3 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 2 Feb 2011 10:29:22 +0100 Subject: Split QScriptContext autotest into smaller functions Task-number: QTBUG-16746 Reviewed-by: Jedrzej Nowacki --- tests/auto/qscriptcontext/tst_qscriptcontext.cpp | 330 +++++++++++++---------- 1 file changed, 194 insertions(+), 136 deletions(-) diff --git a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp index cb6311e..eee67df 100644 --- a/tests/auto/qscriptcontext/tst_qscriptcontext.cpp +++ b/tests/auto/qscriptcontext/tst_qscriptcontext.cpp @@ -66,7 +66,9 @@ public: private slots: void callee(); + void callee_implicitCall(); void arguments(); + void argumentsInJS(); void thisObject(); void returnValue(); void throwError_data(); @@ -77,16 +79,24 @@ private slots: void throwValue(); void evaluateInFunction(); void pushAndPopContext(); + void pushAndPopContext_variablesInActivation(); + void pushAndPopContext_setThisObject(); + void pushAndPopContext_throwException(); void lineNumber(); void backtrace_data(); void backtrace(); - void scopeChain(); - void pushAndPopScope(); - void getSetActivationObject(); + void scopeChain_globalContext(); + void scopeChain_closure(); + void scopeChain_withStatement(); + void pushAndPopScope_globalContext(); + void pushAndPopScope_globalContext2(); + void getSetActivationObject_globalContext(); void getSetActivationObject_customContext(); void inheritActivationAndThisObject(); void toString(); - void calledAsConstructor(); + void calledAsConstructor_fromCpp(); + void calledAsConstructor_fromJS(); + void calledAsConstructor_parentContext(); void argumentsObjectInNative(); void jsActivationObject(); void qobjectAsActivationObject(); @@ -121,33 +131,33 @@ void tst_QScriptContext::callee() { QScriptEngine eng; - { - QScriptValue fun = eng.newFunction(get_callee); - fun.setProperty("foo", QScriptValue(&eng, "bar")); - eng.globalObject().setProperty("get_callee", fun); + QScriptValue fun = eng.newFunction(get_callee); + fun.setProperty("foo", QScriptValue(&eng, "bar")); + eng.globalObject().setProperty("get_callee", fun); - QScriptValue result = eng.evaluate("get_callee()"); - QCOMPARE(result.isFunction(), true); - QCOMPARE(result.property("foo").toString(), QString("bar")); - } + QScriptValue result = eng.evaluate("get_callee()"); + QCOMPARE(result.isFunction(), true); + QCOMPARE(result.property("foo").toString(), QString("bar")); +} +void tst_QScriptContext::callee_implicitCall() +{ + QScriptEngine eng; // callee when toPrimitive() is called internally - { - QScriptValue fun = eng.newFunction(store_callee_and_return_primitive); - QScriptValue obj = eng.newObject(); - obj.setProperty("toString", fun); - QVERIFY(!obj.property("callee").isValid()); - (void)obj.toString(); - QVERIFY(obj.property("callee").isFunction()); - QVERIFY(obj.property("callee").strictlyEquals(fun)); - - obj.setProperty("callee", QScriptValue()); - QVERIFY(!obj.property("callee").isValid()); - obj.setProperty("valueOf", fun); - (void)obj.toNumber(); - QVERIFY(obj.property("callee").isFunction()); - QVERIFY(obj.property("callee").strictlyEquals(fun)); - } + QScriptValue fun = eng.newFunction(store_callee_and_return_primitive); + QScriptValue obj = eng.newObject(); + obj.setProperty("toString", fun); + QVERIFY(!obj.property("callee").isValid()); + (void)obj.toString(); + QVERIFY(obj.property("callee").isFunction()); + QVERIFY(obj.property("callee").strictlyEquals(fun)); + + obj.setProperty("callee", QScriptValue()); + QVERIFY(!obj.property("callee").isValid()); + obj.setProperty("valueOf", fun); + (void)obj.toNumber(); + QVERIFY(obj.property("callee").isFunction()); + QVERIFY(obj.property("callee").strictlyEquals(fun)); } static QScriptValue get_arguments(QScriptContext *ctx, QScriptEngine *eng) @@ -167,6 +177,8 @@ void tst_QScriptContext::arguments() { QScriptEngine eng; + // See section 10.6 ("Arguments Object") of ECMA-262. + { QScriptValue args = eng.currentContext()->argumentsObject(); QVERIFY(args.isObject()); @@ -178,6 +190,8 @@ void tst_QScriptContext::arguments() } for (int x = 0; x < 2; ++x) { + // The expected arguments array should be the same regardless of + // whether get_arguments() is called as a constructor. QString prefix; if (x == 0) prefix = ""; @@ -224,11 +238,15 @@ void tst_QScriptContext::arguments() QCOMPARE(result.propertyFlags("length"), QScriptValue::SkipInEnumeration); QCOMPARE(result.property("callee").strictlyEquals(fun), true); QCOMPARE(result.propertyFlags("callee"), QScriptValue::SkipInEnumeration); + + // callee and length properties should be writable. QScriptValue replacedCallee(&eng, 123); result.setProperty("callee", replacedCallee); QVERIFY(result.property("callee").equals(replacedCallee)); QScriptValue replacedLength(&eng, 456); result.setProperty("length", replacedLength); + + // callee and length properties should be deletable. QVERIFY(result.property("length").equals(replacedLength)); result.setProperty("callee", QScriptValue()); QVERIFY(!result.property("callee").isValid()); @@ -262,28 +280,31 @@ void tst_QScriptContext::arguments() QCOMPARE(result.property("2").toBoolean(), true); QCOMPARE(result.property("3").isUndefined(), true); } + } +} - // arguments object returned from script - { - QScriptValue result = eng.evaluate("(function() { return arguments; })(123)"); - QCOMPARE(result.isArray(), false); - QVERIFY(result.isObject()); - QCOMPARE(result.property("length").toUInt32(), quint32(1)); - QCOMPARE(result.property("0").isNumber(), true); - QCOMPARE(result.property("0").toNumber(), 123.0); - } +void tst_QScriptContext::argumentsInJS() +{ + QScriptEngine eng; + { + QScriptValue result = eng.evaluate("(function() { return arguments; })(123)"); + QCOMPARE(result.isArray(), false); + QVERIFY(result.isObject()); + QCOMPARE(result.property("length").toUInt32(), quint32(1)); + QCOMPARE(result.property("0").isNumber(), true); + QCOMPARE(result.property("0").toNumber(), 123.0); + } - { - QScriptValue result = eng.evaluate("(function() { return arguments; })('ciao', null, true, undefined)"); - QCOMPARE(result.isArray(), false); - QCOMPARE(result.property("length").toUInt32(), quint32(4)); - QCOMPARE(result.property("0").isString(), true); - QCOMPARE(result.property("0").toString(), QString("ciao")); - QCOMPARE(result.property("1").isNull(), true); - QCOMPARE(result.property("2").isBoolean(), true); - QCOMPARE(result.property("2").toBoolean(), true); - QCOMPARE(result.property("3").isUndefined(), true); - } + { + QScriptValue result = eng.evaluate("(function() { return arguments; })('ciao', null, true, undefined)"); + QCOMPARE(result.isArray(), false); + QCOMPARE(result.property("length").toUInt32(), quint32(4)); + QCOMPARE(result.property("0").isString(), true); + QCOMPARE(result.property("0").toString(), QString("ciao")); + QCOMPARE(result.property("1").isNull(), true); + QCOMPARE(result.property("2").isBoolean(), true); + QCOMPARE(result.property("2").toBoolean(), true); + QCOMPARE(result.property("3").isUndefined(), true); } } @@ -517,58 +538,65 @@ void tst_QScriptContext::pushAndPopContext() QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()"); eng.popContext(); QCOMPARE(eng.currentContext(), topLevel); +} - { - QScriptContext *ctx3 = eng.pushContext(); - ctx3->activationObject().setProperty("foo", QScriptValue(&eng, 123)); - QVERIFY(eng.evaluate("foo").strictlyEquals(QScriptValue(&eng, 123))); - QCOMPARE(ctx3->activationObject().propertyFlags("foo"), QScriptValue::PropertyFlags(0)); - - ctx3->activationObject().setProperty(4, 456); - QVERIFY(ctx3->activationObject().property(4, QScriptValue::ResolveLocal).equals(456)); - - eng.evaluate("var bar = 'ciao'"); - QVERIFY(ctx3->activationObject().property("bar", QScriptValue::ResolveLocal).strictlyEquals(QScriptValue(&eng, "ciao"))); - - ctx3->activationObject().setProperty("baz", 789, QScriptValue::ReadOnly); - QVERIFY(eng.evaluate("baz").equals(789)); - QCOMPARE(ctx3->activationObject().propertyFlags("baz"), QScriptValue::ReadOnly); - - QSet activationPropertyNames; - QScriptValueIterator it(ctx3->activationObject()); - while (it.hasNext()) { - it.next(); - activationPropertyNames.insert(it.name()); - } - QCOMPARE(activationPropertyNames.size(), 4); - QVERIFY(activationPropertyNames.contains("foo")); - QVERIFY(activationPropertyNames.contains("4")); - QVERIFY(activationPropertyNames.contains("bar")); - QVERIFY(activationPropertyNames.contains("baz")); - - eng.popContext(); +void tst_QScriptContext::pushAndPopContext_variablesInActivation() +{ + QScriptEngine eng; + QScriptContext *ctx = eng.pushContext(); + ctx->activationObject().setProperty("foo", QScriptValue(&eng, 123)); + // evaluate() should use the current context. + QVERIFY(eng.evaluate("foo").strictlyEquals(QScriptValue(&eng, 123))); + QCOMPARE(ctx->activationObject().propertyFlags("foo"), QScriptValue::PropertyFlags(0)); + + ctx->activationObject().setProperty(4, 456); + QVERIFY(ctx->activationObject().property(4, QScriptValue::ResolveLocal).equals(456)); + + // New JS variables should become properties of the current context's activation. + eng.evaluate("var bar = 'ciao'"); + QVERIFY(ctx->activationObject().property("bar", QScriptValue::ResolveLocal).strictlyEquals(QScriptValue(&eng, "ciao"))); + + ctx->activationObject().setProperty("baz", 789, QScriptValue::ReadOnly); + QVERIFY(eng.evaluate("baz").equals(789)); + QCOMPARE(ctx->activationObject().propertyFlags("baz"), QScriptValue::ReadOnly); + + QSet activationPropertyNames; + QScriptValueIterator it(ctx->activationObject()); + while (it.hasNext()) { + it.next(); + activationPropertyNames.insert(it.name()); } + QCOMPARE(activationPropertyNames.size(), 4); + QVERIFY(activationPropertyNames.contains("foo")); + QVERIFY(activationPropertyNames.contains("4")); + QVERIFY(activationPropertyNames.contains("bar")); + QVERIFY(activationPropertyNames.contains("baz")); - { - QScriptContext *ctx4 = eng.pushContext(); - QScriptValue obj = eng.newObject(); - obj.setProperty("prop", QScriptValue(&eng, 456)); - ctx4->setThisObject(obj); - QScriptValue ret = eng.evaluate("var tmp = this.prop; tmp + 1"); - QCOMPARE(eng.currentContext(), ctx4); - QVERIFY(ret.strictlyEquals(QScriptValue(&eng, 457))); - eng.popContext(); - } + eng.popContext(); +} - // throwing an exception - { - QScriptContext *ctx5 = eng.pushContext(); - QScriptValue ret = eng.evaluate("throw new Error('oops')"); - QVERIFY(ret.isError()); - QVERIFY(eng.hasUncaughtException()); - QCOMPARE(eng.currentContext(), ctx5); - eng.popContext(); - } +void tst_QScriptContext::pushAndPopContext_setThisObject() +{ + QScriptEngine eng; + QScriptContext *ctx = eng.pushContext(); + QScriptValue obj = eng.newObject(); + obj.setProperty("prop", QScriptValue(&eng, 456)); + ctx->setThisObject(obj); + QScriptValue ret = eng.evaluate("var tmp = this.prop; tmp + 1"); + QCOMPARE(eng.currentContext(), ctx); + QVERIFY(ret.strictlyEquals(QScriptValue(&eng, 457))); + eng.popContext(); +} + +void tst_QScriptContext::pushAndPopContext_throwException() +{ + QScriptEngine eng; + QScriptContext *ctx = eng.pushContext(); + QScriptValue ret = eng.evaluate("throw new Error('oops')"); + QVERIFY(ret.isError()); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.currentContext(), ctx); + eng.popContext(); } void tst_QScriptContext::popNativeContextScope() @@ -596,7 +624,7 @@ void tst_QScriptContext::popNativeContextScope() QVERIFY(ctx->activationObject().strictlyEquals(customScope)); QCOMPARE(ctx->scopeChain().size(), 2); QVERIFY(ctx->scopeChain().at(0).strictlyEquals(customScope)); - QEXPECT_FAIL("", "QTBUG-11012", Continue); + QEXPECT_FAIL("", "QTBUG-11012: Global object is replaced in scope chain", Continue); QVERIFY(ctx->scopeChain().at(1).strictlyEquals(eng.globalObject())); QVERIFY(!eng.evaluate("baz = 456; var foo = 789; function barbar() {}").isError()); @@ -918,7 +946,7 @@ static QScriptValue getScopeChain(QScriptContext *ctx, QScriptEngine *eng) return qScriptValueFromValue(eng, ctx->parentContext()->scopeChain()); } -void tst_QScriptContext::scopeChain() +void tst_QScriptContext::scopeChain_globalContext() { QScriptEngine eng; { @@ -932,19 +960,35 @@ void tst_QScriptContext::scopeChain() QCOMPARE(ret.size(), 1); QVERIFY(ret.at(0).strictlyEquals(eng.globalObject())); } - { - eng.evaluate("function foo() { function bar() { return getScopeChain(); } return bar() }"); - QScriptValueList ret = qscriptvalue_cast(eng.evaluate("foo()")); - QEXPECT_FAIL("", "Number of items in returned scope chain is incorrect", Abort); - QCOMPARE(ret.size(), 3); - QVERIFY(ret.at(2).strictlyEquals(eng.globalObject())); - QCOMPARE(ret.at(1).toString(), QString::fromLatin1("activation")); - QVERIFY(ret.at(1).property("arguments").isObject()); - QCOMPARE(ret.at(0).toString(), QString::fromLatin1("activation")); - QVERIFY(ret.at(0).property("arguments").isObject()); - } +} + +void tst_QScriptContext::scopeChain_closure() +{ + QScriptEngine eng; + eng.globalObject().setProperty("getScopeChain", eng.newFunction(getScopeChain)); + + eng.evaluate("function foo() { function bar() { return getScopeChain(); } return bar() }"); + QScriptValueList ret = qscriptvalue_cast(eng.evaluate("foo()")); + // JSC will not create an activation for bar() unless we insert a call + // to eval() in the function body; JSC has no way of knowing that the + // native function will be asking for the activation, and we don't want + // to needlessly create it. + QEXPECT_FAIL("", "QTBUG-10313: JSC optimizes away the activation object", Abort); + QCOMPARE(ret.size(), 3); + QVERIFY(ret.at(2).strictlyEquals(eng.globalObject())); + QCOMPARE(ret.at(1).toString(), QString::fromLatin1("activation")); + QVERIFY(ret.at(1).property("arguments").isObject()); + QCOMPARE(ret.at(0).toString(), QString::fromLatin1("activation")); + QVERIFY(ret.at(0).property("arguments").isObject()); +} + +void tst_QScriptContext::scopeChain_withStatement() +{ + QScriptEngine eng; + eng.globalObject().setProperty("getScopeChain", eng.newFunction(getScopeChain)); { QScriptValueList ret = qscriptvalue_cast(eng.evaluate("o = { x: 123 }; with(o) getScopeChain();")); + QEXPECT_FAIL("", "QTBUG-17131: with-scope isn't reflected by QScriptContext", Abort); QCOMPARE(ret.size(), 2); QVERIFY(ret.at(1).strictlyEquals(eng.globalObject())); QVERIFY(ret.at(0).isObject()); @@ -962,7 +1006,7 @@ void tst_QScriptContext::scopeChain() } } -void tst_QScriptContext::pushAndPopScope() +void tst_QScriptContext::pushAndPopScope_globalContext() { QScriptEngine eng; QScriptContext *ctx = eng.currentContext(); @@ -1019,14 +1063,19 @@ void tst_QScriptContext::pushAndPopScope() ctx->pushScope(QScriptValue()); QCOMPARE(ctx->scopeChain().size(), 1); +} +void tst_QScriptContext::pushAndPopScope_globalContext2() +{ + QScriptEngine eng; + QScriptContext *ctx = eng.currentContext(); QVERIFY(ctx->popScope().strictlyEquals(eng.globalObject())); QVERIFY(ctx->scopeChain().isEmpty()); // Used to work with old back-end, doesn't with new one because JSC requires that the last object in // a scope chain is the Global Object. QTest::ignoreMessage(QtWarningMsg, "QScriptContext::pushScope() failed: initial object in scope chain has to be the Global Object"); - ctx->pushScope(obj); + ctx->pushScope(eng.newObject()); QCOMPARE(ctx->scopeChain().size(), 0); QScriptEngine eng2; @@ -1043,7 +1092,7 @@ static QScriptValue get_activationObject(QScriptContext *ctx, QScriptEngine *) return ctx->activationObject(); } -void tst_QScriptContext::getSetActivationObject() +void tst_QScriptContext::getSetActivationObject_globalContext() { QScriptEngine eng; QScriptContext *ctx = eng.currentContext(); @@ -1076,7 +1125,7 @@ void tst_QScriptContext::getSetActivationObject() QScriptValue ret = eng.evaluate("get_activationObject(1, 2, 3)"); QVERIFY(ret.isObject()); QScriptValue arguments = ret.property("arguments"); - QEXPECT_FAIL("", "Getting arguments property of activation object doesn't work", Abort); + QEXPECT_FAIL("", "QTBUG-17136: arguments property of activation object is missing", Abort); QVERIFY(arguments.isObject()); QCOMPARE(arguments.property("length").toInt32(), 3); QCOMPARE(arguments.property("0").toInt32(), 1); @@ -1099,6 +1148,8 @@ void tst_QScriptContext::getSetActivationObject_customContext() QCOMPARE(act.property("foo").toInt32(), 123); } +// Helper function that's intended to have the same behavior +// as the built-in eval() function. static QScriptValue myEval(QScriptContext *ctx, QScriptEngine *eng) { QString code = ctx->argument(0).toString(); @@ -1127,13 +1178,14 @@ void tst_QScriptContext::inheritActivationAndThisObject() QCOMPARE(ret.toInt32(), 123); } - // QT-2219 { eng.globalObject().setProperty("a", 123); QScriptValue ret = eng.evaluate("(function() { myEval('var a = 456'); return a; })()"); QVERIFY(ret.isNumber()); QCOMPARE(ret.toInt32(), 456); - QEXPECT_FAIL("", "QT-2219: Wrong activation object is returned from native function's parent context", Continue); + // Since JSC doesn't create an activation object for the anonymous function call, + // myEval() will use the global object as the activation, which is wrong. + QEXPECT_FAIL("", "QTBUG-10313: Wrong activation object is returned from native function's parent context", Continue); QVERIFY(eng.globalObject().property("a").strictlyEquals(123)); } } @@ -1172,11 +1224,11 @@ static QScriptValue storeCalledAsConstructorV3(QScriptContext *ctx, QScriptEngin return eng->undefinedValue(); } -void tst_QScriptContext::calledAsConstructor() +void tst_QScriptContext::calledAsConstructor_fromCpp() { QScriptEngine eng; - QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor); { + QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor); fun1.call(); QVERIFY(!fun1.property("calledAsConstructor").toBool()); fun1.construct(); @@ -1189,25 +1241,31 @@ void tst_QScriptContext::calledAsConstructor() fun.construct(); QVERIFY(fun.property("calledAsConstructor").toBool()); } - { - eng.globalObject().setProperty("fun1", fun1); - eng.evaluate("fun1();"); - QVERIFY(!fun1.property("calledAsConstructor").toBool()); - eng.evaluate("new fun1();"); - QVERIFY(fun1.property("calledAsConstructor").toBool()); - } - { - QScriptValue fun3 = eng.newFunction(storeCalledAsConstructorV3); - eng.globalObject().setProperty("fun3", fun3); - eng.evaluate("function test() { fun3() }"); - eng.evaluate("test();"); - QVERIFY(!fun3.property("calledAsConstructor").toBool()); - eng.evaluate("new test();"); - if (qt_script_isJITEnabled()) - QEXPECT_FAIL("", "QTBUG-6132: calledAsConstructor is not correctly set for JS functions when JIT is enabled", Continue); - QVERIFY(fun3.property("calledAsConstructor").toBool()); - } +} + +void tst_QScriptContext::calledAsConstructor_fromJS() +{ + QScriptEngine eng; + QScriptValue fun1 = eng.newFunction(storeCalledAsConstructor); + eng.globalObject().setProperty("fun1", fun1); + eng.evaluate("fun1();"); + QVERIFY(!fun1.property("calledAsConstructor").toBool()); + eng.evaluate("new fun1();"); + QVERIFY(fun1.property("calledAsConstructor").toBool()); +} +void tst_QScriptContext::calledAsConstructor_parentContext() +{ + QScriptEngine eng; + QScriptValue fun3 = eng.newFunction(storeCalledAsConstructorV3); + eng.globalObject().setProperty("fun3", fun3); + eng.evaluate("function test() { fun3() }"); + eng.evaluate("test();"); + QVERIFY(!fun3.property("calledAsConstructor").toBool()); + eng.evaluate("new test();"); + if (qt_script_isJITEnabled()) + QEXPECT_FAIL("", "QTBUG-6132: calledAsConstructor is not correctly set for JS functions when JIT is enabled", Continue); + QVERIFY(fun3.property("calledAsConstructor").toBool()); } static QScriptValue argumentsObjectInNative_test1(QScriptContext *ctx, QScriptEngine *eng) @@ -1269,7 +1327,7 @@ void tst_QScriptContext::jsActivationObject() QScriptValue result1 = eng.evaluate("f2('hello', 'useless', 'world')"); QScriptValue result2 = eng.evaluate("f3()"); QVERIFY(result1.isObject()); - QEXPECT_FAIL("", "JSC optimize away the activation object", Abort); + QEXPECT_FAIL("", "QTBUG-10313: JSC optimizes away the activation object", Abort); QCOMPARE(result1.property("v1").toInt32() , 42); QCOMPARE(result1.property("arguments").property(1).toString() , QString::fromLatin1("useless")); QVERIFY(result2.isObject()); -- cgit v0.12 From e3be9077b284174a97bff82f52ffbfda11369d18 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 2 Feb 2011 15:18:33 +0100 Subject: Split QScriptEngine test into smaller functions Also add comments to clarify what we're testing, and remove dependency on precise error messages (we don't care what the precise wording of an error is, only that it is e.g. a TypeError). Task-number: QTBUG-16746 Reviewed-by: Jedrzej Nowacki --- tests/auto/qscriptengine/tst_qscriptengine.cpp | 856 +++++++++++++++---------- 1 file changed, 529 insertions(+), 327 deletions(-) diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp index 956f06c..2f7e0b5 100644 --- a/tests/auto/qscriptengine/tst_qscriptengine.cpp +++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp @@ -93,7 +93,8 @@ private slots: void constructWithParent(); void currentContext(); void pushPopContext(); - void getSetDefaultPrototype(); + void getSetDefaultPrototype_int(); + void getSetDefaultPrototype_customType(); void newFunction(); void newFunctionWithArg(); void newFunctionWithProto(); @@ -109,7 +110,9 @@ private slots: void newVariant_promoteNonObject(); void newVariant_promoteNonQScriptObject(); void newRegExp(); + void jsRegExp(); void newDate(); + void jsParseDate(); void newQObject(); void newQObject_ownership(); void newQObject_promoteObject(); @@ -121,6 +124,7 @@ private slots: void newActivationObject(); void getSetGlobalObject(); void globalObjectProperties(); + void globalObjectProperties_enumerate(); void createGlobalObjectProperty(); void globalObjectGetterSetterProperty(); void customGlobalObjectWithPrototype(); @@ -136,7 +140,14 @@ private slots: void nestedEvaluate(); void uncaughtException(); void errorMessage_QT679(); - void valueConversion(); + void valueConversion_basic(); + void valueConversion_customType(); + void valueConversion_sequence(); + void valueConversion_QVariant(); + void valueConversion_hooliganTask248802(); + void valueConversion_basic2(); + void valueConversion_dateTime(); + void valueConversion_regExp(); void qScriptValueFromValue_noEngine(); void importExtension(); void infiniteRecursion(); @@ -152,32 +163,51 @@ private slots: void numberParsing_data(); void numberParsing(); void automaticSemicolonInsertion(); + void abortEvaluation_notEvaluating(); void abortEvaluation(); + void abortEvaluation_tryCatch(); + void abortEvaluation_fromNative(); void abortEvaluation_QTBUG9433(); - void isEvaluating(); + void isEvaluating_notEvaluating(); + void isEvaluating_fromNative(); + void isEvaluating_fromEvent(); void printFunctionWithCustomHandler(); void printThrowsException(); void errorConstructors(); - void argumentsProperty(); - void numberClass(); - void forInStatement(); - void functionExpression(); + void argumentsProperty_globalContext(); + void argumentsProperty_JS(); + void argumentsProperty_evaluateInNativeFunction(); + void jsNumberClass(); + void jsForInStatement_simple(); + void jsForInStatement_prototypeProperties(); + void jsForInStatement_mutateWhileIterating(); + void jsForInStatement_arrays(); + void jsForInStatement_nullAndUndefined(); + void jsFunctionDeclarationAsStatement(); void stringObjects(); + void jsStringPrototypeReplaceBugs(); void getterSetterThisObject_global(); void getterSetterThisObject_plain(); void getterSetterThisObject_prototypeChain(); void getterSetterThisObject_activation(); - void continueInSwitch(); - void readOnlyPrototypeProperty(); + void jsContinueInSwitch(); + void jsShadowReadOnlyPrototypeProperty(); void toObject(); - void reservedWords_data(); - void reservedWords(); - void futureReservedWords_data(); - void futureReservedWords(); - void throwInsideWithStatement(); - void getSetAgent(); - void reentrancy(); - void incDecNonObjectProperty(); + void jsReservedWords_data(); + void jsReservedWords(); + void jsFutureReservedWords_data(); + void jsFutureReservedWords(); + void jsThrowInsideWithStatement(); + void getSetAgent_ownership(); + void getSetAgent_deleteAgent(); + void getSetAgent_differentEngine(); + void reentrancy_stringHandles(); + void reentrancy_processEventsInterval(); + void reentrancy_typeConversion(); + void reentrancy_globalObjectProperties(); + void reentrancy_Array(); + void reentrancy_objectCreation(); + void jsIncDecNonObjectProperty(); void installTranslatorFunctions_data(); void installTranslatorFunctions(); void translateScript_data(); @@ -609,54 +639,65 @@ void tst_QScriptEngine::newRegExp() QCOMPARE(rexp.toRegExp().pattern(), QRegExp("foo").pattern()); } - { - QScriptValue r = eng.evaluate("/foo/gim"); - QVERIFY(r.isRegExp()); - QCOMPARE(r.toString(), QString::fromLatin1("/foo/gim")); +} + +void tst_QScriptEngine::jsRegExp() +{ + // See ECMA-262 Section 15.10, "RegExp Objects". + // These should really be JS-only tests, as they test the implementation's + // ECMA-compliance, not the C++ API. Compliance should already be covered + // by the Mozilla tests (qscriptjstestsuite). + // We can consider updating the expected results of this test if the + // RegExp implementation changes. - QScriptValue rxCtor = eng.globalObject().property("RegExp"); - QScriptValue r2 = rxCtor.call(QScriptValue(), QScriptValueList() << r); - QVERIFY(r2.isRegExp()); - QVERIFY(r2.strictlyEquals(r)); + QScriptEngine eng; + QScriptValue r = eng.evaluate("/foo/gim"); + QVERIFY(r.isRegExp()); + QCOMPARE(r.toString(), QString::fromLatin1("/foo/gim")); - QScriptValue r3 = rxCtor.call(QScriptValue(), QScriptValueList() << r << "gim"); - QVERIFY(r3.isError()); - QCOMPARE(r3.toString(), QString::fromLatin1("TypeError: Cannot supply flags when constructing one RegExp from another.")); + QScriptValue rxCtor = eng.globalObject().property("RegExp"); + QScriptValue r2 = rxCtor.call(QScriptValue(), QScriptValueList() << r); + QVERIFY(r2.isRegExp()); + QVERIFY(r2.strictlyEquals(r)); - QScriptValue r4 = rxCtor.call(QScriptValue(), QScriptValueList() << "foo" << "gim"); - QVERIFY(r4.isRegExp()); + QScriptValue r3 = rxCtor.call(QScriptValue(), QScriptValueList() << r << "gim"); + QVERIFY(r3.isError()); + QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another - QScriptValue r5 = rxCtor.construct(QScriptValueList() << r); - QVERIFY(r5.isRegExp()); - QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim")); - // In JSC, constructing a RegExp from another produces the same identical object. - // This is different from SpiderMonkey and old back-end. - QVERIFY(r5.strictlyEquals(r)); + QScriptValue r4 = rxCtor.call(QScriptValue(), QScriptValueList() << "foo" << "gim"); + QVERIFY(r4.isRegExp()); - QScriptValue r6 = rxCtor.construct(QScriptValueList() << "foo" << "bar"); - QVERIFY(r6.isError()); - QCOMPARE(r6.toString(), QString::fromLatin1("SyntaxError: Invalid regular expression: invalid regular expression flag")); + QScriptValue r5 = rxCtor.construct(QScriptValueList() << r); + QVERIFY(r5.isRegExp()); + QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim")); + // In JSC, constructing a RegExp from another produces the same identical object. + // This is different from SpiderMonkey and old back-end. + QVERIFY(r5.strictlyEquals(r)); - QScriptValue r7 = eng.evaluate("/foo/gimp"); - QVERIFY(r7.isError()); - QCOMPARE(r7.toString(), QString::fromLatin1("SyntaxError: Invalid regular expression: invalid regular expression flag")); + QScriptValue r6 = rxCtor.construct(QScriptValueList() << "foo" << "bar"); + QVERIFY(r6.isError()); + QVERIFY(r6.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag - QScriptValue r8 = eng.evaluate("/foo/migmigmig"); - QVERIFY(r8.isRegExp()); - QCOMPARE(r8.toString(), QString::fromLatin1("/foo/gim")); + QScriptValue r7 = eng.evaluate("/foo/gimp"); + QVERIFY(r7.isError()); + QVERIFY(r7.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag - QScriptValue r9 = rxCtor.construct(); - QVERIFY(r9.isRegExp()); - QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/")); + // JSC doesn't complain about duplicate flags. + QScriptValue r8 = eng.evaluate("/foo/migmigmig"); + QVERIFY(r8.isRegExp()); + QCOMPARE(r8.toString(), QString::fromLatin1("/foo/gim")); - QScriptValue r10 = rxCtor.construct(QScriptValueList() << "" << "gim"); - QVERIFY(r10.isRegExp()); - QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim")); + QScriptValue r9 = rxCtor.construct(); + QVERIFY(r9.isRegExp()); + QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/")); - QScriptValue r11 = rxCtor.construct(QScriptValueList() << "{1.*}" << "g"); - QVERIFY(r11.isRegExp()); - QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g")); - } + QScriptValue r10 = rxCtor.construct(QScriptValueList() << "" << "gim"); + QVERIFY(r10.isRegExp()); + QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim")); + + QScriptValue r11 = rxCtor.construct(QScriptValueList() << "{1.*}" << "g"); + QVERIFY(r11.isRegExp()); + QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g")); } void tst_QScriptEngine::newDate() @@ -695,7 +736,11 @@ void tst_QScriptEngine::newDate() // toDateTime() result should be in local time QCOMPARE(date.toDateTime(), dt.toLocalTime()); } +} +void tst_QScriptEngine::jsParseDate() +{ + QScriptEngine eng; // Date.parse() should return NaN when it fails { QScriptValue ret = eng.evaluate("Date.parse()"); @@ -1196,6 +1241,8 @@ static QScriptValue getSetFoo(QScriptContext *ctx, QScriptEngine *) void tst_QScriptEngine::globalObjectProperties() { + // See ECMA-262 Section 15.1, "The Global Object". + QScriptEngine eng; QScriptValue global = eng.globalObject(); @@ -1271,8 +1318,13 @@ void tst_QScriptEngine::globalObjectProperties() QVERIFY(!global.property("Math").isFunction()); QEXPECT_FAIL("", "[ECMA compliance] JSC sets DontDelete flag for Math object", Continue); QCOMPARE(global.propertyFlags("Math"), QScriptValue::SkipInEnumeration); +} + +void tst_QScriptEngine::globalObjectProperties_enumerate() +{ + QScriptEngine eng; + QScriptValue global = eng.globalObject(); - // enumeration QSet expectedNames; expectedNames << "isNaN" @@ -1500,7 +1552,7 @@ void tst_QScriptEngine::globalObjectWithCustomPrototype() { QScriptValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a"); QVERIFY(ret.isNumber()); - QEXPECT_FAIL("", "QTBUG-9737", Continue); + QEXPECT_FAIL("", "QTBUG-9737: Prototype change in JS not reflected on C++ side", Continue); QVERIFY(ret.strictlyEquals(global.property("a"))); } } @@ -1510,7 +1562,9 @@ void tst_QScriptEngine::builtinFunctionNames_data() QTest::addColumn("expression"); QTest::addColumn("expectedName"); - QTest::newRow("print") << QString("print") << QString("print"); + // See ECMA-262 Chapter 15, "Standard Built-in ECMAScript Objects". + + QTest::newRow("print") << QString("print") << QString("print"); // QtScript extension. QTest::newRow("parseInt") << QString("parseInt") << QString("parseInt"); QTest::newRow("parseFloat") << QString("parseFloat") << QString("parseFloat"); QTest::newRow("isNaN") << QString("isNaN") << QString("isNaN"); @@ -1521,8 +1575,8 @@ void tst_QScriptEngine::builtinFunctionNames_data() QTest::newRow("encodeURIComponent") << QString("encodeURIComponent") << QString("encodeURIComponent"); QTest::newRow("escape") << QString("escape") << QString("escape"); QTest::newRow("unescape") << QString("unescape") << QString("unescape"); - QTest::newRow("version") << QString("version") << QString("version"); - QTest::newRow("gc") << QString("gc") << QString("gc"); + QTest::newRow("version") << QString("version") << QString("version"); // QtScript extension. + QTest::newRow("gc") << QString("gc") << QString("gc"); // QtScript extension. QTest::newRow("Array") << QString("Array") << QString("Array"); QTest::newRow("Array.prototype.toString") << QString("Array.prototype.toString") << QString("toString"); @@ -1673,6 +1727,7 @@ void tst_QScriptEngine::builtinFunctionNames() QFETCH(QString, expression); QFETCH(QString, expectedName); QScriptEngine eng; + // The "name" property is actually non-standard, but JSC supports it. QScriptValue ret = eng.evaluate(QString::fromLatin1("%0.name").arg(expression)); QVERIFY(ret.isString()); QCOMPARE(ret.toString(), expectedName); @@ -1894,22 +1949,24 @@ static QScriptValue eval_nested(QScriptContext *ctx, QScriptEngine *eng) return result; } +// Tests that nested evaluate uses the "this" that was passed. void tst_QScriptEngine::nestedEvaluate() { QScriptEngine eng; QScriptValue fun = eng.newFunction(eval_nested); eng.globalObject().setProperty("fun", fun); + // From JS function call { QScriptValue result = eng.evaluate("o = { id:'foo'}; o.fun = fun; o.fun()"); QCOMPARE(result.property("local_bar").toString(), QString("local")); QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo")); QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo")); QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo")); - QScriptValue bar = eng.evaluate("bar"); + QScriptValue bar = eng.evaluate("bar"); // Was introduced in local scope. QVERIFY(bar.isError()); - QCOMPARE(bar.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bar")); + QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError"))); } - + // From QScriptValue::call() { QScriptValue result = fun.call(eng.evaluate("p = { id:'foo' }") , QScriptValueList() ); QCOMPARE(result.property("local_bar").toString(), QString("local")); @@ -1918,7 +1975,7 @@ void tst_QScriptEngine::nestedEvaluate() QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo")); QScriptValue bar = eng.evaluate("bar"); QVERIFY(bar.isError()); - QCOMPARE(bar.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bar")); + QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError"))); } } @@ -1985,6 +2042,7 @@ void tst_QScriptEngine::errorMessage_QT679() engine.globalObject().setProperty("foo", 15); QScriptValue error = engine.evaluate("'hello world';\nfoo.bar.blah"); QVERIFY(error.isError()); + // The exact message is back-end specific and subject to change. QCOMPARE(error.toString(), QString::fromLatin1("TypeError: Result of expression 'foo.bar' [undefined] is not an object.")); } @@ -1997,37 +2055,40 @@ public: Q_DECLARE_METATYPE(Foo) Q_DECLARE_METATYPE(Foo*) -void tst_QScriptEngine::getSetDefaultPrototype() +void tst_QScriptEngine::getSetDefaultPrototype_int() { QScriptEngine eng; - { - QScriptValue object = eng.newObject(); - QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); - eng.setDefaultPrototype(qMetaTypeId(), object); - QCOMPARE(eng.defaultPrototype(qMetaTypeId()).strictlyEquals(object), true); - QScriptValue value = eng.newVariant(int(123)); - QCOMPARE(value.prototype().isObject(), true); - QCOMPARE(value.prototype().strictlyEquals(object), true); - eng.setDefaultPrototype(qMetaTypeId(), QScriptValue()); - QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); - QScriptValue value2 = eng.newVariant(int(123)); - QCOMPARE(value2.prototype().strictlyEquals(object), false); - } - { - QScriptValue object = eng.newObject(); - QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); - eng.setDefaultPrototype(qMetaTypeId(), object); - QCOMPARE(eng.defaultPrototype(qMetaTypeId()).strictlyEquals(object), true); - QScriptValue value = eng.newVariant(qVariantFromValue(Foo())); - QCOMPARE(value.prototype().isObject(), true); - QCOMPARE(value.prototype().strictlyEquals(object), true); + QScriptValue object = eng.newObject(); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); + eng.setDefaultPrototype(qMetaTypeId(), object); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).strictlyEquals(object), true); + QScriptValue value = eng.newVariant(int(123)); + QCOMPARE(value.prototype().isObject(), true); + QCOMPARE(value.prototype().strictlyEquals(object), true); - eng.setDefaultPrototype(qMetaTypeId(), QScriptValue()); - QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); - QScriptValue value2 = eng.newVariant(qVariantFromValue(Foo())); - QCOMPARE(value2.prototype().strictlyEquals(object), false); - } + eng.setDefaultPrototype(qMetaTypeId(), QScriptValue()); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); + QScriptValue value2 = eng.newVariant(int(123)); + QCOMPARE(value2.prototype().strictlyEquals(object), false); +} + +void tst_QScriptEngine::getSetDefaultPrototype_customType() +{ + QScriptEngine eng; + + QScriptValue object = eng.newObject(); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); + eng.setDefaultPrototype(qMetaTypeId(), object); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).strictlyEquals(object), true); + QScriptValue value = eng.newVariant(qVariantFromValue(Foo())); + QCOMPARE(value.prototype().isObject(), true); + QCOMPARE(value.prototype().strictlyEquals(object), true); + + eng.setDefaultPrototype(qMetaTypeId(), QScriptValue()); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); + QScriptValue value2 = eng.newVariant(qVariantFromValue(Foo())); + QCOMPARE(value2.prototype().strictlyEquals(object), false); } static QScriptValue fooToScriptValue(QScriptEngine *eng, const Foo &foo) @@ -2061,7 +2122,7 @@ Q_DECLARE_METATYPE(QStack) Q_DECLARE_METATYPE(QQueue) Q_DECLARE_METATYPE(QLinkedList >) -void tst_QScriptEngine::valueConversion() +void tst_QScriptEngine::valueConversion_basic() { QScriptEngine eng; { @@ -2123,7 +2184,11 @@ void tst_QScriptEngine::valueConversion() QCOMPARE(qScriptValueToValue(code), c); QCOMPARE(qScriptValueToValue(qScriptValueFromValue(&eng, c)), c); } +} +void tst_QScriptEngine::valueConversion_customType() +{ + QScriptEngine eng; { // a type that we don't have built-in conversion of // (it's stored as a variant) @@ -2169,7 +2234,11 @@ void tst_QScriptEngine::valueConversion() QVERIFY(fooVal2.property("x").strictlyEquals(QScriptValue(&eng, 56))); QVERIFY(fooVal2.property("y").strictlyEquals(QScriptValue(&eng, 78))); } +} +void tst_QScriptEngine::valueConversion_sequence() +{ + QScriptEngine eng; qScriptRegisterSequenceMetaType >(&eng); { @@ -2248,7 +2317,11 @@ void tst_QScriptEngine::valueConversion() QCOMPARE(qscriptvalue_cast(val), lst); } +} +void tst_QScriptEngine::valueConversion_QVariant() +{ + QScriptEngine eng; // qScriptValueFromValue() should be "smart" when the argument is a QVariant { QScriptValue val = qScriptValueFromValue(&eng, QVariant()); @@ -2325,7 +2398,12 @@ void tst_QScriptEngine::valueConversion() QCOMPARE(val.toVariant(), var); } - // task 248802 + QCOMPARE(qscriptvalue_cast(QScriptValue(123)), QVariant(123)); +} + +void tst_QScriptEngine::valueConversion_hooliganTask248802() +{ + QScriptEngine eng; qScriptRegisterMetaType(&eng, fooToScriptValueV2, fooFromScriptValueV2); { QScriptValue num(&eng, 123); @@ -2343,6 +2421,11 @@ void tst_QScriptEngine::valueConversion() QCOMPARE(foo.x, 123); } +} + +void tst_QScriptEngine::valueConversion_basic2() +{ + QScriptEngine eng; // more built-in types { QScriptValue val = qScriptValueFromValue(&eng, uint(123)); @@ -2379,6 +2462,11 @@ void tst_QScriptEngine::valueConversion() QVERIFY(val.isNumber()); QCOMPARE(val.toInt32(), 123); } +} + +void tst_QScriptEngine::valueConversion_dateTime() +{ + QScriptEngine eng; { QDateTime in = QDateTime::currentDateTime(); QScriptValue val = qScriptValueFromValue(&eng, in); @@ -2391,6 +2479,11 @@ void tst_QScriptEngine::valueConversion() QVERIFY(val.isDate()); QCOMPARE(val.toDateTime().date(), in); } +} + +void tst_QScriptEngine::valueConversion_regExp() +{ + QScriptEngine eng; { QRegExp in = QRegExp("foo"); QScriptValue val = qScriptValueFromValue(&eng, in); @@ -2416,8 +2509,6 @@ void tst_QScriptEngine::valueConversion() QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::minimal (always false)", Continue); QCOMPARE(val.toRegExp().isMinimal(), in.isMinimal()); } - - QCOMPARE(qscriptvalue_cast(QScriptValue(123)), QVariant(123)); } void tst_QScriptEngine::qScriptValueFromValue_noEngine() @@ -2522,7 +2613,7 @@ void tst_QScriptEngine::importExtension() QEXPECT_FAIL("", "JSC throws syntax error eagerly", Continue); QCOMPARE(eng.uncaughtExceptionLineNumber(), 4); QVERIFY(ret.isError()); - QCOMPARE(ret.property("message").toString(), QLatin1String("Parse error")); + QVERIFY(ret.toString().contains(QLatin1String("SyntaxError"))); } QStringList imp = eng.importedExtensions(); QCOMPARE(imp.size(), 2); @@ -2548,6 +2639,9 @@ static QScriptValue recurse2(QScriptContext *ctx, QScriptEngine *eng) void tst_QScriptEngine::infiniteRecursion() { + // Infinite recursion in JS should cause the VM to throw an error + // when the JS stack is exhausted. + // The exact error is back-end specific and subject to change. const QString stackOverflowError = QString::fromLatin1("RangeError: Maximum call stack size exceeded."); QScriptEngine eng; { @@ -2610,6 +2704,8 @@ void tst_QScriptEngine::castWithPrototypeChain() baz2.b = 456; QScriptValue baz2Value = qScriptValueFromValue(&eng, &baz2); { + // qscriptvalue_cast() does magic; if the QScriptValue contains + // t of type T, and the target type is T*, &t is returned. Baz *pbaz = qscriptvalue_cast(baz2Value); QVERIFY(pbaz != 0); QCOMPARE(pbaz->b, baz2.b); @@ -2632,6 +2728,13 @@ void tst_QScriptEngine::castWithPrototypeChain() } // establish chain -- now casting should work + // Why? because qscriptvalue_cast() does magic again. + // It the instance itself is not of type T, qscriptvalue_cast() + // searches the prototype chain for T, and if it finds one, it infers + // that the instance can also be casted to that type. This cast is + // _not_ safe and thus relies on the developer doing the right thing. + // This is an undocumented feature to enable qscriptvalue_cast() to + // be used by prototype functions to cast the JS this-object to C++. bazProto.setPrototype(barProto); { @@ -2740,6 +2843,9 @@ void tst_QScriptEngine::collectGarbage() void tst_QScriptEngine::reportAdditionalMemoryCost() { QScriptEngine eng; + // There isn't any reliable way to test whether calling + // this function affects garbage collection responsiveness; + // the best we can do is call it with a few different values. for (int x = 0; x < 1000; ++x) { eng.reportAdditionalMemoryCost(0); eng.reportAdditionalMemoryCost(10); @@ -2757,6 +2863,8 @@ void tst_QScriptEngine::reportAdditionalMemoryCost() void tst_QScriptEngine::gcWithNestedDataStructure() { + // The GC must be able to traverse deeply nested objects, otherwise this + // test would crash. QScriptEngine eng; eng.evaluate( "function makeList(size)" @@ -2778,6 +2886,7 @@ void tst_QScriptEngine::gcWithNestedDataStructure() if (x == 1) eng.evaluate("gc()"); QScriptValue l = head; + // Make sure all the nodes are still alive. for (int i = 0; i < 200; ++i) { QCOMPARE(l.property("data").toString(), QString::number(i)); l = l.property("next"); @@ -2807,6 +2916,8 @@ void tst_QScriptEngine::processEventsWhileRunning() if (x == 0) eng.pushContext(); + // This is running for a silly amount of time just to make sure + // the script doesn't finish before event processing is triggered. QString script = QString::fromLatin1( "var end = Number(new Date()) + 2000;" "var x = 0;" @@ -3012,6 +3123,8 @@ void tst_QScriptEngine::numberParsing() } // see ECMA-262, section 7.9 +// This is testing ECMA compliance, not our C++ API, but it's important that +// the back-end is conformant in this regard. void tst_QScriptEngine::automaticSemicolonInsertion() { QScriptEngine eng; @@ -3257,7 +3370,7 @@ static QScriptValue myFunctionAbortingEvaluation(QScriptContext *, QScriptEngine return eng->nullValue(); // should be ignored } -void tst_QScriptEngine::abortEvaluation() +void tst_QScriptEngine::abortEvaluation_notEvaluating() { QScriptEngine eng; @@ -3270,7 +3383,11 @@ void tst_QScriptEngine::abortEvaluation() QVERIFY(ret.isString()); QCOMPARE(ret.toString(), QString::fromLatin1("ciao")); } +} +void tst_QScriptEngine::abortEvaluation() +{ + QScriptEngine eng; EventReceiver3 receiver(&eng); eng.setProcessEventsInterval(100); @@ -3301,6 +3418,13 @@ void tst_QScriptEngine::abortEvaluation() } } +} + +void tst_QScriptEngine::abortEvaluation_tryCatch() +{ + QScriptEngine eng; + EventReceiver3 receiver(&eng); + eng.setProcessEventsInterval(100); // scripts cannot intercept the abortion with try/catch for (int y = 0; y < 4; ++y) { QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1))); @@ -3334,13 +3458,15 @@ void tst_QScriptEngine::abortEvaluation() break; } } +} - { - QScriptValue fun = eng.newFunction(myFunctionAbortingEvaluation); - eng.globalObject().setProperty("myFunctionAbortingEvaluation", fun); - QScriptValue ret = eng.evaluate("myFunctionAbortingEvaluation()"); - QVERIFY(!ret.isValid()); - } +void tst_QScriptEngine::abortEvaluation_fromNative() +{ + QScriptEngine eng; + QScriptValue fun = eng.newFunction(myFunctionAbortingEvaluation); + eng.globalObject().setProperty("myFunctionAbortingEvaluation", fun); + QScriptValue ret = eng.evaluate("myFunctionAbortingEvaluation()"); + QVERIFY(!ret.isValid()); } class ThreadedEngine : public QThread { @@ -3407,7 +3533,7 @@ public: bool wasEvaluating; }; -void tst_QScriptEngine::isEvaluating() +void tst_QScriptEngine::isEvaluating_notEvaluating() { QScriptEngine eng; @@ -3419,30 +3545,34 @@ void tst_QScriptEngine::isEvaluating() QVERIFY(!eng.isEvaluating()); eng.evaluate("0 = 0"); QVERIFY(!eng.isEvaluating()); +} - { - QScriptValue fun = eng.newFunction(myFunctionReturningIsEvaluating); - eng.globalObject().setProperty("myFunctionReturningIsEvaluating", fun); - QScriptValue ret = eng.evaluate("myFunctionReturningIsEvaluating()"); - QVERIFY(ret.isBoolean()); - QVERIFY(ret.toBoolean()); - } +void tst_QScriptEngine::isEvaluating_fromNative() +{ + QScriptEngine eng; + QScriptValue fun = eng.newFunction(myFunctionReturningIsEvaluating); + eng.globalObject().setProperty("myFunctionReturningIsEvaluating", fun); + QScriptValue ret = eng.evaluate("myFunctionReturningIsEvaluating()"); + QVERIFY(ret.isBoolean()); + QVERIFY(ret.toBoolean()); +} - { - EventReceiver4 receiver(&eng); - QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1))); +void tst_QScriptEngine::isEvaluating_fromEvent() +{ + QScriptEngine eng; + EventReceiver4 receiver(&eng); + QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1))); - QString script = QString::fromLatin1( - "var end = Number(new Date()) + 1000;" - "var x = 0;" - "while (Number(new Date()) < end) {" - " ++x;" - "}"); + QString script = QString::fromLatin1( + "var end = Number(new Date()) + 1000;" + "var x = 0;" + "while (Number(new Date()) < end) {" + " ++x;" + "}"); - eng.setProcessEventsInterval(100); - eng.evaluate(script); - QVERIFY(receiver.wasEvaluating); - } + eng.setProcessEventsInterval(100); + eng.evaluate(script); + QVERIFY(receiver.wasEvaluating); } static QtMsgType theMessageType; @@ -3456,24 +3586,33 @@ static void myMsgHandler(QtMsgType type, const char *msg) void tst_QScriptEngine::printFunctionWithCustomHandler() { + // The built-in print() function passes the output to Qt's message + // handler. By installing a custom message handler, the output can be + // redirected without changing the print() function itself. + // This behavior is not documented. QScriptEngine eng; QtMsgHandler oldHandler = qInstallMsgHandler(myMsgHandler); QVERIFY(eng.globalObject().property("print").isFunction()); + theMessageType = QtSystemMsg; QVERIFY(theMessage.isEmpty()); QVERIFY(eng.evaluate("print('test')").isUndefined()); QCOMPARE(theMessageType, QtDebugMsg); QCOMPARE(theMessage, QString::fromLatin1("test")); + theMessageType = QtSystemMsg; theMessage.clear(); QVERIFY(eng.evaluate("print(3, true, 'little pigs')").isUndefined()); QCOMPARE(theMessageType, QtDebugMsg); QCOMPARE(theMessage, QString::fromLatin1("3 true little pigs")); + qInstallMsgHandler(oldHandler); } void tst_QScriptEngine::printThrowsException() { + // If an argument to print() causes an exception to be thrown when + // it's converted to a string, print() should propagate the exception. QScriptEngine eng; QScriptValue ret = eng.evaluate("print({ toString: function() { throw 'foo'; } });"); QVERIFY(eng.hasUncaughtException()); @@ -3506,34 +3645,30 @@ void tst_QScriptEngine::errorConstructors() } } -static QScriptValue argumentsProperty_fun(QScriptContext *, QScriptEngine *eng) +void tst_QScriptEngine::argumentsProperty_globalContext() { - eng->evaluate("var a = arguments[0];"); - eng->evaluate("arguments[0] = 200;"); - return eng->evaluate("a + arguments[0]"); + QScriptEngine eng; + { + // Unlike function calls, the global context doesn't have an + // arguments property. + QScriptValue ret = eng.evaluate("arguments"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError"))); + } + eng.evaluate("arguments = 10"); + { + QScriptValue ret = eng.evaluate("arguments"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 10); + } + QVERIFY(eng.evaluate("delete arguments").toBoolean()); + QVERIFY(!eng.globalObject().property("arguments").isValid()); } - -void tst_QScriptEngine::argumentsProperty() +void tst_QScriptEngine::argumentsProperty_JS() { { QScriptEngine eng; - { - QScriptValue ret = eng.evaluate("arguments"); - QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: arguments")); - } - eng.evaluate("arguments = 10"); - { - QScriptValue ret = eng.evaluate("arguments"); - QVERIFY(ret.isNumber()); - QCOMPARE(ret.toInt32(), 10); - } - QVERIFY(eng.evaluate("delete arguments").toBoolean()); - QVERIFY(!eng.globalObject().property("arguments").isValid()); - } - { - QScriptEngine eng; eng.evaluate("o = { arguments: 123 }"); QScriptValue ret = eng.evaluate("with (o) { arguments; }"); QVERIFY(ret.isNumber()); @@ -3542,25 +3677,40 @@ void tst_QScriptEngine::argumentsProperty() { QScriptEngine eng; QVERIFY(!eng.globalObject().property("arguments").isValid()); + // This is testing ECMA-262 compliance. In function calls, "arguments" + // appears like a local variable, and it can be replaced. QScriptValue ret = eng.evaluate("(function() { arguments = 456; return arguments; })()"); QVERIFY(ret.isNumber()); QCOMPARE(ret.toInt32(), 456); QVERIFY(!eng.globalObject().property("arguments").isValid()); } +} - { - QScriptEngine eng; - QScriptValue fun = eng.newFunction(argumentsProperty_fun); - eng.globalObject().setProperty("fun", eng.newFunction(argumentsProperty_fun)); - QScriptValue result = eng.evaluate("fun(18)"); - QVERIFY(result.isNumber()); - QCOMPARE(result.toInt32(), 218); - } +static QScriptValue argumentsProperty_fun(QScriptContext *, QScriptEngine *eng) +{ + // Since evaluate() is done in the current context, "arguments" should + // refer to currentContext()->argumentsObject(). + // This is for consistency with the built-in JS eval() function. + eng->evaluate("var a = arguments[0];"); + eng->evaluate("arguments[0] = 200;"); + return eng->evaluate("a + arguments[0]"); } -void tst_QScriptEngine::numberClass() +void tst_QScriptEngine::argumentsProperty_evaluateInNativeFunction() { QScriptEngine eng; + QScriptValue fun = eng.newFunction(argumentsProperty_fun); + eng.globalObject().setProperty("fun", eng.newFunction(argumentsProperty_fun)); + QScriptValue result = eng.evaluate("fun(18)"); + QVERIFY(result.isNumber()); + QCOMPARE(result.toInt32(), 200+18); +} + +void tst_QScriptEngine::jsNumberClass() +{ + // See ECMA-262 Section 15.7, "Number Objects". + + QScriptEngine eng; QScriptValue ctor = eng.globalObject().property("Number"); QVERIFY(ctor.property("length").isNumber()); @@ -3669,7 +3819,7 @@ void tst_QScriptEngine::numberClass() } } -void tst_QScriptEngine::forInStatement() +void tst_QScriptEngine::jsForInStatement_simple() { QScriptEngine eng; { @@ -3692,8 +3842,11 @@ void tst_QScriptEngine::forInStatement() QCOMPARE(lst.at(0), QString::fromLatin1("p")); QCOMPARE(lst.at(1), QString::fromLatin1("q")); } +} - // properties in prototype +void tst_QScriptEngine::jsForInStatement_prototypeProperties() +{ + QScriptEngine eng; { QScriptValue ret = eng.evaluate("o = { }; o.__proto__ = { p: 123 }; r = [];" "for (var p in o) r[r.length] = p; r"); @@ -3718,6 +3871,11 @@ void tst_QScriptEngine::forInStatement() QCOMPARE(lst.at(0), QString::fromLatin1("p")); } +} + +void tst_QScriptEngine::jsForInStatement_mutateWhileIterating() +{ + QScriptEngine eng; // deleting property during enumeration { QScriptValue ret = eng.evaluate("o = { p: 123 }; r = [];" @@ -3743,7 +3901,11 @@ void tst_QScriptEngine::forInStatement() QCOMPARE(lst.at(0), QString::fromLatin1("p")); } - // arrays +} + +void tst_QScriptEngine::jsForInStatement_arrays() +{ + QScriptEngine eng; { QScriptValue ret = eng.evaluate("a = [123, 456]; r = [];" "for (var p in a) r[r.length] = p; r"); @@ -3774,10 +3936,11 @@ void tst_QScriptEngine::forInStatement() QCOMPARE(lst.at(3), QString::fromLatin1("2")); QCOMPARE(lst.at(4), QString::fromLatin1("bar")); } +} - // null and undefined - // according to the spec, we should throw an exception; however, for - // compability with the real world, we don't +void tst_QScriptEngine::jsForInStatement_nullAndUndefined() +{ + QScriptEngine eng; { QScriptValue ret = eng.evaluate("r = true; for (var p in undefined) r = false; r"); QVERIFY(ret.isBool()); @@ -3790,9 +3953,17 @@ void tst_QScriptEngine::forInStatement() } } -void tst_QScriptEngine::functionExpression() +void tst_QScriptEngine::jsFunctionDeclarationAsStatement() { - // task 175679 + // ECMA-262 does not allow function declarations to be used as statements, + // but several popular implementations (including JSC) do. See the NOTE + // at the beginning of chapter 12 in ECMA-262 5th edition, where it's + // recommended that implementations either disallow this usage or issue + // a warning. + // Since we had a bug report long ago about QtScript not supporting this + // "feature" (and thus deviating from other implementations), we still + // check this behavior. + QScriptEngine eng; QVERIFY(!eng.globalObject().property("bar").isValid()); eng.evaluate("function foo(arg) {\n" @@ -3825,6 +3996,8 @@ void tst_QScriptEngine::functionExpression() void tst_QScriptEngine::stringObjects() { + // See ECMA-262 Section 15.5, "String Objects". + QScriptEngine eng; QString str("ciao"); // in C++ @@ -3886,7 +4059,11 @@ void tst_QScriptEngine::stringObjects() QVERIFY(ret7.isBoolean()); QVERIFY(ret7.toBoolean()); } +} +void tst_QScriptEngine::jsStringPrototypeReplaceBugs() +{ + QScriptEngine eng; // task 212440 { QScriptValue ret = eng.evaluate("replace_args = []; \"a a a\".replace(/(a)/g, function() { replace_args.push(arguments); }); replace_args"); @@ -4038,8 +4215,10 @@ void tst_QScriptEngine::getterSetterThisObject_activation() } } -void tst_QScriptEngine::continueInSwitch() +void tst_QScriptEngine::jsContinueInSwitch() { + // This is testing ECMA-262 compliance, not C++ API. + QScriptEngine eng; // switch - continue { @@ -4116,35 +4295,17 @@ void tst_QScriptEngine::continueInSwitch() } } -void tst_QScriptEngine::readOnlyPrototypeProperty() +void tst_QScriptEngine::jsShadowReadOnlyPrototypeProperty() { - QSKIP("JSC semantics differ from old back-end and SpiderMonkey", SkipAll); + // SpiderMonkey has different behavior than JSC and V8; it disallows + // creating a property on the instance if there's a property with the + // same name in the prototype, and that property is read-only. We + // adopted that behavior in the old (4.5) QtScript back-end, but it + // just seems weird -- and non-compliant. Adopt the JSC behavior instead. QScriptEngine eng; - QCOMPARE(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length = 4; o.length").toInt32(), 2); - QVERIFY(!eng.evaluate("o.hasOwnProperty('length')").toBoolean()); - QCOMPARE(eng.evaluate("o.length *= 2; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length /= 2; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length %= 2; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length += 2; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length -= 2; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length <<= 2; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length >>= 2; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length >>>= 2; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length &= 0; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length ^= 255; o.length").toInt32(), 2); - QCOMPARE(eng.evaluate("o.length |= 255; o.length").toInt32(), 2); - - { - QScriptValue ret = eng.evaluate("o.__defineGetter__('length', function() {})"); - QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("Error: cannot redefine read-only property")); - } - { - QScriptValue ret = eng.evaluate("o.__defineSetter__('length', function() {})"); - QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("Error: cannot redefine read-only property")); - } + QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber()); + QCOMPARE(eng.evaluate("o.length = 123; o.length").toInt32(), 123); + QVERIFY(eng.evaluate("o.hasOwnProperty('length')").toBoolean()); } void tst_QScriptEngine::toObject() @@ -4226,7 +4387,7 @@ void tst_QScriptEngine::toObject() QVERIFY(stringValue.isString()); } -void tst_QScriptEngine::reservedWords_data() +void tst_QScriptEngine::jsReservedWords_data() { QTest::addColumn("word"); QTest::newRow("break") << QString("break"); @@ -4259,8 +4420,13 @@ void tst_QScriptEngine::reservedWords_data() QTest::newRow("with") << QString("with"); } -void tst_QScriptEngine::reservedWords() +void tst_QScriptEngine::jsReservedWords() { + // See ECMA-262 Section 7.6.1, "Reserved Words". + // We prefer that the implementation is less strict than the spec; e.g. + // it's good to allow reserved words as identifiers in object literals, + // and when accessing properties using dot notation. + QFETCH(QString, word); { QScriptEngine eng; @@ -4298,7 +4464,7 @@ void tst_QScriptEngine::reservedWords() } } -void tst_QScriptEngine::futureReservedWords_data() +void tst_QScriptEngine::jsFutureReservedWords_data() { QTest::addColumn("word"); QTest::addColumn("allowed"); @@ -4335,8 +4501,12 @@ void tst_QScriptEngine::futureReservedWords_data() QTest::newRow("volatile") << QString("volatile") << true; } -void tst_QScriptEngine::futureReservedWords() +void tst_QScriptEngine::jsFutureReservedWords() { + // See ECMA-262 Section 7.6.1.2, "Future Reserved Words". + // In real-world implementations, most of these words are + // actually allowed as normal identifiers. + QFETCH(QString, word); QFETCH(bool, allowed); { @@ -4364,8 +4534,10 @@ void tst_QScriptEngine::futureReservedWords() } } -void tst_QScriptEngine::throwInsideWithStatement() +void tst_QScriptEngine::jsThrowInsideWithStatement() { + // This is testing ECMA-262 compliance, not C++ API. + // task 209988 QScriptEngine eng; { @@ -4379,7 +4551,7 @@ void tst_QScriptEngine::throwInsideWithStatement() " bad;" "}"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bad")); + QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError"))); } { QScriptValue ret = eng.evaluate( @@ -4392,7 +4564,7 @@ void tst_QScriptEngine::throwInsideWithStatement() " bad;" "}"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bad")); + QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError"))); } { eng.clearExceptions(); @@ -4419,7 +4591,7 @@ void tst_QScriptEngine::throwInsideWithStatement() QVERIFY(ret.isNumber()); QScriptValue ret2 = eng.evaluate("bug"); QVERIFY(ret2.isError()); - QCOMPARE(ret2.toString(), QString::fromLatin1("ReferenceError: Can't find variable: bug")); + QVERIFY(ret2.toString().contains(QString::fromLatin1("ReferenceError"))); } } @@ -4429,106 +4601,116 @@ public: TestAgent(QScriptEngine *engine) : QScriptEngineAgent(engine) {} }; -void tst_QScriptEngine::getSetAgent() +void tst_QScriptEngine::getSetAgent_ownership() { - // case 1: engine deleted before agent --> agent deleted too - { - QScriptEngine *eng = new QScriptEngine; - QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); - TestAgent *agent = new TestAgent(eng); - eng->setAgent(agent); - QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent); - eng->setAgent(0); // the engine maintains ownership of the old agent - QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); - delete eng; - } - // case 2: agent deleted before engine --> engine's agent should become 0 - { - QScriptEngine *eng = new QScriptEngine; - TestAgent *agent = new TestAgent(eng); - eng->setAgent(agent); - QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent); - delete agent; - QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); - eng->evaluate("(function(){ return 123; })()"); - delete eng; - } - { - QScriptEngine eng; - QScriptEngine eng2; - TestAgent *agent = new TestAgent(&eng); - QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::setAgent(): cannot set agent belonging to different engine"); - eng2.setAgent(agent); - QCOMPARE(eng2.agent(), (QScriptEngineAgent*)0); - } + // engine deleted before agent --> agent deleted too + QScriptEngine *eng = new QScriptEngine; + QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); + TestAgent *agent = new TestAgent(eng); + eng->setAgent(agent); + QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent); + eng->setAgent(0); // the engine maintains ownership of the old agent + QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); + delete eng; } -void tst_QScriptEngine::reentrancy() +void tst_QScriptEngine::getSetAgent_deleteAgent() { + // agent deleted before engine --> engine's agent should become 0 + QScriptEngine *eng = new QScriptEngine; + TestAgent *agent = new TestAgent(eng); + eng->setAgent(agent); + QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent); + delete agent; + QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); + eng->evaluate("(function(){ return 123; })()"); + delete eng; +} + +void tst_QScriptEngine::getSetAgent_differentEngine() +{ + QScriptEngine eng; + QScriptEngine eng2; + TestAgent *agent = new TestAgent(&eng); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::setAgent(): cannot set agent belonging to different engine"); + eng2.setAgent(agent); + QCOMPARE(eng2.agent(), (QScriptEngineAgent*)0); +} + +void tst_QScriptEngine::reentrancy_stringHandles() +{ + QScriptEngine eng1; + QScriptEngine eng2; + QScriptString s1 = eng1.toStringHandle("foo"); + QScriptString s2 = eng2.toStringHandle("foo"); + QVERIFY(s1 != s2); +} + +void tst_QScriptEngine::reentrancy_processEventsInterval() +{ + QScriptEngine eng1; + QScriptEngine eng2; + eng1.setProcessEventsInterval(123); + QCOMPARE(eng2.processEventsInterval(), -1); + eng2.setProcessEventsInterval(456); + QCOMPARE(eng1.processEventsInterval(), 123); +} + +void tst_QScriptEngine::reentrancy_typeConversion() +{ + QScriptEngine eng1; + QScriptEngine eng2; + qScriptRegisterMetaType(&eng1, fooToScriptValue, fooFromScriptValue); + Foo foo; + foo.x = 12; + foo.y = 34; { - QScriptEngine eng1; - QScriptEngine eng2; - QScriptString s1 = eng1.toStringHandle("foo"); - QScriptString s2 = eng2.toStringHandle("foo"); - QVERIFY(s1 != s2); - } - { - QScriptEngine eng1; - QScriptEngine eng2; - eng1.setProcessEventsInterval(123); - QCOMPARE(eng2.processEventsInterval(), -1); - eng2.setProcessEventsInterval(456); - QCOMPARE(eng1.processEventsInterval(), 123); + QScriptValue fooVal = qScriptValueFromValue(&eng1, foo); + QVERIFY(fooVal.isObject()); + QVERIFY(!fooVal.isVariant()); + QCOMPARE(fooVal.property("x").toInt32(), 12); + QCOMPARE(fooVal.property("y").toInt32(), 34); + fooVal.setProperty("x", 56); + fooVal.setProperty("y", 78); + + Foo foo2 = qScriptValueToValue(fooVal); + QCOMPARE(foo2.x, 56); + QCOMPARE(foo2.y, 78); } { - QScriptEngine eng1; - QScriptEngine eng2; - qScriptRegisterMetaType(&eng1, fooToScriptValue, fooFromScriptValue); - Foo foo; - foo.x = 12; - foo.y = 34; - { - QScriptValue fooVal = qScriptValueFromValue(&eng1, foo); - QVERIFY(fooVal.isObject()); - QVERIFY(!fooVal.isVariant()); - QCOMPARE(fooVal.property("x").toInt32(), 12); - QCOMPARE(fooVal.property("y").toInt32(), 34); - fooVal.setProperty("x", 56); - fooVal.setProperty("y", 78); - - Foo foo2 = qScriptValueToValue(fooVal); - QCOMPARE(foo2.x, 56); - QCOMPARE(foo2.y, 78); - } - { - QScriptValue fooVal = qScriptValueFromValue(&eng2, foo); - QVERIFY(fooVal.isVariant()); + QScriptValue fooVal = qScriptValueFromValue(&eng2, foo); + QVERIFY(fooVal.isVariant()); - Foo foo2 = qScriptValueToValue(fooVal); - QCOMPARE(foo2.x, 12); - QCOMPARE(foo2.y, 34); - } - QVERIFY(!eng1.defaultPrototype(qMetaTypeId()).isValid()); - QVERIFY(!eng2.defaultPrototype(qMetaTypeId()).isValid()); - QScriptValue proto1 = eng1.newObject(); - eng1.setDefaultPrototype(qMetaTypeId(), proto1); - QVERIFY(!eng2.defaultPrototype(qMetaTypeId()).isValid()); - QScriptValue proto2 = eng2.newObject(); - eng2.setDefaultPrototype(qMetaTypeId(), proto2); - QVERIFY(eng2.defaultPrototype(qMetaTypeId()).isValid()); - QVERIFY(eng1.defaultPrototype(qMetaTypeId()).strictlyEquals(proto1)); - } - { - QScriptEngine eng1; - QScriptEngine eng2; - QVERIFY(!eng2.globalObject().property("a").isValid()); - eng1.evaluate("a = 10"); - QVERIFY(eng1.globalObject().property("a").isNumber()); - QVERIFY(!eng2.globalObject().property("a").isValid()); - eng2.evaluate("a = 20"); - QVERIFY(eng2.globalObject().property("a").isNumber()); - QCOMPARE(eng1.globalObject().property("a").toInt32(), 10); + Foo foo2 = qScriptValueToValue(fooVal); + QCOMPARE(foo2.x, 12); + QCOMPARE(foo2.y, 34); } + QVERIFY(!eng1.defaultPrototype(qMetaTypeId()).isValid()); + QVERIFY(!eng2.defaultPrototype(qMetaTypeId()).isValid()); + QScriptValue proto1 = eng1.newObject(); + eng1.setDefaultPrototype(qMetaTypeId(), proto1); + QVERIFY(!eng2.defaultPrototype(qMetaTypeId()).isValid()); + QScriptValue proto2 = eng2.newObject(); + eng2.setDefaultPrototype(qMetaTypeId(), proto2); + QVERIFY(eng2.defaultPrototype(qMetaTypeId()).isValid()); + QVERIFY(eng1.defaultPrototype(qMetaTypeId()).strictlyEquals(proto1)); +} + +void tst_QScriptEngine::reentrancy_globalObjectProperties() +{ + QScriptEngine eng1; + QScriptEngine eng2; + QVERIFY(!eng2.globalObject().property("a").isValid()); + eng1.evaluate("a = 10"); + QVERIFY(eng1.globalObject().property("a").isNumber()); + QVERIFY(!eng2.globalObject().property("a").isValid()); + eng2.evaluate("a = 20"); + QVERIFY(eng2.globalObject().property("a").isNumber()); + QCOMPARE(eng1.globalObject().property("a").toInt32(), 10); +} + +void tst_QScriptEngine::reentrancy_Array() +{ // weird bug with JSC backend { QScriptEngine eng; @@ -4540,79 +4722,82 @@ void tst_QScriptEngine::reentrancy() QScriptEngine eng; QCOMPARE(eng.evaluate("Array()").toString(), QString()); } +} +void tst_QScriptEngine::reentrancy_objectCreation() +{ + QScriptEngine eng1; + QScriptEngine eng2; { - QScriptEngine eng1; - QScriptEngine eng2; - { - QScriptValue d1 = eng1.newDate(0); - QScriptValue d2 = eng2.newDate(0); - QCOMPARE(d1.toDateTime(), d2.toDateTime()); - QCOMPARE(d2.toDateTime(), d1.toDateTime()); - } - { - QScriptValue r1 = eng1.newRegExp("foo", "gim"); - QScriptValue r2 = eng2.newRegExp("foo", "gim"); - QCOMPARE(r1.toRegExp(), r2.toRegExp()); - QCOMPARE(r2.toRegExp(), r1.toRegExp()); - } - { - QScriptValue o1 = eng1.newQObject(this); - QScriptValue o2 = eng2.newQObject(this); - QCOMPARE(o1.toQObject(), o2.toQObject()); - QCOMPARE(o2.toQObject(), o1.toQObject()); - } - { - QScriptValue mo1 = eng1.newQMetaObject(&staticMetaObject); - QScriptValue mo2 = eng2.newQMetaObject(&staticMetaObject); - QCOMPARE(mo1.toQMetaObject(), mo2.toQMetaObject()); - QCOMPARE(mo2.toQMetaObject(), mo1.toQMetaObject()); - } + QScriptValue d1 = eng1.newDate(0); + QScriptValue d2 = eng2.newDate(0); + QCOMPARE(d1.toDateTime(), d2.toDateTime()); + QCOMPARE(d2.toDateTime(), d1.toDateTime()); + } + { + QScriptValue r1 = eng1.newRegExp("foo", "gim"); + QScriptValue r2 = eng2.newRegExp("foo", "gim"); + QCOMPARE(r1.toRegExp(), r2.toRegExp()); + QCOMPARE(r2.toRegExp(), r1.toRegExp()); + } + { + QScriptValue o1 = eng1.newQObject(this); + QScriptValue o2 = eng2.newQObject(this); + QCOMPARE(o1.toQObject(), o2.toQObject()); + QCOMPARE(o2.toQObject(), o1.toQObject()); + } + { + QScriptValue mo1 = eng1.newQMetaObject(&staticMetaObject); + QScriptValue mo2 = eng2.newQMetaObject(&staticMetaObject); + QCOMPARE(mo1.toQMetaObject(), mo2.toQMetaObject()); + QCOMPARE(mo2.toQMetaObject(), mo1.toQMetaObject()); } } -void tst_QScriptEngine:: incDecNonObjectProperty() +void tst_QScriptEngine::jsIncDecNonObjectProperty() { + // This is testing ECMA-262 compliance, not C++ API. + QScriptEngine eng; { QScriptValue ret = eng.evaluate("var a; a.n++"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [undefined] is not an object.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { QScriptValue ret = eng.evaluate("var a; a.n--"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [undefined] is not an object.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { QScriptValue ret = eng.evaluate("var a = null; a.n++"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { QScriptValue ret = eng.evaluate("var a = null; a.n--"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { QScriptValue ret = eng.evaluate("var a; ++a.n"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { QScriptValue ret = eng.evaluate("var a; --a.n"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { QScriptValue ret = eng.evaluate("var a; a.n += 1"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { QScriptValue ret = eng.evaluate("var a; a.n -= 1"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'a' [null] is not an object.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { QScriptValue ret = eng.evaluate("var a = 'ciao'; a.length++"); @@ -5436,6 +5621,7 @@ void tst_QScriptEngine::collectGarbageAfterConnect() .isUndefined()); QVERIFY(widget != 0); engine.evaluate("widget = null;"); + // The connection should not keep the widget alive. collectGarbage_helper(engine); QVERIFY(widget == 0); } @@ -5599,6 +5785,17 @@ void tst_QScriptEngine::reentrency() void tst_QScriptEngine::newFixedStaticScopeObject() { + // "Static scope objects" is an optimization we do for QML. + // It enables the creation of JS objects that can guarantee to the + // compiler that no properties will be added or removed. This enables + // the compiler to generate a very simple (fast) property access, as + // opposed to a full virtual lookup. Due to the inherent use of scope + // chains in QML, this can make a huge difference (10x improvement for + // benchmark in QTBUG-8576). + // Ideally we would not need a special object type for this, and the + // VM would dynamically optimize it to be fast... + // See also QScriptEngine benchmark. + QScriptEngine eng; static const int propertyCount = 4; QString names[] = { "foo", "bar", "baz", "Math" }; @@ -5739,6 +5936,11 @@ void tst_QScriptEngine::newFixedStaticScopeObject() void tst_QScriptEngine::newGrowingStaticScopeObject() { + // The main use case for a growing static scope object is to set it as + // the activation object of a QScriptContext, so that all JS variable + // declarations end up in that object. It needs to be "growable" since + // we don't know in advance how many variables a script will declare. + QScriptEngine eng; QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng); @@ -5826,10 +6028,10 @@ void tst_QScriptEngine::newGrowingStaticScopeObject() { QScriptValue fun = eng.evaluate("(function() { return futureProperty; })"); QVERIFY(fun.isFunction()); - QCOMPARE(fun.call().toString(), QString::fromLatin1("ReferenceError: Can't find variable: futureProperty")); + QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError"))); scope.setProperty("futureProperty", "added after the function was compiled"); // If scope were dynamic, this would return the new property. - QCOMPARE(fun.call().toString(), QString::fromLatin1("ReferenceError: Can't find variable: futureProperty")); + QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError"))); } eng.popContext(); -- cgit v0.12 From da48bbe66b7821416bee1ebc0215bc93e68f0a2a Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 2 Feb 2011 15:45:08 +0100 Subject: Split QScriptQObject test into smaller functions It could be split further (and with more descriptive names), but this is a start, at least. Task-number: QTBUG-16746 Reviewed-by: Jedrzej Nowacki --- .../qscriptextqobject/tst_qscriptextqobject.cpp | 46 ++++++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/tests/auto/qscriptextqobject/tst_qscriptextqobject.cpp b/tests/auto/qscriptextqobject/tst_qscriptextqobject.cpp index cb29586..1607bed 100644 --- a/tests/auto/qscriptextqobject/tst_qscriptextqobject.cpp +++ b/tests/auto/qscriptextqobject/tst_qscriptextqobject.cpp @@ -549,10 +549,19 @@ private slots: void getSetDynamicProperty_doNotHideJSProperty(); void getSetChildren(); void callQtInvokable(); + void callQtInvokable2(); + void callQtInvokable3(); + void callQtInvokable4(); + void callQtInvokable5(); + void callQtInvokable6(); + void callQtInvokable7(); void connectAndDisconnect(); + void connectAndDisconnect_emitFromJS(); + void connectAndDisconnect_senderWrapperCollected(); void connectAndDisconnectWithBadArgs(); void connectAndDisconnect_senderDeleted(); void cppConnectAndDisconnect(); + void cppConnectAndDisconnect2(); void classEnums(); void classConstructor(); void overrideInvokable(); @@ -1270,7 +1279,10 @@ void tst_QScriptExtQObject::callQtInvokable() QCOMPARE(m_myObject->qtFunctionActuals().size(), 2); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 123); QCOMPARE(m_myObject->qtFunctionActuals().at(1).toInt(), 456); +} +void tst_QScriptExtQObject::callQtInvokable2() +{ m_myObject->resetQtFunctionInvoked(); QVERIFY(m_engine->evaluate("myObject.myInvokableWithVoidStarArg(null)").isUndefined()); QCOMPARE(m_myObject->qtFunctionInvoked(), 44); @@ -1359,7 +1371,10 @@ void tst_QScriptExtQObject::callQtInvokable() QCOMPARE(ret.isArray(), true); QCOMPARE(m_myObject->qtFunctionInvoked(), 11); } +} +void tst_QScriptExtQObject::callQtInvokable3() +{ { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithVectorOfIntArg(myObject.myInvokableReturningVectorOfInt())"); QCOMPARE(ret.isUndefined(), true); @@ -1485,7 +1500,10 @@ void tst_QScriptExtQObject::callQtInvokable() QCOMPARE(ret.property("0").strictlyEquals(QScriptValue(m_engine, 1)), true); QCOMPARE(ret.property("1").strictlyEquals(QScriptValue(m_engine, 5)), true); } +} +void tst_QScriptExtQObject::callQtInvokable4() +{ m_myObject->resetQtFunctionInvoked(); { QScriptValue ret = m_engine->evaluate("myObject.myInvokableWithQObjectStarArg(myObject)"); @@ -1571,7 +1589,10 @@ void tst_QScriptExtQObject::callQtInvokable() QCOMPARE(v.userType(), int(QMetaType::ULongLong)); QCOMPARE(qvariant_cast(v), qulonglong(123)); } +} +void tst_QScriptExtQObject::callQtInvokable5() +{ m_myObject->resetQtFunctionInvoked(); { QScriptValue fun = m_engine->evaluate("myObject.myInvokableWithQBrushArg"); @@ -1659,7 +1680,10 @@ void tst_QScriptExtQObject::callQtInvokable() QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(qvariant_cast(m_myObject->qtFunctionActuals().at(0)), (QObject*)m_myObject); } +} +void tst_QScriptExtQObject::callQtInvokable6() +{ // QScriptValue arguments should be passed on without conversion m_myObject->resetQtFunctionInvoked(); { @@ -1723,18 +1747,21 @@ void tst_QScriptExtQObject::callQtInvokable() QCOMPARE(m_myObject->qtFunctionInvoked(), 55); } } +} +void tst_QScriptExtQObject::callQtInvokable7() +{ // qscript_call() { m_myObject->resetQtFunctionInvoked(); QScriptValue ret = m_engine->evaluate("new myObject(123)"); QVERIFY(ret.isError()); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'myObject' [MyQObject(name = \"\")] is not a constructor.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } { m_myObject->resetQtFunctionInvoked(); QScriptValue ret = m_engine->evaluate("myObject(123)"); - QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Result of expression 'myObject' [MyQObject(name = \"\")] is not a function.")); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); } // task 233624 @@ -1971,9 +1998,10 @@ void tst_QScriptExtQObject::connectAndDisconnect() QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myObject, 'mySlot')").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(yetAnotherObject, 'func')").isUndefined()); QVERIFY(m_engine->evaluate("myObject.mySignal.disconnect(myObject, 'mySlot')").isUndefined()); +} - // check that emitting signals from script works - +void tst_QScriptExtQObject::connectAndDisconnect_emitFromJS() +{ // no arguments QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myObject.mySlot)").isUndefined()); m_myObject->resetQtFunctionInvoked(); @@ -2022,7 +2050,10 @@ void tst_QScriptExtQObject::connectAndDisconnect() QCOMPARE(m_myObject->qtFunctionActuals().size(), 1); QCOMPARE(m_myObject->qtFunctionActuals().at(0).toInt(), 456); QVERIFY(m_engine->evaluate("myObject.mySignalWithIntArg.disconnect(myObject['myOverloadedSlot(int)'])").isUndefined()); +} +void tst_QScriptExtQObject::connectAndDisconnect_senderWrapperCollected() +{ // when the wrapper dies, the connection stays alive QVERIFY(m_engine->evaluate("myObject.mySignal.connect(myObject.mySlot)").isUndefined()); m_myObject->resetQtFunctionInvoked(); @@ -2210,7 +2241,14 @@ void tst_QScriptExtQObject::cppConnectAndDisconnect() QVERIFY(!qScriptDisconnect(&edit2, SIGNAL(textChanged(const QString &)), receiver, fun)); } } +} +void tst_QScriptExtQObject::cppConnectAndDisconnect2() +{ + QScriptEngine eng; + QLineEdit edit; + QLineEdit edit2; + QScriptValue fun = eng.evaluate("function fun(text) { signalObject = this; signalArg = text; }; fun"); // make sure we don't crash when engine is deleted { QScriptEngine *eng2 = new QScriptEngine; -- cgit v0.12 From e97d3f3f571e3cc5f5851ea2e131ac92212802dd Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 3 Feb 2011 19:30:09 +0100 Subject: QtDBus: add support for QVariantHash out of the box --- src/dbus/qdbusargument.h | 15 +++++++++++++++ src/dbus/qdbusmetatype.cpp | 1 + 2 files changed, 16 insertions(+) diff --git a/src/dbus/qdbusargument.h b/src/dbus/qdbusargument.h index 2c8693b..e331d8f 100644 --- a/src/dbus/qdbusargument.h +++ b/src/dbus/qdbusargument.h @@ -376,6 +376,21 @@ inline const QDBusArgument &operator>>(const QDBusArgument &arg, QHash & return arg; } +inline QDBusArgument &operator<<(QDBusArgument &arg, const QVariantHash &map) +{ + arg.beginMap(QVariant::String, qMetaTypeId()); + QVariantHash::ConstIterator it = map.constBegin(); + QVariantHash::ConstIterator end = map.constEnd(); + for ( ; it != end; ++it) { + arg.beginMapEntry(); + arg << it.key() << QDBusVariant(it.value()); + arg.endMapEntry(); + } + arg.endMap(); + return arg; +} + + QT_END_NAMESPACE Q_DECLARE_METATYPE(QDBusArgument) diff --git a/src/dbus/qdbusmetatype.cpp b/src/dbus/qdbusmetatype.cpp index 6de5d93..9f29205 100644 --- a/src/dbus/qdbusmetatype.cpp +++ b/src/dbus/qdbusmetatype.cpp @@ -127,6 +127,7 @@ void QDBusMetaTypeId::init() registerHelper(); registerHelper(); registerHelper(); + registerHelper(); qDBusRegisterMetaType >(); qDBusRegisterMetaType >(); -- cgit v0.12 From d9c06bf25210b3d0b31ee6126e57bcb82c292da1 Mon Sep 17 00:00:00 2001 From: Harald Fernengel Date: Fri, 4 Feb 2011 10:46:01 +0100 Subject: Revert "Delay creation of the process manager" This reverts commit daba2c507ad42c66dafa6a29cffa94e9641e0c58. There's a potential deadlock when a QProcess is created while a QCoreApplication is instantiated but never executed, or if the main thread waits() for the child thread. --- src/corelib/io/qprocess_unix.cpp | 30 ++++-------------------------- src/corelib/kernel/qcoreapplication.cpp | 18 ++++++------------ src/corelib/kernel/qcoreapplication.h | 1 - src/corelib/kernel/qcoreapplication_p.h | 2 -- 4 files changed, 10 insertions(+), 41 deletions(-) diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index b120f7f..f9f5362 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -169,27 +169,17 @@ private: Q_GLOBAL_STATIC(QMutex, processManagerGlobalMutex) -static QProcessManager *processManagerInstance = 0; - -static QProcessManager *processManager() -{ +static QProcessManager *processManager() { // The constructor of QProcessManager should be called only once // so we cannot use Q_GLOBAL_STATIC directly for QProcessManager QMutex *mutex = processManagerGlobalMutex(); QMutexLocker locker(mutex); - - if (!processManagerInstance) - QProcessPrivate::initializeProcessManager(); - - Q_ASSERT(processManagerInstance); - return processManagerInstance; + static QProcessManager processManager; + return &processManager; } QProcessManager::QProcessManager() { - // can only be called from main thread - Q_ASSERT(!qApp || qApp->thread() == QThread::currentThread()); - #if defined (QPROCESS_DEBUG) qDebug() << "QProcessManager::QProcessManager()"; #endif @@ -208,8 +198,6 @@ QProcessManager::QProcessManager() ::sigaction(SIGCHLD, &action, &oldAction); if (oldAction.sa_handler != qt_sa_sigchld_handler) qt_sa_old_sigchld_handler = oldAction.sa_handler; - - processManagerInstance = this; } QProcessManager::~QProcessManager() @@ -238,8 +226,6 @@ QProcessManager::~QProcessManager() if (oldAction.sa_handler != qt_sa_sigchld_handler) { ::sigaction(SIGCHLD, &oldAction, 0); } - - processManagerInstance = 0; } void QProcessManager::run() @@ -1306,15 +1292,7 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a void QProcessPrivate::initializeProcessManager() { - if (qApp && qApp->thread() != QThread::currentThread()) { - // The process manager must be initialized in the main thread - // Note: The call below will re-enter this function, but in the right thread, - // so the else statement below will be executed. - QMetaObject::invokeMethod(qApp, "_q_initializeProcessManager", Qt::BlockingQueuedConnection); - } else { - static QProcessManager processManager; - Q_UNUSED(processManager); - } + (void) processManager(); } QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 9bd32d1..26a398e 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -392,16 +392,6 @@ void QCoreApplicationPrivate::createEventDispatcher() #endif } -void QCoreApplicationPrivate::_q_initializeProcessManager() -{ -#ifndef QT_NO_PROCESS -# ifdef Q_OS_UNIX - QProcessPrivate::initializeProcessManager(); -# endif -#endif -} - - QThread *QCoreApplicationPrivate::theMainThread = 0; QThread *QCoreApplicationPrivate::mainThread() { @@ -666,6 +656,12 @@ void QCoreApplication::init() } #endif +#if defined(Q_OS_UNIX) && !(defined(QT_NO_PROCESS)) + // Make sure the process manager thread object is created in the main + // thread. + QProcessPrivate::initializeProcessManager(); +#endif + #ifdef QT_EVAL extern void qt_core_eval_init(uint); qt_core_eval_init(d->application_type); @@ -2732,5 +2728,3 @@ int QCoreApplication::loopLevel() */ QT_END_NAMESPACE - -#include "moc_qcoreapplication.cpp" diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index 024c509..3957158 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -205,7 +205,6 @@ protected: QCoreApplication(QCoreApplicationPrivate &p); private: - Q_PRIVATE_SLOT(d_func(), void _q_initializeProcessManager()) static bool sendSpontaneousEvent(QObject *receiver, QEvent *event); bool notifyInternal(QObject *receiver, QEvent *event); diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index fdceab4..add2a35 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -85,8 +85,6 @@ public: bool sendThroughObjectEventFilters(QObject *, QEvent *); bool notify_helper(QObject *, QEvent *); - void _q_initializeProcessManager(); - virtual QString appName() const; virtual void createEventDispatcher(); static void removePostedEvent(QEvent *); -- cgit v0.12 From 6163c36249eea0a2a578b9990470fcaa2f7bcf5f Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Thu, 3 Feb 2011 15:48:02 +0100 Subject: Add Context2D QtScript benchmark Reuse the Context2D implementation from examples/script/context2d and create a benchmark out of it. This benchmark actually measures a meaningful, real-world use case. Task-number: QTBUG-17192 Reviewed-by: Jedrzej Nowacki --- examples/script/context2d/environment.cpp | 17 ++ examples/script/context2d/environment.h | 2 + tests/benchmarks/script/context2d/context2d.pro | 22 +++ .../benchmarks/script/context2d/tst_context2d.cpp | 177 +++++++++++++++++++++ tests/benchmarks/script/script.pro | 3 +- 5 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 tests/benchmarks/script/context2d/context2d.pro create mode 100644 tests/benchmarks/script/context2d/tst_context2d.cpp diff --git a/examples/script/context2d/environment.cpp b/examples/script/context2d/environment.cpp index 7d6d6f8..e68c1ca 100644 --- a/examples/script/context2d/environment.cpp +++ b/examples/script/context2d/environment.cpp @@ -363,6 +363,23 @@ QScriptValue Environment::evaluate(const QString &code, const QString &fileName) return m_engine->evaluate(code, fileName); } +bool Environment::hasIntervalTimers() const +{ + return !m_intervalHash.isEmpty(); +} + +// This is used by the Context2D QtScript benchmark. +void Environment::triggerTimers() +{ + for (int x = 0; x < 2; ++x) { + QList timerIds = x ? m_intervalHash.keys() : m_timeoutHash.keys(); + for (int i = 0; i < timerIds.size(); ++i) { + QTimerEvent fakeEvent(timerIds.at(i)); + timerEvent(&fakeEvent); + } + } +} + //! [2] QScriptValue Environment::toWrapper(QObject *object) { diff --git a/examples/script/context2d/environment.h b/examples/script/context2d/environment.h index dd92e85..221875f 100644 --- a/examples/script/context2d/environment.h +++ b/examples/script/context2d/environment.h @@ -78,6 +78,8 @@ public: //! [0] QScriptEngine *engine() const; + bool hasIntervalTimers() const; + void triggerTimers(); //! [1] public slots: diff --git a/tests/benchmarks/script/context2d/context2d.pro b/tests/benchmarks/script/context2d/context2d.pro new file mode 100644 index 0000000..bc94c4f --- /dev/null +++ b/tests/benchmarks/script/context2d/context2d.pro @@ -0,0 +1,22 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_bench_context2d + +SOURCES += tst_context2d.cpp + +CONTEXT2D_EXAMPLE_DIR = $$QT_SOURCE_TREE/examples/script/context2d +INCLUDEPATH += $$CONTEXT2D_EXAMPLE_DIR + +HEADERS += $$CONTEXT2D_EXAMPLE_DIR/qcontext2dcanvas.h \ + $$CONTEXT2D_EXAMPLE_DIR/context2d.h \ + $$CONTEXT2D_EXAMPLE_DIR/domimage.h \ + $$CONTEXT2D_EXAMPLE_DIR/environment.h + +SOURCES += $$CONTEXT2D_EXAMPLE_DIR/qcontext2dcanvas.cpp \ + $$CONTEXT2D_EXAMPLE_DIR/context2d.cpp \ + $$CONTEXT2D_EXAMPLE_DIR/domimage.cpp \ + $$CONTEXT2D_EXAMPLE_DIR/environment.cpp + +RESOURCES += $$CONTEXT2D_EXAMPLE_DIR/context2d.qrc + +QT += script diff --git a/tests/benchmarks/script/context2d/tst_context2d.cpp b/tests/benchmarks/script/context2d/tst_context2d.cpp new file mode 100644 index 0000000..8401590 --- /dev/null +++ b/tests/benchmarks/script/context2d/tst_context2d.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "context2d.h" +#include "environment.h" +#include "qcontext2dcanvas.h" + +static QString readFile(const QString &filename) +{ + QFile file(filename); + if (!file.open(QFile::ReadOnly)) + return QString(); + QTextStream stream(&file); + stream.setCodec("UTF-8"); + return stream.readAll(); +} + +class tst_Context2D : public QObject +{ + Q_OBJECT + +public: + tst_Context2D(); + ~tst_Context2D(); + +private slots: + void singleExecution_data(); + void singleExecution(); + void repeatedExecution_data(); + void repeatedExecution(); + +private: + void newEnvironment(); + +private: + QDir testsDir; + Environment *m_env; + QContext2DCanvas *m_canvas; +}; + +tst_Context2D::tst_Context2D() + : m_env(0), m_canvas(0) +{ + testsDir = QDir(":/scripts"); + if (!testsDir.exists()) + qWarning("*** no scripts/ dir!"); +} + +tst_Context2D::~tst_Context2D() +{ + delete m_canvas; + delete m_env; +} + +void tst_Context2D::newEnvironment() +{ + delete m_canvas; + delete m_env; + m_env = new Environment(); + Context2D *context = new Context2D(m_env); + context->setSize(150, 150); // Hard-coded in many of the scripts. + m_canvas = new QContext2DCanvas(context, m_env); + m_canvas->setFixedSize(context->size()); + m_canvas->setObjectName("tutorial"); // Acts as the DOM element ID. + m_env->addCanvas(m_canvas); +} + +void tst_Context2D::singleExecution_data() +{ + QTest::addColumn("testName"); + QFileInfoList testFileInfos = testsDir.entryInfoList(QStringList() << "*.js", QDir::Files); + foreach (QFileInfo tfi, testFileInfos) { + QString name = tfi.baseName(); + QTest::newRow(name.toLatin1().constData()) << name; + } +} + +void tst_Context2D::singleExecution() +{ + QFETCH(QString, testName); + QString script = readFile(testsDir.absoluteFilePath(testName + ".js")); + QVERIFY(!script.isEmpty()); + + newEnvironment(); + QBENCHMARK { + m_env->evaluate(script, testName); + // Some of the scripts (e.g. plasma.js) merely start a timer and do + // the actual drawing in the timer event. Trigger the timers now to + // ensure that the real work is done. + m_env->triggerTimers(); + } + QVERIFY(!m_env->engine()->hasUncaughtException()); +} + +void tst_Context2D::repeatedExecution_data() +{ + // We look for scripts that register an interval timer. + // Such scripts run a function every n milliseconds to update the canvas. + // The benchmark will execute this function repeatedly, which can allow + // us to observe potential effects of profiling-based JIT optimizations. + QTest::addColumn("testName"); + QTest::addColumn("script"); + QFileInfoList testFileInfos = testsDir.entryInfoList(QStringList() << "*.js", QDir::Files); + foreach (QFileInfo tfi, testFileInfos) { + QString script = readFile(tfi.absoluteFilePath()); + QString name = tfi.baseName(); + newEnvironment(); + m_env->evaluate(script, name); + if (m_env->engine()->hasUncaughtException()) + continue; + if (m_env->hasIntervalTimers()) + QTest::newRow(name.toLatin1().constData()) << name << script; + } +} + +void tst_Context2D::repeatedExecution() +{ + QFETCH(QString, testName); + QFETCH(QString, script); + + newEnvironment(); + m_env->evaluate(script, testName); + QBENCHMARK { + // Trigger the update function repeatedly, effectively + // performing several frames of animation. + for (int i = 0; i < 16; ++i) + m_env->triggerTimers(); + } + QVERIFY(!m_env->engine()->hasUncaughtException()); +} + +QTEST_MAIN(tst_Context2D) +#include "tst_context2d.moc" diff --git a/tests/benchmarks/script/script.pro b/tests/benchmarks/script/script.pro index b0770ca..da7a5a1 100644 --- a/tests/benchmarks/script/script.pro +++ b/tests/benchmarks/script/script.pro @@ -1,5 +1,6 @@ TEMPLATE = subdirs SUBDIRS = \ + context2d \ qscriptclass \ qscriptengine \ qscriptvalue \ @@ -13,4 +14,4 @@ TRUSTED_BENCHMARKS += \ qscriptvalue \ qscriptengine -include(../trusted-benchmarks.pri) \ No newline at end of file +include(../trusted-benchmarks.pri) -- cgit v0.12 From b0c109ff2479dd8f4474b6c5ad537168224a5dc4 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 4 Feb 2011 09:39:10 +0100 Subject: Add QScriptClass/ByteArray benchmark Reuse the ByteArray implementation from examples/script/customclass and create a benchmark out of it. This benchmark actually measures a meaningful, real-world use case for QScriptClass. Task-number: QTBUG-17192 Reviewed-by: Jedrzej Nowacki --- .../qscriptclass_bytearray.pro | 10 ++ .../qscriptclass_bytearray.qrc | 5 + .../qscriptclass_bytearray/tests/construct-copy.js | 3 + .../qscriptclass_bytearray/tests/construct.js | 2 + .../script/qscriptclass_bytearray/tests/for-in.js | 3 + .../qscriptclass_bytearray/tests/get-element.js | 3 + .../qscriptclass_bytearray/tests/get-length.js | 3 + .../script/qscriptclass_bytearray/tests/mid.js | 3 + .../qscriptclass_bytearray/tests/set-element.js | 3 + .../qscriptclass_bytearray/tests/set-length.js | 3 + .../script/qscriptclass_bytearray/tests/sum.js | 8 ++ .../script/qscriptclass_bytearray/tests/trimmed.js | 3 + .../tst_qscriptclass_bytearray.cpp | 109 +++++++++++++++++++++ tests/benchmarks/script/script.pro | 1 + 14 files changed, 159 insertions(+) create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/qscriptclass_bytearray.pro create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/qscriptclass_bytearray.qrc create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/construct-copy.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/construct.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/for-in.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/get-element.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/get-length.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/mid.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/set-element.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/set-length.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/sum.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tests/trimmed.js create mode 100644 tests/benchmarks/script/qscriptclass_bytearray/tst_qscriptclass_bytearray.cpp diff --git a/tests/benchmarks/script/qscriptclass_bytearray/qscriptclass_bytearray.pro b/tests/benchmarks/script/qscriptclass_bytearray/qscriptclass_bytearray.pro new file mode 100644 index 0000000..d64f705 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/qscriptclass_bytearray.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_bench_qscriptclass_bytearray + +SOURCES += tst_qscriptclass_bytearray.cpp +RESOURCES += qscriptclass_bytearray.qrc + +include($$QT_SOURCE_TREE/examples/script/customclass/bytearrayclass.pri) + +QT = core script diff --git a/tests/benchmarks/script/qscriptclass_bytearray/qscriptclass_bytearray.qrc b/tests/benchmarks/script/qscriptclass_bytearray/qscriptclass_bytearray.qrc new file mode 100644 index 0000000..a894ee5 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/qscriptclass_bytearray.qrc @@ -0,0 +1,5 @@ + + + tests + + diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/construct-copy.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/construct-copy.js new file mode 100644 index 0000000..9c03871 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/construct-copy.js @@ -0,0 +1,3 @@ +ba = new ByteArray(123); +for (i = 0; i < 5000; ++i) + new ByteArray(ba); diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/construct.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/construct.js new file mode 100644 index 0000000..2c2bbf5 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/construct.js @@ -0,0 +1,2 @@ +for (i = 0; i < 5000; ++i) + new ByteArray(123); diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/for-in.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/for-in.js new file mode 100644 index 0000000..46bc9f3 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/for-in.js @@ -0,0 +1,3 @@ +ba = new ByteArray(8000); +for (var p in ba) + ; diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/get-element.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/get-element.js new file mode 100644 index 0000000..9f6a503 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/get-element.js @@ -0,0 +1,3 @@ +ba = new ByteArray(123); +for (i = 0; i < 10000; ++i) + ba[10]; diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/get-length.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/get-length.js new file mode 100644 index 0000000..5de2f58 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/get-length.js @@ -0,0 +1,3 @@ +ba = new ByteArray(123); +for (i = 0; i < 10000; ++i) + ba.length; diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/mid.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/mid.js new file mode 100644 index 0000000..752d875 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/mid.js @@ -0,0 +1,3 @@ +ba = new ByteArray(123); +for (i = 0; i < 3000; ++i) + ba.mid(50); diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/set-element.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/set-element.js new file mode 100644 index 0000000..4883765 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/set-element.js @@ -0,0 +1,3 @@ +ba = new ByteArray(123); +for (i = 0; i < 10000; ++i) + ba[10] = 123; diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/set-length.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/set-length.js new file mode 100644 index 0000000..18c9f59 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/set-length.js @@ -0,0 +1,3 @@ +ba = new ByteArray(); +for (i = 0; i < 10000; ++i) + ba.length = 123; diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/sum.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/sum.js new file mode 100644 index 0000000..096937d --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/sum.js @@ -0,0 +1,8 @@ +function sum(ba) { + var result = 0; + for (var i = 0; i < ba.length; ++i) + result += ba[i]; + return result; +} + +sum(new ByteArray(10000)); diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tests/trimmed.js b/tests/benchmarks/script/qscriptclass_bytearray/tests/trimmed.js new file mode 100644 index 0000000..967dba6 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tests/trimmed.js @@ -0,0 +1,3 @@ +ba = new ByteArray(123); +for (i = 0; i < 3000; ++i) + ba.trimmed(); diff --git a/tests/benchmarks/script/qscriptclass_bytearray/tst_qscriptclass_bytearray.cpp b/tests/benchmarks/script/qscriptclass_bytearray/tst_qscriptclass_bytearray.cpp new file mode 100644 index 0000000..351adc8 --- /dev/null +++ b/tests/benchmarks/script/qscriptclass_bytearray/tst_qscriptclass_bytearray.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "bytearrayclass.h" + +static QString readFile(const QString &filename) +{ + QFile file(filename); + if (!file.open(QFile::ReadOnly)) + return QString(); + QTextStream stream(&file); + stream.setCodec("UTF-8"); + return stream.readAll(); +} + +class tst_QScriptClass_ByteArray : public QObject +{ + Q_OBJECT + +public: + tst_QScriptClass_ByteArray(); + +private slots: + void benchmark_data(); + void benchmark(); + +private: + QDir testsDir; +}; + +tst_QScriptClass_ByteArray::tst_QScriptClass_ByteArray() +{ + testsDir = QDir(":/tests"); + if (!testsDir.exists()) + qWarning("*** no tests/ dir!"); +} + +void tst_QScriptClass_ByteArray::benchmark_data() +{ + QTest::addColumn("testName"); + QFileInfoList testFileInfos = testsDir.entryInfoList(QStringList() << "*.js", QDir::Files); + foreach (QFileInfo tfi, testFileInfos) { + QString name = tfi.baseName(); + QTest::newRow(name.toLatin1().constData()) << name; + } +} + +void tst_QScriptClass_ByteArray::benchmark() +{ + QFETCH(QString, testName); + QString testContents = readFile(testsDir.absoluteFilePath(testName + ".js")); + QVERIFY(!testContents.isEmpty()); + + QScriptEngine eng; + ByteArrayClass *baClass = new ByteArrayClass(&eng); + eng.globalObject().setProperty("ByteArray", baClass->constructor()); + + QBENCHMARK { + eng.evaluate(testContents); + } + QVERIFY(!eng.hasUncaughtException()); +} + +QTEST_MAIN(tst_QScriptClass_ByteArray) +#include "tst_qscriptclass_bytearray.moc" diff --git a/tests/benchmarks/script/script.pro b/tests/benchmarks/script/script.pro index da7a5a1..d4fc822 100644 --- a/tests/benchmarks/script/script.pro +++ b/tests/benchmarks/script/script.pro @@ -2,6 +2,7 @@ TEMPLATE = subdirs SUBDIRS = \ context2d \ qscriptclass \ + qscriptclass_bytearray \ qscriptengine \ qscriptvalue \ sunspider \ -- cgit v0.12 From 59f440ef78d310fdf18d655b2b768b6e59786fa9 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 4 Feb 2011 13:27:55 +0100 Subject: Fix warning about deprecated conversions of string literals to char * MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Samuel Rødal --- src/opengl/qglshaderprogram.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp index 7f41339..4598bff 100644 --- a/src/opengl/qglshaderprogram.cpp +++ b/src/opengl/qglshaderprogram.cpp @@ -261,14 +261,14 @@ bool QGLShaderPrivate::compile(QGLShader *q) log = QString::fromLatin1(logbuf); QString name = q->objectName(); - char *types[] = { + const char *types[] = { "Fragment", "Vertex", "Geometry", "" }; - char *type = types[3]; + const char *type = types[3]; if (shaderType == QGLShader::Fragment) type = types[0]; else if (shaderType == QGLShader::Vertex) -- cgit v0.12