From 9a5fb6bd5f0fb3b37897bf722e4cc1673309623c Mon Sep 17 00:00:00 2001 From: Honglei Zhang Date: Wed, 30 Nov 2011 13:36:40 +0200 Subject: Fix sqlite driver memory eating due to close failure If an ongoing query is not finalized before close function is called, sqlite driver still tries to close the connection to sqlite. In this case, sqlite reports an error to sqlite driver which is not reported to the client. The failure in close causes connection to sqlite unclosed and memory is not freed. This fix tries to finalize all queries before close function is called. The close function should succeed. Task-number: QTBUG-16967 Reviewed-by: Charles Yin --- src/sql/drivers/sqlite/qsql_sqlite.cpp | 7 +++++ tests/auto/qsqlquery/tst_qsqlquery.cpp | 49 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/sql/drivers/sqlite/qsql_sqlite.cpp b/src/sql/drivers/sqlite/qsql_sqlite.cpp index 8294a55..38e4a63 100644 --- a/src/sql/drivers/sqlite/qsql_sqlite.cpp +++ b/src/sql/drivers/sqlite/qsql_sqlite.cpp @@ -104,6 +104,7 @@ class QSQLiteDriverPrivate public: inline QSQLiteDriverPrivate() : access(0) {} sqlite3 *access; + QList results; }; @@ -286,10 +287,12 @@ QSQLiteResult::QSQLiteResult(const QSQLiteDriver* db) { d = new QSQLiteResultPrivate(this); d->access = db->d->access; + db->d->results.append(this); } QSQLiteResult::~QSQLiteResult() { + qobject_cast(driver())->d->results.removeOne(this); d->cleanup(); delete d; } @@ -553,6 +556,10 @@ bool QSQLiteDriver::open(const QString & db, const QString &, const QString &, c void QSQLiteDriver::close() { if (isOpen()) { + foreach (QSQLiteResult *result, d->results) { + result->d->finalize(); + } + if (sqlite3_close(d->access) != SQLITE_OK) setLastError(qMakeError(d->access, tr("Error closing database"), QSqlError::ConnectionError)); diff --git a/tests/auto/qsqlquery/tst_qsqlquery.cpp b/tests/auto/qsqlquery/tst_qsqlquery.cpp index 3cbdb63..652a82e 100644 --- a/tests/auto/qsqlquery/tst_qsqlquery.cpp +++ b/tests/auto/qsqlquery/tst_qsqlquery.cpp @@ -215,6 +215,8 @@ private slots: void QTBUG_14132(); void QTBUG_21884_data() { generic_data("QSQLITE"); } void QTBUG_21884(); + void QTBUG_16967_data() { generic_data("QSQLITE"); } + void QTBUG_16967(); //clean close void sqlite_constraint_data() { generic_data("QSQLITE"); } void sqlite_constraint(); @@ -3150,6 +3152,53 @@ void tst_QSqlQuery::QTBUG_21884() } } +/** + * This test case test sqlite driver close function. Sqlite driver should close cleanly + * even if there is still outstanding prepared statement. + */ +void tst_QSqlQuery::QTBUG_16967() +{ + QFETCH(QString, dbName); + { + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + db.close(); + QCOMPARE(db.lastError().type(), QSqlError::NoError); + } + { + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + QSqlQuery q(db); + q.prepare("CREATE TABLE t1 (id INTEGER PRIMARY KEY, str TEXT);"); + db.close(); + QCOMPARE(db.lastError().type(), QSqlError::NoError); + } + { + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + QSqlQuery q(db); + q.prepare("CREATE TABLE t1 (id INTEGER PRIMARY KEY, str TEXT);"); + q.exec(); + db.close(); + QCOMPARE(db.lastError().type(), QSqlError::NoError); + } + { + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + QSqlQuery q(db); + q.exec("INSERT INTO t1 (id, str) VALUES(1, \"test1\");"); + db.close(); + QCOMPARE(db.lastError().type(), QSqlError::NoError); + } + { + QSqlDatabase db = QSqlDatabase::database(dbName); + CHECK_DATABASE(db); + QSqlQuery q(db); + q.exec("SELECT * FROM t1;"); + db.close(); + QCOMPARE(db.lastError().type(), QSqlError::NoError); + } +} void tst_QSqlQuery::oraOCINumber() { -- cgit v0.12 From 8752faf0564bed86396b01529dc8ef5064150f4c Mon Sep 17 00:00:00 2001 From: Jani Hautakangas Date: Wed, 30 Nov 2011 14:41:39 +0200 Subject: Fix memory leaks in OpenVG and OpenGL resource pools Task-number: QTBUG-22743 Reviewed-by: Murray Read --- src/opengl/qgltexturepool.cpp | 6 ++---- src/openvg/qvgimagepool.cpp | 13 ++----------- src/openvg/qvgimagepool_p.h | 4 ---- 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/opengl/qgltexturepool.cpp b/src/opengl/qgltexturepool.cpp index 9ad66f2..d19b1db 100644 --- a/src/opengl/qgltexturepool.cpp +++ b/src/opengl/qgltexturepool.cpp @@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE Q_OPENGL_EXPORT extern QGLWidget* qt_gl_share_widget(); -static QGLTexturePool *qt_gl_texture_pool = 0; +Q_GLOBAL_STATIC(QGLTexturePool, qt_gl_texture_pool) class QGLTexturePoolPrivate { @@ -69,9 +69,7 @@ QGLTexturePool::~QGLTexturePool() QGLTexturePool *QGLTexturePool::instance() { - if (!qt_gl_texture_pool) - qt_gl_texture_pool = new QGLTexturePool(); - return qt_gl_texture_pool; + return qt_gl_texture_pool(); } GLuint QGLTexturePool::createTexture(GLenum target, diff --git a/src/openvg/qvgimagepool.cpp b/src/openvg/qvgimagepool.cpp index 3a187b0..cd1caf4 100644 --- a/src/openvg/qvgimagepool.cpp +++ b/src/openvg/qvgimagepool.cpp @@ -44,7 +44,7 @@ QT_BEGIN_NAMESPACE -static QVGImagePool *qt_vg_image_pool = 0; +Q_GLOBAL_STATIC(QVGImagePool, qt_vg_image_pool) class QVGImagePoolPrivate { @@ -66,16 +66,7 @@ QVGImagePool::~QVGImagePool() QVGImagePool *QVGImagePool::instance() { - if (!qt_vg_image_pool) - qt_vg_image_pool = new QVGImagePool(); - return qt_vg_image_pool; -} - -void QVGImagePool::setImagePool(QVGImagePool *pool) -{ - if (qt_vg_image_pool != pool) - delete qt_vg_image_pool; - qt_vg_image_pool = pool; + return qt_vg_image_pool(); } VGImage QVGImagePool::createTemporaryImage(VGImageFormat format, diff --git a/src/openvg/qvgimagepool_p.h b/src/openvg/qvgimagepool_p.h index 07c57bf..e4fd4e1 100644 --- a/src/openvg/qvgimagepool_p.h +++ b/src/openvg/qvgimagepool_p.h @@ -69,10 +69,6 @@ public: static QVGImagePool *instance(); - // This function can be used from system-specific graphics system - // plugins to alter the image allocation strategy. - static void setImagePool(QVGImagePool *pool); - // Create a new VGImage from the pool with the specified parameters // that is not associated with a pixmap. The VGImage is returned to // the pool when releaseImage() is called. -- cgit v0.12 From 9b40e102cc76666dfa76a7b0e3d1339d672d8a95 Mon Sep 17 00:00:00 2001 From: Ari Lehtola Date: Tue, 29 Nov 2011 10:08:05 +0200 Subject: CBA comes on top of option menu Layout has changed so that option menu starts from bottom of screen. CBA is set behind in this case Reviewed-by: Miikka Heikkinen Task-number: ou1cimx1#932398 --- src/gui/kernel/qsoftkeymanager_s60.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/gui/kernel/qsoftkeymanager_s60.cpp b/src/gui/kernel/qsoftkeymanager_s60.cpp index 999fccc..4d58baa 100644 --- a/src/gui/kernel/qsoftkeymanager_s60.cpp +++ b/src/gui/kernel/qsoftkeymanager_s60.cpp @@ -104,8 +104,13 @@ void QSoftKeyManagerPrivateS60::ensureCbaVisibilityAndResponsiviness(CEikButtonG { RDrawableWindow *cbaWindow = cba.DrawableWindow(); Q_ASSERT_X(cbaWindow, Q_FUNC_INFO, "Native CBA does not have window!"); - // Make sure CBA is visible, i.e. CBA window is on top - cbaWindow->SetOrdinalPosition(0); + // CBA comes on top of new option menu + int pos = 0; + + if(cba.ButtonGroupType()== SLafButtonGroupContainer::ECba) + pos = 1; + + cbaWindow->SetOrdinalPosition(pos); // Qt shares same CBA instance between top-level widgets, // make sure we are not faded by underlying window. cbaWindow->SetFaded(EFalse, RWindowTreeNode::EFadeIncludeChildren); -- cgit v0.12 From e443cd2cfead3d0d6c53adcd4366cc18bd253096 Mon Sep 17 00:00:00 2001 From: Marko Kenttala Date: Wed, 30 Nov 2011 09:57:47 +0200 Subject: Lightmaps demo Symbian fix Enabling night mode does not work on Symbian devices as it uses difference composition mode which is not available. Application changed to use raster graphics engine which supports difference composition mode. Task-number: ou1cimx1#932185 Reviewed-by: Murray Read --- demos/embedded/lightmaps/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/embedded/lightmaps/main.cpp b/demos/embedded/lightmaps/main.cpp index 85f74e6..f9b6fb0 100644 --- a/demos/embedded/lightmaps/main.cpp +++ b/demos/embedded/lightmaps/main.cpp @@ -44,7 +44,7 @@ int main(int argc, char **argv) { -#if defined(Q_WS_X11) +#if defined(Q_WS_X11) || defined(Q_OS_SYMBIAN) QApplication::setGraphicsSystem("raster"); #endif -- cgit v0.12 From be34b17416535a3bd257398e089ad285ee3a2d77 Mon Sep 17 00:00:00 2001 From: Satyam Bandarapu Date: Mon, 28 Nov 2011 16:25:14 +0200 Subject: Freezing Def files in Qt Adding unfrozen exports to def files in Qt Reviewed-by: mread --- src/s60installs/bwins/QtCoreu.def | 1 + src/s60installs/bwins/QtGuiu.def | 9 ++++++++- src/s60installs/bwins/QtNetworku.def | 2 ++ src/s60installs/bwins/QtOpenGLu.def | 2 ++ src/s60installs/eabi/QtCoreu.def | 1 + src/s60installs/eabi/QtGuiu.def | 7 +++++++ src/s60installs/eabi/QtNetworku.def | 2 ++ src/s60installs/eabi/QtOpenGLu.def | 1 + 8 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/s60installs/bwins/QtCoreu.def b/src/s60installs/bwins/QtCoreu.def index 9181dd7..08c67e0 100644 --- a/src/s60installs/bwins/QtCoreu.def +++ b/src/s60installs/bwins/QtCoreu.def @@ -4885,4 +4885,5 @@ EXPORTS ?updateDir@QFactoryLoader@@QAEXABVQString@@AAVQSettings@@@Z @ 4884 NONAME ; void QFactoryLoader::updateDir(class QString const &, class QSettings &) ?disconnectNotify@QFutureWatcherBase@@MAEXPBD@Z @ 4885 NONAME ; void QFutureWatcherBase::disconnectNotify(char const *) ?maybeQueueForLater@QActiveObject@@QAE_NXZ @ 4886 NONAME ; bool QActiveObject::maybeQueueForLater(void) + ?activeObjectError@QEventDispatcherSymbian@@QAEXH@Z @ 4887 NONAME ; void QEventDispatcherSymbian::activeObjectError(int) diff --git a/src/s60installs/bwins/QtGuiu.def b/src/s60installs/bwins/QtGuiu.def index 6f903fd..4e0d7df 100644 --- a/src/s60installs/bwins/QtGuiu.def +++ b/src/s60installs/bwins/QtGuiu.def @@ -13990,4 +13990,11 @@ EXPORTS ?resetFontEngineCache@QTextEngine@@QAEXXZ @ 13989 NONAME ; void QTextEngine::resetFontEngineCache(void) ?symbianHandleLiteModeStartup@QApplicationPrivate@@QAEXXZ @ 13990 NONAME ; void QApplicationPrivate::symbianHandleLiteModeStartup(void) ?_q_cleanupWinIds@QWidgetPrivate@@QAEXXZ @ 13991 NONAME ; void QWidgetPrivate::_q_cleanupWinIds(void) - ?qt_s60_setEditorFlags@@YAXH@Z @ 13992 NONAME ; void qt_s60_setEditorFlags(int) + ?qt_s60_setEditorFlags@@YAXH@Z @ 13992 NONAME ; void qt_s60_setEditorFlags(int) + ?adjustViewOptionsForIndex@QTreeViewPrivate@@UBEXPAVQStyleOptionViewItemV4@@ABVQModelIndex@@@Z @ 13993 NONAME ; void QTreeViewPrivate::adjustViewOptionsForIndex(class QStyleOptionViewItemV4 *, class QModelIndex const &) const + ?setNativePaintMode@QSymbianGraphicsSystemHelper@@SAXPAVQWidget@@W4NativePaintMode@1@@Z @ 13994 NONAME ; void QSymbianGraphicsSystemHelper::setNativePaintMode(class QWidget *, enum QSymbianGraphicsSystemHelper::NativePaintMode) + ?calcLogicalIndices@QTreeViewPrivate@@QBEXPAV?$QVector@H@@PAV?$QVector@W4ViewItemPosition@QStyleOptionViewItemV4@@@@@Z @ 13995 NONAME ; void QTreeViewPrivate::calcLogicalIndices(class QVector *, class QVector *) const + ?setReceiveNativePaintEvents@QSymbianGraphicsSystemHelper@@SAXPAVQWidget@@_N@Z @ 13996 NONAME ; void QSymbianGraphicsSystemHelper::setReceiveNativePaintEvents(class QWidget *, bool) + ?setNativePaintMode@QSymbianGraphicsSystemHelper@@SAXPAVCCoeControl@@W4NativePaintMode@1@@Z @ 13997 NONAME ; void QSymbianGraphicsSystemHelper::setNativePaintMode(class CCoeControl *, enum QSymbianGraphicsSystemHelper::NativePaintMode) + ?setIgnoreFocusChanged@QSymbianGraphicsSystemHelper@@SAXPAVQWidget@@_N@Z @ 13998 NONAME ; void QSymbianGraphicsSystemHelper::setIgnoreFocusChanged(class QWidget *, bool) + diff --git a/src/s60installs/bwins/QtNetworku.def b/src/s60installs/bwins/QtNetworku.def index 2f8a7de..e03c024 100644 --- a/src/s60installs/bwins/QtNetworku.def +++ b/src/s60installs/bwins/QtNetworku.def @@ -1258,4 +1258,6 @@ EXPORTS ?metaObject@QHttpMultiPart@@UBEPBUQMetaObject@@XZ @ 1257 NONAME ; struct QMetaObject const * QHttpMultiPart::metaObject(void) const ?qt_static_metacall@QNetworkSession@@CAXPAVQObject@@W4Call@QMetaObject@@HPAPAX@Z @ 1258 NONAME ; void QNetworkSession::qt_static_metacall(class QObject *, enum QMetaObject::Call, int, void * *) ?startPolling@QNetworkConfigurationManagerPrivate@@AAEXXZ @ 1259 NONAME ; void QNetworkConfigurationManagerPrivate::startPolling(void) + ?testSslOption@QSslConfiguration@@QBE_NW4SslOption@QSsl@@@Z @ 1260 NONAME ; bool QSslConfiguration::testSslOption(enum QSsl::SslOption) const + ?setSslOption@QSslConfiguration@@QAEXW4SslOption@QSsl@@_N@Z @ 1261 NONAME ; void QSslConfiguration::setSslOption(enum QSsl::SslOption, bool) diff --git a/src/s60installs/bwins/QtOpenGLu.def b/src/s60installs/bwins/QtOpenGLu.def index 664e981..ed73a33 100644 --- a/src/s60installs/bwins/QtOpenGLu.def +++ b/src/s60installs/bwins/QtOpenGLu.def @@ -878,4 +878,6 @@ EXPORTS ?glGetShaderPrecisionFormat@QGLFunctions@@QAEXIIPAH0@Z @ 877 NONAME ; void QGLFunctions::glGetShaderPrecisionFormat(unsigned int, unsigned int, int *, int *) ?qt_static_metacall@QGLShaderProgram@@CAXPAVQObject@@W4Call@QMetaObject@@HPAPAX@Z @ 878 NONAME ; void QGLShaderProgram::qt_static_metacall(class QObject *, enum QMetaObject::Call, int, void * *) ?insert@QGLContextGroupResourceBase@@QAEXPBVQGLContext@@PAX@Z @ 879 NONAME ; void QGLContextGroupResourceBase::insert(class QGLContext const *, void *) + ?contextDeleted@QGLContextGroupResourceBase@@UAEXPBVQGLContext@@@Z @ 880 NONAME ; void QGLContextGroupResourceBase::contextDeleted(class QGLContext const *) + ?contextDeleted@QGLTextureGlyphCache@@UAEXPBVQGLContext@@@Z @ 881 NONAME ; void QGLTextureGlyphCache::contextDeleted(class QGLContext const *) diff --git a/src/s60installs/eabi/QtCoreu.def b/src/s60installs/eabi/QtCoreu.def index 5436390..d80aecb 100644 --- a/src/s60installs/eabi/QtCoreu.def +++ b/src/s60installs/eabi/QtCoreu.def @@ -4165,4 +4165,5 @@ EXPORTS _ZN14QFactoryLoader9updateDirERK7QStringR9QSettings @ 4164 NONAME _ZN23QCoreApplicationPrivate26rebuildInstallLibraryPathsEv @ 4165 NONAME _ZN13QActiveObject18maybeQueueForLaterEv @ 4166 NONAME + _ZN23QEventDispatcherSymbian17activeObjectErrorEi @ 4167 NONAME diff --git a/src/s60installs/eabi/QtGuiu.def b/src/s60installs/eabi/QtGuiu.def index 07f0929..8aaa65d 100644 --- a/src/s60installs/eabi/QtGuiu.def +++ b/src/s60installs/eabi/QtGuiu.def @@ -12804,3 +12804,10 @@ EXPORTS _ZN14QWidgetPrivate16_q_cleanupWinIdsEv @ 12803 NONAME _ZN19QApplicationPrivate28symbianHandleLiteModeStartupEv @ 12804 NONAME _Z21qt_s60_setEditorFlagsi @ 12805 NONAME + _ZN28QSymbianGraphicsSystemHelper18setNativePaintModeEP11CCoeControlNS_15NativePaintModeE @ 12806 NONAME + _ZN28QSymbianGraphicsSystemHelper18setNativePaintModeEP7QWidgetNS_15NativePaintModeE @ 12807 NONAME + _ZN28QSymbianGraphicsSystemHelper21setIgnoreFocusChangedEP7QWidgetb @ 12808 NONAME + _ZN28QSymbianGraphicsSystemHelper27setReceiveNativePaintEventsEP7QWidgetb @ 12809 NONAME + _ZNK16QTreeViewPrivate18calcLogicalIndicesEP7QVectorIiEPS0_IN22QStyleOptionViewItemV416ViewItemPositionEE @ 12810 NONAME + _ZNK16QTreeViewPrivate25adjustViewOptionsForIndexEP22QStyleOptionViewItemV4RK11QModelIndex @ 12811 NONAME + diff --git a/src/s60installs/eabi/QtNetworku.def b/src/s60installs/eabi/QtNetworku.def index 6e7dedd..54f5b5f 100644 --- a/src/s60installs/eabi/QtNetworku.def +++ b/src/s60installs/eabi/QtNetworku.def @@ -1270,4 +1270,6 @@ EXPORTS _ZNK9QHttpParteqERKS_ @ 1269 NONAME _ZTI14QHttpMultiPart @ 1270 NONAME _ZTV14QHttpMultiPart @ 1271 NONAME + _ZN17QSslConfiguration12setSslOptionEN4QSsl9SslOptionEb @ 1272 NONAME + _ZNK17QSslConfiguration13testSslOptionEN4QSsl9SslOptionE @ 1273 NONAME diff --git a/src/s60installs/eabi/QtOpenGLu.def b/src/s60installs/eabi/QtOpenGLu.def index 2d9aa8f..edb5e03 100644 --- a/src/s60installs/eabi/QtOpenGLu.def +++ b/src/s60installs/eabi/QtOpenGLu.def @@ -782,4 +782,5 @@ EXPORTS _ZThn104_N20QGLTextureGlyphCacheD0Ev @ 781 NONAME _ZThn104_N20QGLTextureGlyphCacheD1Ev @ 782 NONAME _ZThn8_NK16QGLWindowSurface8featuresEv @ 783 NONAME + _ZN27QGLContextGroupResourceBase14contextDeletedEPK10QGLContext @ 784 NONAME -- cgit v0.12 From 916076bfec520210966f67ae211af65b21a29dac Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Thu, 24 Nov 2011 11:40:52 +0000 Subject: Symbian - prefer sessions started by this process to choose proxy When WLAN and 3G connections are both active, the proxy for the wrong connection may have been chosen in the case of plain sockets or QNetworkAccessManager with an invalid configuration. When enumarating active connections to choose a proxy, prefer a connection that was opened by this process. Task-number: QTBUG-22615 Task-number: ou1cimx1#930701 Reviewed-by: mread --- src/corelib/kernel/qcore_symbian_p.cpp | 32 ++++++++++++ src/corelib/kernel/qcore_symbian_p.h | 22 +++++++++ src/network/kernel/qnetworkproxy_symbian.cpp | 27 ++++++++++ .../bearer/symbian/qnetworksession_impl.cpp | 57 +++++++++++++++++----- src/plugins/bearer/symbian/qnetworksession_impl.h | 3 ++ src/s60installs/bwins/QtCoreu.def | 5 +- src/s60installs/eabi/QtCoreu.def | 5 +- 7 files changed, 137 insertions(+), 14 deletions(-) diff --git a/src/corelib/kernel/qcore_symbian_p.cpp b/src/corelib/kernel/qcore_symbian_p.cpp index 4f953a7..65ec3fe 100644 --- a/src/corelib/kernel/qcore_symbian_p.cpp +++ b/src/corelib/kernel/qcore_symbian_p.cpp @@ -246,6 +246,38 @@ RConnection* QSymbianSocketManager::defaultConnection() const return iDefaultConnection; } +void QSymbianSocketManager::addActiveConnection(TUint32 identifier) +{ + QMutexLocker l(&iMutex); + activeConnectionsMap[identifier]++; +#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG + qDebug() << "addActiveConnection" << identifier << activeConnectionsMap[identifier]; +#endif +} + +void QSymbianSocketManager::removeActiveConnection(TUint32 identifier) +{ + QMutexLocker l(&iMutex); + int& val(activeConnectionsMap[identifier]); + Q_ASSERT(val > 0); +#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG + qDebug() << "removeActiveConnection" << identifier << val - 1; +#endif + if (val <= 1) + activeConnectionsMap.remove(identifier); + else + val--; +} + +QList QSymbianSocketManager::activeConnections() const +{ + QMutexLocker l(&iMutex); +#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG + qDebug() << "activeConnections" << activeConnectionsMap.keys(); +#endif + return activeConnectionsMap.keys(); +} + Q_GLOBAL_STATIC(QSymbianSocketManager, qt_symbianSocketManager); QSymbianSocketManager& QSymbianSocketManager::instance() diff --git a/src/corelib/kernel/qcore_symbian_p.h b/src/corelib/kernel/qcore_symbian_p.h index a8f576d..6176ab5 100644 --- a/src/corelib/kernel/qcore_symbian_p.h +++ b/src/corelib/kernel/qcore_symbian_p.h @@ -247,6 +247,27 @@ public: /*! \internal + Add an opened connection to the active list + \param an open connection + */ + void addActiveConnection(TUint32 identifier); + + /*! + \internal + Remove a connection from the active list + \param a closed connection + */ + void removeActiveConnection(TUint32 identifier); + + /*! + \internal + Add an opened connection to the active list + \param an open connection + */ + QList activeConnections() const; + + /*! + \internal Gets a reference to the singleton socket manager */ static QSymbianSocketManager& instance(); @@ -258,6 +279,7 @@ private: int iNextSocket; QHash socketMap; QHash reverseSocketMap; + QHash activeConnectionsMap; mutable QMutex iMutex; RSocketServ iSocketServ; RConnection *iDefaultConnection; diff --git a/src/network/kernel/qnetworkproxy_symbian.cpp b/src/network/kernel/qnetworkproxy_symbian.cpp index 73068d6..e96c372 100644 --- a/src/network/kernel/qnetworkproxy_symbian.cpp +++ b/src/network/kernel/qnetworkproxy_symbian.cpp @@ -60,6 +60,7 @@ #include #include #include +#include using namespace CommsDat; @@ -73,6 +74,25 @@ public: void setIapId(TUint32 iapId) { valid = true; id = iapId; } bool isValid() { return valid; } TUint32 iapId() { return id; } + static SymbianIapId fromConfiguration(const QNetworkConfiguration& config) + { + SymbianIapId iapId; + // Note: the following code assumes that the identifier is in format + // I_xxxx where xxxx is the identifier of IAP. This is meant as a + // temporary solution until there is a support for returning + // implementation specific identifier. + const int generalPartLength = 2; + QString idString(config.identifier().mid(generalPartLength)); + bool success; + uint id = idString.toUInt(&success); + if (success) + iapId.setIapId(id); + else + qWarning() << "Failed to convert identifier to access point identifier: " + << config.identifier(); + return iapId; + } + private: bool valid; TUint32 id; @@ -122,9 +142,16 @@ QNetworkConfiguration SymbianProxyQuery::findCurrentConfigurationFromServiceNetw QNetworkConfiguration SymbianProxyQuery::findCurrentConfiguration(QNetworkConfigurationManager& configurationManager) { + QList openConfigurations = QSymbianSocketManager::instance().activeConnections(); QList activeConfigurations = configurationManager.allConfigurations( QNetworkConfiguration::Active); + for (int i = 0; i < activeConfigurations.count(); i++) { + // get first configuration which was opened by this process + if (openConfigurations.contains(SymbianIapId::fromConfiguration(activeConfigurations.at(i)).iapId())) + return activeConfigurations.at(i); + } if (activeConfigurations.count() > 0) { + // get first active configuration opened by any process return activeConfigurations.at(0); } else { // No active configurations, try default one diff --git a/src/plugins/bearer/symbian/qnetworksession_impl.cpp b/src/plugins/bearer/symbian/qnetworksession_impl.cpp index f5f71cf..58ce8fe 100644 --- a/src/plugins/bearer/symbian/qnetworksession_impl.cpp +++ b/src/plugins/bearer/symbian/qnetworksession_impl.cpp @@ -64,7 +64,7 @@ QNetworkSessionPrivateImpl::QNetworkSessionPrivateImpl(SymbianEngine *engine) ipConnectionNotifier(0), ipConnectionStarter(0), iHandleStateNotificationsFromManager(false), iFirstSync(true), iStoppedByUser(false), iClosedByUser(false), iError(QNetworkSession::UnknownSessionError), iALREnabled(0), - iConnectInBackground(false), isOpening(false) + iConnectInBackground(false), iCurrentIap(0), isOpening(false) { #ifdef SNAP_FUNCTIONALITY_AVAILABLE @@ -77,6 +77,7 @@ QNetworkSessionPrivateImpl::QNetworkSessionPrivateImpl(SymbianEngine *engine) void QNetworkSessionPrivateImpl::closeHandles() { QMutexLocker lock(&mutex); + updateCurrentIap(0); // Cancel Connection Progress Notifications first. // Note: ConnectionNotifier must be destroyed before RConnection::Close() // => deleting ipConnectionNotifier results RConnection::CancelProgressNotification() @@ -637,6 +638,8 @@ void QNetworkSessionPrivateImpl::accept() QSymbianSocketManager::instance().setDefaultConnection(&iConnection); + updateCurrentIap(iNewRoamingIap); + newState(QNetworkSession::Connected, iNewRoamingIap); } #endif @@ -867,19 +870,31 @@ quint64 QNetworkSessionPrivateImpl::activeTime() const return startTime.secsTo(QDateTime::currentDateTime()); } -QNetworkConfiguration QNetworkSessionPrivateImpl::activeConfiguration(TUint32 iapId) const +bool QNetworkSessionPrivateImpl::activeIapId(TUint32& iapId) const { - if (iapId == 0) { - _LIT(KSetting, "IAP\\Id"); - iConnection.GetIntSetting(KSetting, iapId); + if (!iConnection.SubSessionHandle()) + return false; + _LIT(KSetting, "IAP\\Id"); + TInt err = iConnection.GetIntSetting(KSetting, iapId); + if (err != KErrNone) + return false; #ifdef SNAP_FUNCTIONALITY_AVAILABLE - // Check if this is an Easy WLAN configuration. On Symbian^3 RConnection may report - // the used configuration as 'EasyWLAN' IAP ID if someone has just opened the configuration - // from WLAN Scan dialog, _and_ that connection is still up. We need to find the - // real matching configuration. Function alters the Easy WLAN ID to real IAP ID (only if - // easy WLAN): - easyWlanTrueIapId(iapId); + // Check if this is an Easy WLAN configuration. On Symbian^3 RConnection may report + // the used configuration as 'EasyWLAN' IAP ID if someone has just opened the configuration + // from WLAN Scan dialog, _and_ that connection is still up. We need to find the + // real matching configuration. Function alters the Easy WLAN ID to real IAP ID (only if + // easy WLAN): + easyWlanTrueIapId(iapId); #endif + return true; +} + +QNetworkConfiguration QNetworkSessionPrivateImpl::activeConfiguration(TUint32 iapId) const +{ + if (iapId == 0) { + bool ok = activeIapId(iapId); + if (!ok) + return QNetworkConfiguration(); } #ifdef SNAP_FUNCTIONALITY_AVAILABLE @@ -1015,6 +1030,20 @@ QNetworkConfiguration QNetworkSessionPrivateImpl::activeConfiguration(TUint32 ia return publicConfig; } +void QNetworkSessionPrivateImpl::updateCurrentIap(TUint32 iapId) +{ + if (iCurrentIap == iapId) + return; + + if (iCurrentIap != 0) + QSymbianSocketManager::instance().removeActiveConnection(iCurrentIap); + + iCurrentIap = iapId; + + if (iCurrentIap != 0) + QSymbianSocketManager::instance().addActiveConnection(iCurrentIap); +} + void QNetworkSessionPrivateImpl::ConnectionStartComplete(TInt statusCode) { #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG @@ -1028,7 +1057,10 @@ void QNetworkSessionPrivateImpl::ConnectionStartComplete(TInt statusCode) case KErrNone: // Connection created successfully { TInt error = KErrNone; - QNetworkConfiguration newActiveConfig = activeConfiguration(); + TUint32 iapId; + QNetworkConfiguration newActiveConfig; + if (activeIapId(iapId)) + newActiveConfig = activeConfiguration(iapId); if (!newActiveConfig.isValid()) { // RConnection startup was successful but no configuration // was found. That indicates that user has chosen to create a @@ -1038,6 +1070,7 @@ void QNetworkSessionPrivateImpl::ConnectionStartComplete(TInt statusCode) error = KErrGeneral; } else { QSymbianSocketManager::instance().setDefaultConnection(&iConnection); + updateCurrentIap(iapId); } if (error != KErrNone) { isOpen = false; diff --git a/src/plugins/bearer/symbian/qnetworksession_impl.h b/src/plugins/bearer/symbian/qnetworksession_impl.h index 17a051e..9e02e5b 100644 --- a/src/plugins/bearer/symbian/qnetworksession_impl.h +++ b/src/plugins/bearer/symbian/qnetworksession_impl.h @@ -143,6 +143,8 @@ private: void handleSymbianConnectionStatusChange(TInt aConnectionStatus, TInt aError, TUint accessPointId = 0); QNetworkConfiguration bestConfigFromSNAP(const QNetworkConfiguration& snapConfig) const; QNetworkConfiguration activeConfiguration(TUint32 iapId = 0) const; + bool activeIapId(TUint32 &iapId) const; + void updateCurrentIap(TUint32 iapId); #ifndef QT_NO_NETWORKINTERFACE QNetworkInterface interface(TUint iapId) const; #endif @@ -186,6 +188,7 @@ private: // data TUint32 iOldRoamingIap; TUint32 iNewRoamingIap; + TUint32 iCurrentIap; bool isOpening; diff --git a/src/s60installs/bwins/QtCoreu.def b/src/s60installs/bwins/QtCoreu.def index 08c67e0..cfd2cec 100644 --- a/src/s60installs/bwins/QtCoreu.def +++ b/src/s60installs/bwins/QtCoreu.def @@ -4885,5 +4885,8 @@ EXPORTS ?updateDir@QFactoryLoader@@QAEXABVQString@@AAVQSettings@@@Z @ 4884 NONAME ; void QFactoryLoader::updateDir(class QString const &, class QSettings &) ?disconnectNotify@QFutureWatcherBase@@MAEXPBD@Z @ 4885 NONAME ; void QFutureWatcherBase::disconnectNotify(char const *) ?maybeQueueForLater@QActiveObject@@QAE_NXZ @ 4886 NONAME ; bool QActiveObject::maybeQueueForLater(void) - ?activeObjectError@QEventDispatcherSymbian@@QAEXH@Z @ 4887 NONAME ; void QEventDispatcherSymbian::activeObjectError(int) + ?activeConnections@QSymbianSocketManager@@QBE?AV?$QList@K@@XZ @ 4887 NONAME ; class QList QSymbianSocketManager::activeConnections(void) const + ?removeActiveConnection@QSymbianSocketManager@@QAEXK@Z @ 4888 NONAME ; void QSymbianSocketManager::removeActiveConnection(unsigned long) + ?addActiveConnection@QSymbianSocketManager@@QAEXK@Z @ 4889 NONAME ; void QSymbianSocketManager::addActiveConnection(unsigned long) + ?activeObjectError@QEventDispatcherSymbian@@QAEXH@Z @ 4890 NONAME ; void QEventDispatcherSymbian::activeObjectError(int) diff --git a/src/s60installs/eabi/QtCoreu.def b/src/s60installs/eabi/QtCoreu.def index d80aecb..44aa48c 100644 --- a/src/s60installs/eabi/QtCoreu.def +++ b/src/s60installs/eabi/QtCoreu.def @@ -4165,5 +4165,8 @@ EXPORTS _ZN14QFactoryLoader9updateDirERK7QStringR9QSettings @ 4164 NONAME _ZN23QCoreApplicationPrivate26rebuildInstallLibraryPathsEv @ 4165 NONAME _ZN13QActiveObject18maybeQueueForLaterEv @ 4166 NONAME - _ZN23QEventDispatcherSymbian17activeObjectErrorEi @ 4167 NONAME + _ZN21QSymbianSocketManager19addActiveConnectionEm @ 4167 NONAME + _ZN21QSymbianSocketManager22removeActiveConnectionEm @ 4168 NONAME + _ZN23QEventDispatcherSymbian17activeObjectErrorEi @ 4169 NONAME + _ZNK21QSymbianSocketManager17activeConnectionsEv @ 4170 NONAME -- cgit v0.12 From 8a76e0e327ba6330e1520fdd898a7dcf57a9bf2a Mon Sep 17 00:00:00 2001 From: Shane Kearns Date: Thu, 24 Nov 2011 16:49:23 +0000 Subject: doc - document symbian behaviour of QFile::link Task-number: ou1cimx1#943243 Reviewed-by: mread --- src/corelib/io/qfile.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/corelib/io/qfile.cpp b/src/corelib/io/qfile.cpp index cdee7ce..09d0c58 100644 --- a/src/corelib/io/qfile.cpp +++ b/src/corelib/io/qfile.cpp @@ -824,8 +824,7 @@ QFile::rename(const QString &oldName, const QString &newName) \note To create a valid link on Windows, \a linkName must have a \c{.lnk} file extension. - \note On Symbian, no link is created and false is returned if fileName() - currently specifies a directory. + \note Symbian filesystem does not support links. \sa setFileName() */ -- cgit v0.12 From d2a3b9ee8c9329cac96b5e509df0e6a69dbef91c Mon Sep 17 00:00:00 2001 From: Jani Hautakangas Date: Thu, 1 Dec 2011 09:42:00 +0200 Subject: Fix def files Reviewed-by: TRUSTME --- src/s60installs/bwins/QtOpenVGu.def | 2 +- src/s60installs/eabi/QtOpenVGu.def | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/s60installs/bwins/QtOpenVGu.def b/src/s60installs/bwins/QtOpenVGu.def index 547931e..dc1ddce 100644 --- a/src/s60installs/bwins/QtOpenVGu.def +++ b/src/s60installs/bwins/QtOpenVGu.def @@ -159,7 +159,7 @@ EXPORTS ?hibernate@QVGImagePool@@UAEXXZ @ 158 NONAME ; void QVGImagePool::hibernate(void) ?qt_vg_destroy_context@@YAXPAVQEglContext@@H@Z @ 159 NONAME ; void qt_vg_destroy_context(class QEglContext *, int) ??0QVGImagePool@@QAE@XZ @ 160 NONAME ; QVGImagePool::QVGImagePool(void) - ?setImagePool@QVGImagePool@@SAXPAV1@@Z @ 161 NONAME ; void QVGImagePool::setImagePool(class QVGImagePool *) + ?setImagePool@QVGImagePool@@SAXPAV1@@Z @ 161 NONAME ABSENT ; void QVGImagePool::setImagePool(class QVGImagePool *) ?pixmapLRU@QVGImagePool@@IAEPAVQVGPixmapData@@XZ @ 162 NONAME ; class QVGPixmapData * QVGImagePool::pixmapLRU(void) ?qt_vg_create_context@@YAPAVQEglContext@@PAVQPaintDevice@@H@Z @ 163 NONAME ; class QEglContext * qt_vg_create_context(class QPaintDevice *, int) ?reclaimImages@QVGPixmapData@@UAEXXZ @ 164 NONAME ; void QVGPixmapData::reclaimImages(void) diff --git a/src/s60installs/eabi/QtOpenVGu.def b/src/s60installs/eabi/QtOpenVGu.def index a66df98..f87c71d 100644 --- a/src/s60installs/eabi/QtOpenVGu.def +++ b/src/s60installs/eabi/QtOpenVGu.def @@ -176,7 +176,7 @@ EXPORTS _ZN12QVGImagePool11detachImageEP13QVGPixmapData @ 175 NONAME _ZN12QVGImagePool12reclaimSpaceE13VGImageFormatllP13QVGPixmapData @ 176 NONAME _ZN12QVGImagePool12releaseImageEP13QVGPixmapDatam @ 177 NONAME - _ZN12QVGImagePool12setImagePoolEPS_ @ 178 NONAME + _ZN12QVGImagePool12setImagePoolEPS_ @ 178 NONAME ABSENT _ZN12QVGImagePool13removeFromLRUEP13QVGPixmapData @ 179 NONAME _ZN12QVGImagePool15moveToHeadOfLRUEP13QVGPixmapData @ 180 NONAME _ZN12QVGImagePool20createImageForPixmapE13VGImageFormatllmP13QVGPixmapData @ 181 NONAME -- cgit v0.12 From 1affc6720a338f59b824af1825936335e7ce1602 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 1 Dec 2011 15:01:01 +0200 Subject: Symbian: Fix CBA display on application with multiple windows If an application has more than one window and each window has widgets that can receive focus, sometimes a situation happened when the last focused window isn't actually the window that contains the current focused widget, which led to displaying incorrect CBA. Changed CBA displaying evaluation to be done whenever any control is focused instead of just windows. This can add a couple of extra S60->setRecursiveDecorationsVisibility() calls to some applications, but since that is typically already called multiple times anyway, it shouldn't be an issue. Task-number: ou1cimx1#935663 Reviewed-by: Sami Merila --- src/gui/kernel/qapplication_s60.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index ca9c3a3..c94d66d 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -1548,6 +1548,7 @@ void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) || (qwidget->windowType() & Qt::Popup) == Qt::Popup) return; + QWidget *parentWindow = qwidget->window(); if (IsFocused() && IsVisible()) { if (m_symbianPopupIsOpen) { QWidget *fw = QApplication::focusWidget(); @@ -1562,11 +1563,10 @@ void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) qwidget->d_func()->setWindowIcon_sys(true); qwidget->d_func()->setWindowTitle_sys(qwidget->windowTitle()); #ifdef Q_WS_S60 - if (qwidget->isWindow()) - S60->setRecursiveDecorationsVisibility(qwidget, qwidget->windowState()); + if (parentWindow->isWindow()) + S60->setRecursiveDecorationsVisibility(parentWindow, parentWindow->windowState()); #endif } else { - QWidget *parentWindow = qwidget->window(); if (QApplication::activeWindow() == parentWindow && !hasFocusedAndVisibleChild(parentWindow)) { if (CCoeEnv::Static()->AppUi()->IsDisplayingMenuOrDialog() || S60->menuBeingConstructed) { QWidget *fw = QApplication::focusWidget(); -- cgit v0.12 From a6ab48bec20a746ae9bd54c1ea8a260af9967c04 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Tue, 29 Nov 2011 08:52:14 +0100 Subject: qmlplugindump: Avoid reporting types as attaching to themselves. Task-number: QTCREATORBUG-6625 Change-Id: I9f778757b490cb5d79249b92f8c7b6d9d1df66e6 Reviewed-by: Leandro Melo (backported from 409aecbe1e30aa1319ea72bf830c405827e13d21) --- tools/qmlplugindump/main.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 4f523b9..9647704 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -308,8 +308,12 @@ public: qml->writeArrayBinding(QLatin1String("exports"), exports); if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) { - qml->writeScriptBinding(QLatin1String("attachedType"), enquote( - convertToId(attachedType))); + // Can happen when a type is registered that returns itself as attachedPropertiesType() + // because there is no creatable type to attach to. + if (attachedType != meta) { + qml->writeScriptBinding(QLatin1String("attachedType"), enquote( + convertToId(attachedType))); + } } } -- cgit v0.12 From 2cfbc2b874ebec52ee76adc60b51dca138827d8c Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Fri, 2 Dec 2011 09:30:48 +0100 Subject: qmlplugindump: Fix missing flush for objects that don't fit a line. The missing flush could result in script bindings appearing after the closing brace of an object. Change-Id: If05764619668cc4a86f7364f6cd7feeb0d6f6e32 Reviewed-by: Leandro Melo (backported from e01219b77b1e889e70437635905d7ff820568e23) --- tools/qmlplugindump/qmlstreamwriter.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/qmlplugindump/qmlstreamwriter.cpp b/tools/qmlplugindump/qmlstreamwriter.cpp index ca52a7a..48d1b02 100644 --- a/tools/qmlplugindump/qmlstreamwriter.cpp +++ b/tools/qmlplugindump/qmlstreamwriter.cpp @@ -93,8 +93,7 @@ void QmlStreamWriter::writeEndObject() m_pendingLineLength = 0; m_maybeOneline = false; } else { - if (m_maybeOneline) - flushPotentialLinesWithNewlines(); + flushPotentialLinesWithNewlines(); --m_indentDepth; writeIndent(); m_stream->write("}\n"); -- cgit v0.12 From 1c9d051399c683bf89dfd1bca54bc3c48d534087 Mon Sep 17 00:00:00 2001 From: mread Date: Fri, 2 Dec 2011 11:44:56 +0000 Subject: Correct client rect calculation for MCL The clientRect calculation was adjusting the rectangle for the status pane. But this is now taken into account in the rectangle from Avkon, so we do not need to adjust when the status pane is there. However Avkon is supplying an inconsistent clientRect depending on whether we have all Avkon panes disabled (status pane and CBAs). When enabled, Avkon takes the splitview keyboard off the clientRect space, when disabled, Avkon ignores the splitview keyboard. Since we want to ignore the splitview keyboard effect on the clientRect, we only adjust when the Avkon panes are enabled. This has been tested with splitview keyboard, orientation change and debug output of the client rectangle returned in the following situations: - Normal app with status pane and CBA - App with CBA but invisible status pane - App with status pane and CBA disabled Task-number: ou1cimx1#938780 Reviewed-by: Miikka Heikkinen --- src/gui/kernel/qapplication_s60.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index c94d66d..587c0f2 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -273,19 +273,16 @@ void QS60Data::controlVisibilityChanged(CCoeControl *control, bool visible) TRect QS60Data::clientRect() { TRect r = static_cast(S60->appUi())->ClientRect(); - if (S60->partialKeyboardOpen) { - // Adjust client rect when splitview is open, since for some curious reason - // native side insists that clientRect starts from (0,0) even though status - // pane might be visible. + if (S60->partialKeyboardOpen && !QApplication::testAttribute(Qt::AA_S60DontConstructApplicationPanes)) { + // Adjust client rect when splitview is open + // We want it to take the client rect space as if the splitview keyboard was not there TRect statusPaneRect; TRect mainRect; AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EStatusPane, statusPaneRect); AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EMainPane, mainRect); int clientAreaHeight = mainRect.Height(); CEikStatusPane *const s = S60->statusPane(); - if (s && s->IsVisible()) - r.Move(0, statusPaneRect.Height()); - else + if (!(s && s->IsVisible())) clientAreaHeight += statusPaneRect.Height(); r.SetHeight(clientAreaHeight); } -- cgit v0.12 > 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375

/* Write Python objects to files and read them back.
   This is intended for writing and reading compiled Python code only;
   a true persistent storage facility would be much harder, since
   it would have to take circular links and sharing into account. */

#define PY_SSIZE_T_CLEAN

#include "Python.h"
#include "longintrepr.h"
#include "code.h"
#include "marshal.h"

#define ABS(x) ((x) < 0 ? -(x) : (x))

/* High water mark to determine when the marshalled object is dangerously deep
 * and risks coring the interpreter.  When the object stack gets this deep,
 * raise an exception instead of continuing.
 * On Windows debug builds, reduce this value.
 */
#if defined(MS_WINDOWS) && defined(_DEBUG)
#define MAX_MARSHAL_STACK_DEPTH 1500
#else
#define MAX_MARSHAL_STACK_DEPTH 2000
#endif

#define TYPE_NULL               '0'
#define TYPE_NONE               'N'
#define TYPE_FALSE              'F'
#define TYPE_TRUE               'T'
#define TYPE_STOPITER           'S'
#define TYPE_ELLIPSIS           '.'
#define TYPE_INT                'i'
#define TYPE_INT64              'I'
#define TYPE_FLOAT              'f'
#define TYPE_BINARY_FLOAT       'g'
#define TYPE_COMPLEX            'x'
#define TYPE_BINARY_COMPLEX     'y'
#define TYPE_LONG               'l'
#define TYPE_STRING             's'
#define TYPE_TUPLE              '('
#define TYPE_LIST               '['
#define TYPE_DICT               '{'
#define TYPE_CODE               'c'
#define TYPE_UNICODE            'u'
#define TYPE_UNKNOWN            '?'
#define TYPE_SET                '<'
#define TYPE_FROZENSET          '>'

#define WFERR_OK 0
#define WFERR_UNMARSHALLABLE 1
#define WFERR_NESTEDTOODEEP 2
#define WFERR_NOMEMORY 3

typedef struct {
    FILE *fp;
    int error;  /* see WFERR_* values */
    int depth;
    /* If fp == NULL, the following are valid: */
    PyObject *str;
    char *ptr;
    char *end;
    PyObject *strings; /* dict on marshal, list on unmarshal */
    int version;
} WFILE;

#define w_byte(c, p) if (((p)->fp)) putc((c), (p)->fp); \
                      else if ((p)->ptr != (p)->end) *(p)->ptr++ = (c); \
                           else w_more(c, p)

static void
w_more(int c, WFILE *p)
{
    Py_ssize_t size, newsize;
    if (p->str == NULL)
        return; /* An error already occurred */
    size = PyBytes_Size(p->str);
    newsize = size + size + 1024;
    if (newsize > 32*1024*1024) {
        newsize = size + (size >> 3);           /* 12.5% overallocation */
    }
    if (_PyBytes_Resize(&p->str, newsize) != 0) {
        p->ptr = p->end = NULL;
    }
    else {
        p->ptr = PyBytes_AS_STRING((PyBytesObject *)p->str) + size;
        p->end =
            PyBytes_AS_STRING((PyBytesObject *)p->str) + newsize;
        *p->ptr++ = Py_SAFE_DOWNCAST(c, int, char);
    }
}

static void
w_string(char *s, int n, WFILE *p)
{
    if (p->fp != NULL) {
        fwrite(s, 1, n, p->fp);
    }
    else {
        while (--n >= 0) {
            w_byte(*s, p);
            s++;
        }
    }
}

static void
w_short(int x, WFILE *p)
{
    w_byte((char)( x      & 0xff), p);
    w_byte((char)((x>> 8) & 0xff), p);
}

static void
w_long(long x, WFILE *p)
{
    w_byte((char)( x      & 0xff), p);
    w_byte((char)((x>> 8) & 0xff), p);
    w_byte((char)((x>>16) & 0xff), p);
    w_byte((char)((x>>24) & 0xff), p);
}

#if SIZEOF_LONG > 4
static void
w_long64(long x, WFILE *p)
{
    w_long(x, p);
    w_long(x>>32, p);
}
#endif

/* We assume that Python longs are stored internally in base some power of
   2**15; for the sake of portability we'll always read and write them in base
   exactly 2**15. */

#define PyLong_MARSHAL_SHIFT 15
#define PyLong_MARSHAL_BASE ((short)1 << PyLong_MARSHAL_SHIFT)
#define PyLong_MARSHAL_MASK (PyLong_MARSHAL_BASE - 1)
#if PyLong_SHIFT % PyLong_MARSHAL_SHIFT != 0
#error "PyLong_SHIFT must be a multiple of PyLong_MARSHAL_SHIFT"
#endif
#define PyLong_MARSHAL_RATIO (PyLong_SHIFT / PyLong_MARSHAL_SHIFT)

static void
w_PyLong(const PyLongObject *ob, WFILE *p)
{
    Py_ssize_t i, j, n, l;
    digit d;

    w_byte(TYPE_LONG, p);
    if (Py_SIZE(ob) == 0) {
        w_long((long)0, p);
        return;
    }

    /* set l to number of base PyLong_MARSHAL_BASE digits */
    n = ABS(Py_SIZE(ob));
    l = (n-1) * PyLong_MARSHAL_RATIO;
    d = ob->ob_digit[n-1];
    assert(d != 0); /* a PyLong is always normalized */
    do {
        d >>= PyLong_MARSHAL_SHIFT;
        l++;
    } while (d != 0);
    w_long((long)(Py_SIZE(ob) > 0 ? l : -l), p);

    for (i=0; i < n-1; i++) {
        d = ob->ob_digit[i];
        for (j=0; j < PyLong_MARSHAL_RATIO; j++) {
            w_short(d & PyLong_MARSHAL_MASK, p);
            d >>= PyLong_MARSHAL_SHIFT;
        }
        assert (d == 0);
    }
    d = ob->ob_digit[n-1];
    do {
        w_short(d & PyLong_MARSHAL_MASK, p);
        d >>= PyLong_MARSHAL_SHIFT;
    } while (d != 0);
}

static void
w_object(PyObject *v, WFILE *p)
{
    Py_ssize_t i, n;

    p->depth++;

    if (p->depth > MAX_MARSHAL_STACK_DEPTH) {
        p->error = WFERR_NESTEDTOODEEP;
    }
    else if (v == NULL) {
        w_byte(TYPE_NULL, p);
    }
    else if (v == Py_None) {
        w_byte(TYPE_NONE, p);
    }
    else if (v == PyExc_StopIteration) {
        w_byte(TYPE_STOPITER, p);
    }
    else if (v == Py_Ellipsis) {
        w_byte(TYPE_ELLIPSIS, p);
    }
    else if (v == Py_False) {
        w_byte(TYPE_FALSE, p);
    }
    else if (v == Py_True) {
        w_byte(TYPE_TRUE, p);
    }
    else if (PyLong_CheckExact(v)) {
        long x = PyLong_AsLong(v);
        if ((x == -1)  && PyErr_Occurred()) {
            PyLongObject *ob = (PyLongObject *)v;
            PyErr_Clear();
            w_PyLong(ob, p);
        }
        else {
#if SIZEOF_LONG > 4
            long y = Py_ARITHMETIC_RIGHT_SHIFT(long, x, 31);
            if (y && y != -1) {
                w_byte(TYPE_INT64, p);
                w_long64(x, p);
            }
            else
#endif
            {
                w_byte(TYPE_INT, p);
                w_long(x, p);
            }
        }
    }
    else if (PyFloat_CheckExact(v)) {
        if (p->version > 1) {
            unsigned char buf[8];
            if (_PyFloat_Pack8(PyFloat_AsDouble(v),
                               buf, 1) < 0) {
                p->error = WFERR_UNMARSHALLABLE;
                return;
            }
            w_byte(TYPE_BINARY_FLOAT, p);
            w_string((char*)buf, 8, p);
        }
        else {
            char *buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v),
                                              'g', 17, 0, NULL);
            if (!buf) {
                p->error = WFERR_NOMEMORY;
                return;
            }
            n = strlen(buf);
            w_byte(TYPE_FLOAT, p);
            w_byte((int)n, p);
            w_string(buf, (int)n, p);
            PyMem_Free(buf);
        }
    }
    else if (PyComplex_CheckExact(v)) {
        if (p->version > 1) {
            unsigned char buf[8];
            if (_PyFloat_Pack8(PyComplex_RealAsDouble(v),
                               buf, 1) < 0) {
                p->error = WFERR_UNMARSHALLABLE;
                return;
            }
            w_byte(TYPE_BINARY_COMPLEX, p);
            w_string((char*)buf, 8, p);
            if (_PyFloat_Pack8(PyComplex_ImagAsDouble(v),
                               buf, 1) < 0) {
                p->error = WFERR_UNMARSHALLABLE;
                return;
            }
            w_string((char*)buf, 8, p);
        }
        else {
            char *buf;
            w_byte(TYPE_COMPLEX, p);
            buf = PyOS_double_to_string(PyComplex_RealAsDouble(v),
                                        'g', 17, 0, NULL);
            if (!buf) {
                p->error = WFERR_NOMEMORY;
                return;
            }
            n = strlen(buf);
            w_byte((int)n, p);
            w_string(buf, (int)n, p);
            PyMem_Free(buf);
            buf = PyOS_double_to_string(PyComplex_ImagAsDouble(v),
                                        'g', 17, 0, NULL);
            if (!buf) {
                p->error = WFERR_NOMEMORY;
                return;
            }
            n = strlen(buf);
            w_byte((int)n, p);
            w_string(buf, (int)n, p);
            PyMem_Free(buf);
        }
    }
    else if (PyBytes_CheckExact(v)) {
        w_byte(TYPE_STRING, p);
        n = PyBytes_GET_SIZE(v);
        if (n > INT_MAX) {
            /* huge strings are not supported */
            p->depth--;
            p->error = WFERR_UNMARSHALLABLE;
            return;
        }
        w_long((long)n, p);
        w_string(PyBytes_AS_STRING(v), (int)n, p);
    }
    else if (PyUnicode_CheckExact(v)) {
        PyObject *utf8;
        utf8 = PyUnicode_EncodeUTF8(PyUnicode_AS_UNICODE(v),
                                    PyUnicode_GET_SIZE(v),
                                    "surrogatepass");
        if (utf8 == NULL) {
            p->depth--;
            p->error = WFERR_UNMARSHALLABLE;
            return;
        }
        w_byte(TYPE_UNICODE, p);
        n = PyBytes_GET_SIZE(utf8);
        if (n > INT_MAX) {
            p->depth--;
            p->error = WFERR_UNMARSHALLABLE;
            return;
        }
        w_long((long)n, p);
        w_string(PyBytes_AS_STRING(utf8), (int)n, p);
        Py_DECREF(utf8);
    }
    else if (PyTuple_CheckExact(v)) {
        w_byte(TYPE_TUPLE, p);
        n = PyTuple_Size(v);
        w_long((long)n, p);
        for (i = 0; i < n; i++) {
            w_object(PyTuple_GET_ITEM(v, i), p);
        }
    }
    else if (PyList_CheckExact(v)) {
        w_byte(TYPE_LIST, p);
        n = PyList_GET_SIZE(v);
        w_long((long)n, p);
        for (i = 0; i < n; i++) {
            w_object(PyList_GET_ITEM(v, i), p);
        }
    }
    else if (PyDict_CheckExact(v)) {
        Py_ssize_t pos;
        PyObject *key, *value;
        w_byte(TYPE_DICT, p);
        /* This one is NULL object terminated! */
        pos = 0;
        while (PyDict_Next(v, &pos, &key, &value)) {
            w_object(key, p);
            w_object(value, p);
        }
        w_object((PyObject *)NULL, p);
    }
    else if (PyAnySet_CheckExact(v)) {
        PyObject *value, *it;

        if (PyObject_TypeCheck(v, &PySet_Type))
            w_byte(TYPE_SET, p);
        else
            w_byte(TYPE_FROZENSET, p);
        n = PyObject_Size(v);
        if (n == -1) {
            p->depth--;
            p->error = WFERR_UNMARSHALLABLE;
            return;
        }
        w_long((long)n, p);
        it = PyObject_GetIter(v);
        if (it == NULL) {
            p->depth--;
            p->error = WFERR_UNMARSHALLABLE;
            return;
        }
        while ((value = PyIter_Next(it)) != NULL) {
            w_object(value, p);
            Py_DECREF(value);
        }
        Py_DECREF(it);
        if (PyErr_Occurred()) {
            p->depth--;
            p->error = WFERR_UNMARSHALLABLE;
            return;
        }
    }
    else if (PyCode_Check(v)) {
        PyCodeObject *co = (PyCodeObject *)v;
        w_byte(TYPE_CODE, p);
        w_long(co->co_argcount, p);
        w_long(co->co_kwonlyargcount, p);
        w_long(co->co_nlocals, p);
        w_long(co->co_stacksize, p);
        w_long(co->co_flags, p);
        w_object(co->co_code, p);
        w_object(co->co_consts, p);
        w_object(co->co_names, p);
        w_object(co->co_varnames, p);
        w_object(co->co_freevars, p);
        w_object(co->co_cellvars, p);
        w_object(co->co_filename, p);
        w_object(co->co_name, p);
        w_long(co->co_firstlineno, p);
        w_object(co->co_lnotab, p);
    }
    else if (PyObject_CheckBuffer(v)) {
        /* Write unknown buffer-style objects as a string */
        char *s;
        PyBufferProcs *pb = v->ob_type->tp_as_buffer;
        Py_buffer view;
        if ((*pb->bf_getbuffer)(v, &view, PyBUF_SIMPLE) != 0) {
            w_byte(TYPE_UNKNOWN, p);
            p->error = WFERR_UNMARSHALLABLE;
        }
        w_byte(TYPE_STRING, p);
        n = view.len;
        s = view.buf;
        if (n > INT_MAX) {
            p->depth--;
            p->error = WFERR_UNMARSHALLABLE;
            return;
        }
        w_long((long)n, p);
        w_string(s, (int)n, p);
        if (pb->bf_releasebuffer != NULL)
            (*pb->bf_releasebuffer)(v, &view);
    }
    else {
        w_byte(TYPE_UNKNOWN, p);
        p->error = WFERR_UNMARSHALLABLE;
    }
    p->depth--;
}

/* version currently has no effect for writing longs. */
void
PyMarshal_WriteLongToFile(long x, FILE *fp, int version)
{
    WFILE wf;
    wf.fp = fp;
    wf.error = WFERR_OK;
    wf.depth = 0;
    wf.strings = NULL;
    wf.version = version;
    w_long(x, &wf);
}

void
PyMarshal_WriteObjectToFile(PyObject *x, FILE *fp, int version)
{
    WFILE wf;
    wf.fp = fp;
    wf.error = WFERR_OK;
    wf.depth = 0;
    wf.strings = (version > 0) ? PyDict_New() : NULL;
    wf.version = version;
    w_object(x, &wf);
    Py_XDECREF(wf.strings);
}

typedef WFILE RFILE; /* Same struct with different invariants */

#define rs_byte(p) (((p)->ptr < (p)->end) ? (unsigned char)*(p)->ptr++ : EOF)

#define r_byte(p) ((p)->fp ? getc((p)->fp) : rs_byte(p))

static int
r_string(char *s, int n, RFILE *p)
{
    if (p->fp != NULL)
        /* The result fits into int because it must be <=n. */
        return (int)fread(s, 1, n, p->fp);
    if (p->end - p->ptr < n)
        n = (int)(p->end - p->ptr);
    memcpy(s, p->ptr, n);
    p->ptr += n;
    return n;
}

static int
r_short(RFILE *p)
{
    register short x;
    x = r_byte(p);
    x |= r_byte(p) << 8;
    /* Sign-extension, in case short greater than 16 bits */
    x |= -(x & 0x8000);
    return x;
}

static long
r_long(RFILE *p)
{
    register long x;
    register FILE *fp = p->fp;
    if (fp) {
        x = getc(fp);
        x |= (long)getc(fp) << 8;
        x |= (long)getc(fp) << 16;
        x |= (long)getc(fp) << 24;
    }
    else {
        x = rs_byte(p);
        x |= (long)rs_byte(p) << 8;
        x |= (long)rs_byte(p) << 16;
        x |= (long)rs_byte(p) << 24;
    }
#if SIZEOF_LONG > 4
    /* Sign extension for 64-bit machines */
    x |= -(x & 0x80000000L);
#endif
    return x;
}

/* r_long64 deals with the TYPE_INT64 code.  On a machine with
   sizeof(long) > 4, it returns a Python int object, else a Python long
   object.  Note that w_long64 writes out TYPE_INT if 32 bits is enough,
   so there's no inefficiency here in returning a PyLong on 32-bit boxes
   for everything written via TYPE_INT64 (i.e., if an int is written via
   TYPE_INT64, it *needs* more than 32 bits).
*/
static PyObject *
r_long64(RFILE *p)
{
    long lo4 = r_long(p);
    long hi4 = r_long(p);
#if SIZEOF_LONG > 4
    long x = (hi4 << 32) | (lo4 & 0xFFFFFFFFL);
    return PyLong_FromLong(x);
#else
    unsigned char buf[8];
    int one = 1;
    int is_little_endian = (int)*(char*)&one;
    if (is_little_endian) {
        memcpy(buf, &lo4, 4);
        memcpy(buf+4, &hi4, 4);
    }
    else {
        memcpy(buf, &hi4, 4);
        memcpy(buf+4, &lo4, 4);
    }
    return _PyLong_FromByteArray(buf, 8, is_little_endian, 1);
#endif
}

static PyObject *
r_PyLong(RFILE *p)
{
    PyLongObject *ob;
    int size, i, j, md, shorts_in_top_digit;
    long n;
    digit d;

    n = r_long(p);
    if (n == 0)
        return (PyObject *)_PyLong_New(0);
    if (n < -INT_MAX || n > INT_MAX) {
        PyErr_SetString(PyExc_ValueError,
                       "bad marshal data (long size out of range)");
        return NULL;
    }

    size = 1 + (ABS(n) - 1) / PyLong_MARSHAL_RATIO;
    shorts_in_top_digit = 1 + (ABS(n) - 1) % PyLong_MARSHAL_RATIO;
    ob = _PyLong_New(size);
    if (ob == NULL)
        return NULL;
    Py_SIZE(ob) = n > 0 ? size : -size;

    for (i = 0; i < size-1; i++) {
        d = 0;
        for (j=0; j < PyLong_MARSHAL_RATIO; j++) {
            md = r_short(p);
            if (md < 0 || md > PyLong_MARSHAL_BASE)
                goto bad_digit;
            d += (digit)md << j*PyLong_MARSHAL_SHIFT;
        }
        ob->ob_digit[i] = d;
    }
    d = 0;
    for (j=0; j < shorts_in_top_digit; j++) {
        md = r_short(p);
        if (md < 0 || md > PyLong_MARSHAL_BASE)
            goto bad_digit;
        /* topmost marshal digit should be nonzero */
        if (md == 0 && j == shorts_in_top_digit - 1) {
            Py_DECREF(ob);
            PyErr_SetString(PyExc_ValueError,
                "bad marshal data (unnormalized long data)");
            return NULL;
        }
        d += (digit)md << j*PyLong_MARSHAL_SHIFT;
    }
    /* top digit should be nonzero, else the resulting PyLong won't be
       normalized */
    ob->ob_digit[size-1] = d;
    return (PyObject *)ob;
  bad_digit:
    Py_DECREF(ob);
    PyErr_SetString(PyExc_ValueError,
                    "bad marshal data (digit out of range in long)");
    return NULL;
}


static PyObject *
r_object(RFILE *p)
{
    /* NULL is a valid return value, it does not necessarily means that
       an exception is set. */
    PyObject *v, *v2;
    long i, n;
    int type = r_byte(p);
    PyObject *retval;

    p->depth++;

    if (p->depth > MAX_MARSHAL_STACK_DEPTH) {
        p->depth--;
        PyErr_SetString(PyExc_ValueError, "recursion limit exceeded");
        return NULL;
    }

    switch (type) {

    case EOF:
        PyErr_SetString(PyExc_EOFError,
                        "EOF read where object expected");
        retval = NULL;
        break;

    case TYPE_NULL:
        retval = NULL;
        break;

    case TYPE_NONE:
        Py_INCREF(Py_None);
        retval = Py_None;
        break;

    case TYPE_STOPITER:
        Py_INCREF(PyExc_StopIteration);
        retval = PyExc_StopIteration;
        break;

    case TYPE_ELLIPSIS:
        Py_INCREF(Py_Ellipsis);
        retval = Py_Ellipsis;
        break;

    case TYPE_FALSE:
        Py_INCREF(Py_False);
        retval = Py_False;
        break;

    case TYPE_TRUE:
        Py_INCREF(Py_True);
        retval = Py_True;
        break;

    case TYPE_INT:
        retval = PyLong_FromLong(r_long(p));
        break;

    case TYPE_INT64:
        retval = r_long64(p);
        break;

    case TYPE_LONG:
        retval = r_PyLong(p);
        break;

    case TYPE_FLOAT:
        {
            char buf[256];
            double dx;
            retval = NULL;
            n = r_byte(p);
            if (n == EOF || r_string(buf, (int)n, p) != n) {
                PyErr_SetString(PyExc_EOFError,
                    "EOF read where object expected");
                break;
            }
            buf[n] = '\0';
            dx = PyOS_string_to_double(buf, NULL, NULL);
            if (dx == -1.0 && PyErr_Occurred())
                break;
            retval = PyFloat_FromDouble(dx);
            break;
        }

    case TYPE_BINARY_FLOAT:
        {
            unsigned char buf[8];
            double x;
            if (r_string((char*)buf, 8, p) != 8) {
                PyErr_SetString(PyExc_EOFError,
                    "EOF read where object expected");
                retval = NULL;
                break;
            }
            x = _PyFloat_Unpack8(buf, 1);
            if (x == -1.0 && PyErr_Occurred()) {
                retval = NULL;
                break;
            }
            retval = PyFloat_FromDouble(x);
            break;
        }

    case TYPE_COMPLEX:
        {
            char buf[256];
            Py_complex c;
            retval = NULL;
            n = r_byte(p);
            if (n == EOF || r_string(buf, (int)n, p) != n) {
                PyErr_SetString(PyExc_EOFError,
                    "EOF read where object expected");
                break;
            }
            buf[n] = '\0';
            c.real = PyOS_string_to_double(buf, NULL, NULL);
            if (c.real == -1.0 && PyErr_Occurred())
                break;
            n = r_byte(p);
            if (n == EOF || r_string(buf, (int)n, p) != n) {
                PyErr_SetString(PyExc_EOFError,
                    "EOF read where object expected");
                break;
            }
            buf[n] = '\0';
            c.imag = PyOS_string_to_double(buf, NULL, NULL);
            if (c.imag == -1.0 && PyErr_Occurred())
                break;
            retval = PyComplex_FromCComplex(c);
            break;
        }

    case TYPE_BINARY_COMPLEX:
        {
            unsigned char buf[8];
            Py_complex c;
            if (r_string((char*)buf, 8, p) != 8) {
                PyErr_SetString(PyExc_EOFError,
                    "EOF read where object expected");
                retval = NULL;
                break;
            }
            c.real = _PyFloat_Unpack8(buf, 1);
            if (c.real == -1.0 && PyErr_Occurred()) {
                retval = NULL;
                break;
            }
            if (r_string((char*)buf, 8, p) != 8) {
                PyErr_SetString(PyExc_EOFError,
                    "EOF read where object expected");
                retval = NULL;
                break;
            }
            c.imag = _PyFloat_Unpack8(buf, 1);
            if (c.imag == -1.0 && PyErr_Occurred()) {
                retval = NULL;
                break;
            }
            retval = PyComplex_FromCComplex(c);
            break;
        }

    case TYPE_STRING:
        n = r_long(p);
        if (n < 0 || n > INT_MAX) {
            PyErr_SetString(PyExc_ValueError, "bad marshal data (string size out of range)");
            retval = NULL;
            break;
        }
        v = PyBytes_FromStringAndSize((char *)NULL, n);
        if (v == NULL) {
            retval = NULL;
            break;
        }
        if (r_string(PyBytes_AS_STRING(v), (int)n, p) != n) {
            Py_DECREF(v);
            PyErr_SetString(PyExc_EOFError,
                            "EOF read where object expected");
            retval = NULL;
            break;
        }
        retval = v;
        break;

    case TYPE_UNICODE:
        {
        char *buffer;

        n = r_long(p);
        if (n < 0 || n > INT_MAX) {
            PyErr_SetString(PyExc_ValueError, "bad marshal data (unicode size out of range)");
            retval = NULL;
            break;
        }
        buffer = PyMem_NEW(char, n);
        if (buffer == NULL) {
            retval = PyErr_NoMemory();
            break;
        }
        if (r_string(buffer, (int)n, p) != n) {
            PyMem_DEL(buffer);
            PyErr_SetString(PyExc_EOFError,
                "EOF read where object expected");
            retval = NULL;
            break;
        }
        v = PyUnicode_DecodeUTF8(buffer, n, "surrogatepass");
        PyMem_DEL(buffer);
        retval = v;
        break;
        }

    case TYPE_TUPLE:
        n = r_long(p);
        if (n < 0 || n > INT_MAX) {
            PyErr_SetString(PyExc_ValueError, "bad marshal data (tuple size out of range)");
            retval = NULL;
            break;
        }
        v = PyTuple_New((int)n);
        if (v == NULL) {
            retval = NULL;
            break;
        }
        for (i = 0; i < n; i++) {
            v2 = r_object(p);
            if ( v2 == NULL ) {
                if (!PyErr_Occurred())
                    PyErr_SetString(PyExc_TypeError,
                        "NULL object in marshal data for tuple");
                Py_DECREF(v);
                v = NULL;
                break;
            }
            PyTuple_SET_ITEM(v, (int)i, v2);
        }
        retval = v;
        break;

    case TYPE_LIST:
        n = r_long(p);
        if (n < 0 || n > INT_MAX) {
            PyErr_SetString(PyExc_ValueError, "bad marshal data (list size out of range)");
            retval = NULL;
            break;
        }
        v = PyList_New((int)n);
        if (v == NULL) {
            retval = NULL;
            break;
        }
        for (i = 0; i < n; i++) {
            v2 = r_object(p);
            if ( v2 == NULL ) {
                if (!PyErr_Occurred())
                    PyErr_SetString(PyExc_TypeError,
                        "NULL object in marshal data for list");
                Py_DECREF(v);
                v = NULL;
                break;
            }
            PyList_SET_ITEM(v, (int)i, v2);
        }
        retval = v;
        break;

    case TYPE_DICT:
        v = PyDict_New();
        if (v == NULL) {
            retval = NULL;
            break;
        }
        for (;;) {
            PyObject *key, *val;
            key = r_object(p);
            if (key == NULL)
                break;
            val = r_object(p);
            if (val != NULL)
                PyDict_SetItem(v, key, val);
            Py_DECREF(key);
            Py_XDECREF(val);
        }
        if (PyErr_Occurred()) {
            Py_DECREF(v);
            v = NULL;
        }
        retval = v;
        break;

    case TYPE_SET:
    case TYPE_FROZENSET:
        n = r_long(p);
        if (n < 0 || n > INT_MAX) {
            PyErr_SetString(PyExc_ValueError, "bad marshal data (set size out of range)");
            retval = NULL;
            break;
        }
        v = (type == TYPE_SET) ? PySet_New(NULL) : PyFrozenSet_New(NULL);
        if (v == NULL) {
            retval = NULL;
            break;
        }
        for (i = 0; i < n; i++) {
            v2 = r_object(p);
            if ( v2 == NULL ) {
                if (!PyErr_Occurred())
                    PyErr_SetString(PyExc_TypeError,
                        "NULL object in marshal data for set");
                Py_DECREF(v);
                v = NULL;
                break;
            }
            if (PySet_Add(v, v2) == -1) {
                Py_DECREF(v);
                Py_DECREF(v2);
                v = NULL;
                break;
            }
            Py_DECREF(v2);
        }
        retval = v;
        break;

    case TYPE_CODE:
        {
            int argcount;
            int kwonlyargcount;
            int nlocals;
            int stacksize;
            int flags;
            PyObject *code = NULL;
            PyObject *consts = NULL;
            PyObject *names = NULL;
            PyObject *varnames = NULL;
            PyObject *freevars = NULL;
            PyObject *cellvars = NULL;
            PyObject *filename = NULL;
            PyObject *name = NULL;
            int firstlineno;
            PyObject *lnotab = NULL;

            v = NULL;

            /* XXX ignore long->int overflows for now */
            argcount = (int)r_long(p);
            kwonlyargcount = (int)r_long(p);
            nlocals = (int)r_long(p);
            stacksize = (int)r_long(p);
            flags = (int)r_long(p);
            code = r_object(p);
            if (code == NULL)
                goto code_error;
            consts = r_object(p);
            if (consts == NULL)
                goto code_error;
            names = r_object(p);
            if (names == NULL)
                goto code_error;
            varnames = r_object(p);
            if (varnames == NULL)
                goto code_error;
            freevars = r_object(p);
            if (freevars == NULL)
                goto code_error;
            cellvars = r_object(p);
            if (cellvars == NULL)
                goto code_error;
            filename = r_object(p);
            if (filename == NULL)
                goto code_error;
            name = r_object(p);
            if (name == NULL)
                goto code_error;
            firstlineno = (int)r_long(p);
            lnotab = r_object(p);
            if (lnotab == NULL)
                goto code_error;

            v = (PyObject *) PyCode_New(
                            argcount, kwonlyargcount,
                            nlocals, stacksize, flags,
                            code, consts, names, varnames,
                            freevars, cellvars, filename, name,
                            firstlineno, lnotab);

          code_error:
            Py_XDECREF(code);
            Py_XDECREF(consts);
            Py_XDECREF(names);
            Py_XDECREF(varnames);
            Py_XDECREF(freevars);
            Py_XDECREF(cellvars);
            Py_XDECREF(filename);
            Py_XDECREF(name);
            Py_XDECREF(lnotab);
        }
        retval = v;
        break;

    default:
        /* Bogus data got written, which isn't ideal.
           This will let you keep working and recover. */
        PyErr_SetString(PyExc_ValueError, "bad marshal data (unknown type code)");
        retval = NULL;
        break;

    }
    p->depth--;
    return retval;
}

static PyObject *
read_object(RFILE *p)
{
    PyObject *v;
    if (PyErr_Occurred()) {
        fprintf(stderr, "XXX readobject called with exception set\n");
        return NULL;
    }
    v = r_object(p);
    if (v == NULL && !PyErr_Occurred())
        PyErr_SetString(PyExc_TypeError, "NULL object in marshal data for object");
    return v;
}

int
PyMarshal_ReadShortFromFile(FILE *fp)
{
    RFILE rf;
    assert(fp);
    rf.fp = fp;
    rf.strings = NULL;
    rf.end = rf.ptr = NULL;
    return r_short(&rf);
}

long
PyMarshal_ReadLongFromFile(FILE *fp)
{
    RFILE rf;
    rf.fp = fp;
    rf.strings = NULL;
    rf.ptr = rf.end = NULL;
    return r_long(&rf);
}

#ifdef HAVE_FSTAT
/* Return size of file in bytes; < 0 if unknown. */
static off_t
getfilesize(FILE *fp)
{
    struct stat st;
    if (fstat(fileno(fp), &st) != 0)
        return -1;
    else
        return st.st_size;
}
#endif

/* If we can get the size of the file up-front, and it's reasonably small,
 * read it in one gulp and delegate to ...FromString() instead.  Much quicker
 * than reading a byte at a time from file; speeds .pyc imports.
 * CAUTION:  since this may read the entire remainder of the file, don't
 * call it unless you know you're done with the file.
 */
PyObject *
PyMarshal_ReadLastObjectFromFile(FILE *fp)
{
/* REASONABLE_FILE_LIMIT is by defn something big enough for Tkinter.pyc. */
#define REASONABLE_FILE_LIMIT (1L << 18)
#ifdef HAVE_FSTAT
    off_t filesize;
    filesize = getfilesize(fp);
    if (filesize > 0 && filesize <= REASONABLE_FILE_LIMIT) {
        char* pBuf = (char *)PyMem_MALLOC(filesize);
        if (pBuf != NULL) {
            PyObject* v;
            size_t n;
            /* filesize must fit into an int, because it
               is smaller than REASONABLE_FILE_LIMIT */
            n = fread(pBuf, 1, (int)filesize, fp);
            v = PyMarshal_ReadObjectFromString(pBuf, n);
            PyMem_FREE(pBuf);
            return v;
        }

    }
#endif
    /* We don't have fstat, or we do but the file is larger than
     * REASONABLE_FILE_LIMIT or malloc failed -- read a byte at a time.
     */
    return PyMarshal_ReadObjectFromFile(fp);

#undef REASONABLE_FILE_LIMIT
}

PyObject *
PyMarshal_ReadObjectFromFile(FILE *fp)
{
    RFILE rf;
    PyObject *result;
    rf.fp = fp;
    rf.strings = PyList_New(0);
    rf.depth = 0;
    rf.ptr = rf.end = NULL;
    result = r_object(&rf);
    Py_DECREF(rf.strings);
    return result;
}

PyObject *
PyMarshal_ReadObjectFromString(char *str, Py_ssize_t len)
{
    RFILE rf;
    PyObject *result;
    rf.fp = NULL;
    rf.ptr = str;
    rf.end = str + len;
    rf.strings = PyList_New(0);
    rf.depth = 0;
    result = r_object(&rf);
    Py_DECREF(rf.strings);
    return result;
}

PyObject *
PyMarshal_WriteObjectToString(PyObject *x, int version)
{
    WFILE wf;
    PyObject *res = NULL;

    wf.fp = NULL;
    wf.str = PyBytes_FromStringAndSize((char *)NULL, 50);
    if (wf.str == NULL)
        return NULL;
    wf.ptr = PyBytes_AS_STRING((PyBytesObject *)wf.str);
    wf.end = wf.ptr + PyBytes_Size(wf.str);
    wf.error = WFERR_OK;
    wf.depth = 0;
    wf.version = version;
    wf.strings = (version > 0) ? PyDict_New() : NULL;
    w_object(x, &wf);
    Py_XDECREF(wf.strings);
    if (wf.str != NULL) {
        char *base = PyBytes_AS_STRING((PyBytesObject *)wf.str);
        if (wf.ptr - base > PY_SSIZE_T_MAX) {
            Py_DECREF(wf.str);
            PyErr_SetString(PyExc_OverflowError,
                            "too much marshal data for a string");
            return NULL;
        }
        if (_PyBytes_Resize(&wf.str, (Py_ssize_t)(wf.ptr - base)) < 0)
            return NULL;
    }
    if (wf.error != WFERR_OK) {
        Py_XDECREF(wf.str);
        if (wf.error == WFERR_NOMEMORY)
            PyErr_NoMemory();
        else
            PyErr_SetString(PyExc_ValueError,
              (wf.error==WFERR_UNMARSHALLABLE)?"unmarshallable object"
               :"object too deeply nested to marshal");
        return NULL;
    }
    if (wf.str != NULL) {
        /* XXX Quick hack -- need to do this differently */
        res = PyBytes_FromObject(wf.str);
        Py_DECREF(wf.str);
    }
    return res;
}

/* And an interface for Python programs... */

static PyObject *
marshal_dump(PyObject *self, PyObject *args)
{
    /* XXX Quick hack -- need to do this differently */
    PyObject *x;
    PyObject *f;
    int version = Py_MARSHAL_VERSION;
    PyObject *s;
    PyObject *res;
    if (!PyArg_ParseTuple(args, "OO|i:dump", &x, &f, &version))
        return NULL;
    s = PyMarshal_WriteObjectToString(x, version);
    if (s == NULL)
        return NULL;
    res = PyObject_CallMethod(f, "write", "O", s);
    Py_DECREF(s);
    return res;
}

PyDoc_STRVAR(dump_doc,
"dump(value, file[, version])\n\
\n\
Write the value on the open file. The value must be a supported type.\n\
The file must be an open file object such as sys.stdout or returned by\n\
open() or os.popen(). It must be opened in binary mode ('wb' or 'w+b').\n\
\n\
If the value has (or contains an object that has) an unsupported type, a\n\
ValueError exception is raised — but garbage data will also be written\n\
to the file. The object will not be properly read back by load()\n\
\n\
The version argument indicates the data format that dump should use.");

static PyObject *
marshal_load(PyObject *self, PyObject *f)
{
    /* XXX Quick hack -- need to do this differently */
    PyObject *data, *result;
    RFILE rf;
    data = PyObject_CallMethod(f, "read", "");
    if (data == NULL)
        return NULL;
    rf.fp = NULL;
    if (PyBytes_Check(data)) {
        rf.ptr = PyBytes_AS_STRING(data);
        rf.end = rf.ptr + PyBytes_GET_SIZE(data);
    }
    else if (PyBytes_Check(data)) {
        rf.ptr = PyBytes_AS_STRING(data);
        rf.end = rf.ptr + PyBytes_GET_SIZE(data);
    }
    else {
        PyErr_Format(PyExc_TypeError,
                     "f.read() returned neither string "
                     "nor bytes but %.100s",
                     data->ob_type->tp_name);
        Py_DECREF(data);
        return NULL;
    }
    rf.strings = PyList_New(0);
    rf.depth = 0;
    result = read_object(&rf);
    Py_DECREF(rf.strings);
    Py_DECREF(data);
    return result;
}

PyDoc_STRVAR(load_doc,
"load(file)\n\
\n\
Read one value from the open file and return it. If no valid value is\n\
read (e.g. because the data has a different Python version’s\n\
incompatible marshal format), raise EOFError, ValueError or TypeError.\n\
The file must be an open file object opened in binary mode ('rb' or\n\
'r+b').\n\
\n\
Note: If an object containing an unsupported type was marshalled with\n\
dump(), load() will substitute None for the unmarshallable type.");


static PyObject *
marshal_dumps(PyObject *self, PyObject *args)
{
    PyObject *x;
    int version = Py_MARSHAL_VERSION;
    if (!PyArg_ParseTuple(args, "O|i:dumps", &x, &version))
        return NULL;
    return PyMarshal_WriteObjectToString(x, version);
}

PyDoc_STRVAR(dumps_doc,
"dumps(value[, version])\n\
\n\
Return the string that would be written to a file by dump(value, file).\n\
The value must be a supported type. Raise a ValueError exception if\n\
value has (or contains an object that has) an unsupported type.\n\
\n\
The version argument indicates the data format that dumps should use.");


static PyObject *
marshal_loads(PyObject *self, PyObject *args)
{
    RFILE rf;
    Py_buffer p;
    char *s;
    Py_ssize_t n;
    PyObject* result;
    if (!PyArg_ParseTuple(args, "s*:loads", &p))
        return NULL;
    s = p.buf;
    n = p.len;
    rf.fp = NULL;
    rf.ptr = s;
    rf.end = s + n;
    rf.strings = PyList_New(0);
    rf.depth = 0;
    result = read_object(&rf);
    Py_DECREF(rf.strings);
    PyBuffer_Release(&p);
    return result;
}

PyDoc_STRVAR(loads_doc,
"loads(string)\n\
\n\
Convert the string to a value. If no valid value is found, raise\n\
EOFError, ValueError or TypeError. Extra characters in the string are\n\
ignored.");

static PyMethodDef marshal_methods[] = {
    {"dump",            marshal_dump,   METH_VARARGS,   dump_doc},
    {"load",            marshal_load,   METH_O,         load_doc},
    {"dumps",           marshal_dumps,  METH_VARARGS,   dumps_doc},
    {"loads",           marshal_loads,  METH_VARARGS,   loads_doc},
    {NULL,              NULL}           /* sentinel */
};


PyDoc_STRVAR(module_doc,
"This module contains functions that can read and write Python values in\n\
a binary format. The format is specific to Python, but independent of\n\
machine architecture issues.\n\
\n\
Not all Python object types are supported; in general, only objects\n\
whose value is independent from a particular invocation of Python can be\n\
written and read by this module. The following types are supported:\n\
None, integers, floating point numbers, strings, bytes, bytearrays,\n\
tuples, lists, sets, dictionaries, and code objects, where it\n\
should be understood that tuples, lists and dictionaries are only\n\
supported as long as the values contained therein are themselves\n\
supported; and recursive lists and dictionaries should not be written\n\
(they will cause infinite loops).\n\
\n\
Variables:\n\
\n\
version -- indicates the format that the module uses. Version 0 is the\n\
    historical format, version 1 shares interned strings and version 2\n\
    uses a binary format for floating point numbers.\n\
\n\
Functions:\n\
\n\
dump() -- write value to a file\n\
load() -- read value from a file\n\
dumps() -- write value to a string\n\
loads() -- read value from a string");



static struct PyModuleDef marshalmodule = {
    PyModuleDef_HEAD_INIT,
    "marshal",
    module_doc,
    0,
    marshal_methods,
    NULL,
    NULL,
    NULL,
    NULL
};

PyMODINIT_FUNC
PyMarshal_Init(void)
{
    PyObject *mod = PyModule_Create(&marshalmodule);
    if (mod == NULL)
        return NULL;
    PyModule_AddIntConstant(mod, "version", Py_MARSHAL_VERSION);
    return mod;
}