diff options
-rw-r--r-- | compilerplugins/clang/svstreamoutputoperators.cxx | 181 | ||||
-rw-r--r-- | include/tools/stream.hxx | 91 | ||||
-rw-r--r-- | tools/source/stream/stream.cxx | 107 |
3 files changed, 341 insertions, 38 deletions
diff --git a/compilerplugins/clang/svstreamoutputoperators.cxx b/compilerplugins/clang/svstreamoutputoperators.cxx new file mode 100644 index 000000000000..579ca6fda5e9 --- /dev/null +++ b/compilerplugins/clang/svstreamoutputoperators.cxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * Based on LLVM/Clang. + * + * This file is distributed under the University of Illinois Open Source + * License. See LICENSE.TXT for details. + * + */ + +/* +This is a rewriter. + +It changes the SvStream operator<< calls into calls to more explicitly named +methods, which reduces the casting needed, and makes it less likely that we +will accidentally write data to a file using the wrong data-type-size. + +TODO we don't currently cope with macro expansion e.g. if the constant on the RHS is a #define + +TODO we don't currently cope with code like "(*this) << 1;" + +TODO we don't currently cope with code like "aStream << x << endl;" the "endl" parts ends up dangling. + +TODO we don't currently cope with custom overloads of operator<< in some of the use-sites. +*/ + +#include "plugin.hxx" +#include <clang/Lex/Lexer.h> +#include <iostream> + +namespace loplugin +{ + +class SvStreamOutputOperators + : public RecursiveASTVisitor< SvStreamOutputOperators > + , public RewritePlugin +{ + public: + explicit SvStreamOutputOperators( CompilerInstance& compiler, Rewriter& rewriter ); + virtual void run() override; + bool VisitCallExpr( const CallExpr* call ); + private: + SourceLocation after(const SourceLocation& loc); +}; + +SvStreamOutputOperators::SvStreamOutputOperators( CompilerInstance& compiler, Rewriter& rewriter ) + : RewritePlugin( compiler, rewriter ) +{ +} + +void SvStreamOutputOperators::run() +{ + TraverseDecl( compiler.getASTContext().getTranslationUnitDecl()); +} + +bool SvStreamOutputOperators::VisitCallExpr( const CallExpr* callExpr ) +{ + if( ignoreLocation( callExpr )) + return true; + if( callExpr->getNumArgs() < 2 ) + return true; + const FunctionDecl* func = dyn_cast_or_null< FunctionDecl >( callExpr->getCalleeDecl() ); + if ( func == NULL ) + return true; + if( func->getNumParams() != 1 ) + return true; + string qualifiedName = func->getQualifiedNameAsString(); + if( qualifiedName != "SvStream::operator<<" ) + return true; + + string arg0 = func->getParamDecl( 0 )->getType().getAsString(); + string newWriteMethod; + if( arg0 == "sal_uInt16" ) + newWriteMethod = "WriteUInt16"; + else if( arg0 == "sal_uInt32" ) + newWriteMethod = "WriteUInt32"; + else if( arg0 == "sal_uInt64" ) + newWriteMethod = "WriteUInt64"; + else if( arg0 == "sal_Int16" ) + newWriteMethod = "WriteInt16"; + else if( arg0 == "sal_Int32" ) + newWriteMethod = "WriteInt32"; + else if( arg0 == "sal_Int64" ) + newWriteMethod = "WriteInt64"; + else if( arg0 == "sal_uInt8" ) + newWriteMethod = "WriteUInt8"; + else if( arg0 == "sal_Unicode" ) + newWriteMethod = "WriteUnicode"; + else if( arg0 == "rtl::OString" ) + newWriteMethod = "WriteOString"; + else if( arg0 == "bool" ) + newWriteMethod = "WriteBool"; + else if( arg0 == "signed char" ) + newWriteMethod = "WriteSChar"; + else if( arg0 == "char" ) + newWriteMethod = "WriteChar"; + else if( arg0 == "unsigned char" ) + newWriteMethod = "WriteUChar"; + else if( arg0 == "float" ) + newWriteMethod = "WriteFloat"; + else if( arg0 == "double" ) + newWriteMethod = "WriteDouble"; + else if( arg0 == "const double &" ) + newWriteMethod = "WriteDouble"; + else if( arg0 == "const char *" ) + newWriteMethod = "WriteCharPtr"; + else if( arg0 == "char *" ) + newWriteMethod = "WriteCharPtr"; + else if( arg0 == "const unsigned char *" ) + newWriteMethod = "WriteUCharPtr"; + else if( arg0 == "unsigned char *" ) + newWriteMethod = "WriteUCharPtr"; + else if( arg0 == "class SvStream &" ) + newWriteMethod = "WriteStream"; + else + { + report( DiagnosticsEngine::Warning, + "found call to operator<< that I cannot convert with type: " + arg0, + callExpr->getLocStart()); + return true; + } + + // CallExpr overrides the children() method from Stmt, but not the const variant of it, so we need to cast const away. + StmtRange range = const_cast<CallExpr*>(callExpr)->children(); + const Stmt* child1 = *range; // ImplicitCastExpr + ++range; + const Stmt* child2 = *range; // ImplicitCastExpr + + if( dyn_cast_or_null< UnaryOperator >( child2 ) != NULL ) + { + // remove the "*" before the stream variable + if( !replaceText( callExpr->getLocStart(), 1, "" ) ) + return true; + if( !replaceText( child1->getLocStart().getLocWithOffset(-1), 4, "->" ) ) + return true; + } + else + { + if( !replaceText( child1->getLocStart().getLocWithOffset(-1), 4, "." ) ) + return true; + } + + if( !insertTextBefore( callExpr->getArg( 1 )->getLocStart(), newWriteMethod + "( " ) ) + return true; + if( !insertTextAfter( after( callExpr->getLocEnd() ), " )" ) ) + return true; + +//TODO for some reason this is currently removing too much text + // if there was a cast e.g. "r << (sal_Int32) 1", then remove the cast +// const CStyleCastExpr* cast = dyn_cast_or_null< CStyleCastExpr >( callExpr->getArg(1) ); +// if (cast != NULL) +// { +// replaceText( SourceRange( cast->getLParenLoc(), cast->getRParenLoc() ), "" ); +// } + + // if there was already parentheses around the expression, remove them + const ParenExpr* paren = dyn_cast_or_null< ParenExpr >( callExpr->getArg(1) ); + if (paren != NULL) + { + if( !replaceText( paren->getLocStart(), 1, "" ) ) + return true; + if( !replaceText( paren->getLocEnd(), 1, "" ) ) + return true; + } + +// report( DiagnosticsEngine::Note, "found", callExpr->getLocStart()); + return true; +} + +SourceLocation SvStreamOutputOperators::after( const SourceLocation& loc ) +{ + return Lexer::getLocForEndOfToken( loc, 0, compiler.getASTContext().getSourceManager(), compiler.getASTContext().getLangOpts() ); +} + + +static Plugin::Registration< SvStreamOutputOperators > X( "svstreamoutputoperators" ); + +} // namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/tools/stream.hxx b/include/tools/stream.hxx index e390407a274e..7551a59d0dc0 100644 --- a/include/tools/stream.hxx +++ b/include/tools/stream.hxx @@ -315,24 +315,57 @@ public: SvStream& operator>>( double& rDouble ); SvStream& operator>>( SvStream& rStream ); - SvStream& operator<<( sal_uInt16 nUInt16 ); - SvStream& operator<<( sal_uInt32 nUInt32 ); - SvStream& operator<<( sal_uInt64 nuInt64 ); - SvStream& operator<<( sal_Int16 nInt16 ); - SvStream& operator<<( sal_Int32 nInt32 ); + SvStream& operator<<( sal_uInt16 nUInt16 ) + { return WriteUInt16(nUInt16); } + SvStream& operator<<( sal_uInt32 nUInt32 ) + { return WriteUInt32(nUInt32); } + SvStream& operator<<( sal_uInt64 nuInt64 ) + { return WriteUInt64(nuInt64); } + SvStream& operator<<( sal_Int16 nInt16 ) + { return WriteInt16(nInt16); } + SvStream& operator<<( sal_Int32 nInt32 ) + { return WriteInt32(nInt32); } SvStream& operator<<( sal_Int64 nInt64 ) SAL_DELETED_FUNCTION; - SvStream& WriteInt64(sal_Int64 nInt64); - SvStream& operator<<( bool b ) { return operator<<(static_cast< sal_Bool >(b)); } - SvStream& operator<<( signed char nChar ); - SvStream& operator<<( char nChar ); - SvStream& operator<<( unsigned char nChar ); - SvStream& operator<<( float nFloat ); - SvStream& operator<<( const double& rDouble ); - SvStream& operator<<( const char* pBuf ); - SvStream& operator<<( const unsigned char* pBuf ); - SvStream& operator<<( SvStream& rStream ); + SvStream& operator<<( signed char nChar ) + { return WriteSChar(nChar); } + SvStream& operator<<( char nChar ) + { return WriteChar(nChar); } + SvStream& operator<<( unsigned char nChar ) + { return WriteUChar(nChar); } + SvStream& operator<<( float nFloat ) + { return WriteFloat(nFloat); } + SvStream& operator<<( const double& rDouble ) + { return WriteDouble(rDouble); } + SvStream& operator<<( const char* pBuf ) + { return WriteCharPtr(pBuf); } + SvStream& operator<<( const unsigned char* pBuf ) + { return WriteUCharPtr(pBuf); } + SvStream& operator<<( SvStream& rStream ) + { return WriteStream(rStream); } + + SvStream& WriteUInt16( sal_uInt16 nUInt16 ); + SvStream& WriteUInt32( sal_uInt32 nUInt32 ); + SvStream& WriteUInt64( sal_uInt64 nuInt64 ); + SvStream& WriteInt16( sal_Int16 nInt16 ); + SvStream& WriteInt32( sal_Int32 nInt32 ); + SvStream& WriteInt64( sal_Int64 nInt64 ); + SvStream& WriteUInt8( sal_uInt8 nuInt8 ); + SvStream& WriteUnicode( sal_Unicode ); + SvStream& WriteOString(const OString& rStr) + { return WriteCharPtr(rStr.getStr()); } + SvStream& WriteStream( SvStream& rStream ); + + SvStream& WriteBool( bool b ) + { return WriteUChar(static_cast< sal_Bool >(b)); } + SvStream& WriteSChar( signed char nChar ); + SvStream& WriteChar( char nChar ); + SvStream& WriteUChar( unsigned char nChar ); + SvStream& WriteFloat( float nFloat ); + SvStream& WriteDouble( const double& rDouble ); + SvStream& WriteCharPtr( const char* pBuf ); + SvStream& WriteUCharPtr( const unsigned char* pBuf ); SvStream& WriteNumber( sal_uInt32 nUInt32 ); SvStream& WriteNumber( sal_Int32 nInt32 ); @@ -585,6 +618,18 @@ sal_Size write_lenPrefixed_uInt16s_FromOUString(SvStream& rStrm, return streamdetail::write_lenPrefixed_seq_From_str<prefix, OUString, write_uInt16s_FromOUString>(rStrm, rStr); } +/// Attempt to write a pascal-style length (of type prefix) prefixed sequence +/// of 16bit units from an OUString, returned value is number of bytes written +/// (including byte-count of prefix) +TOOLS_DLLPUBLIC sal_Size write_uInt32_lenPrefixed_uInt16s_FromOUString(SvStream& rStrm, + const OUString &rStr); + +/// Attempt to write a pascal-style length (of type prefix) prefixed sequence +/// of 16bit units from an OUString, returned value is number of bytes written +/// (including byte-count of prefix) +TOOLS_DLLPUBLIC sal_Size write_uInt16_lenPrefixed_uInt16s_FromOUString(SvStream& rStrm, + const OUString &rStr); + /// Attempt to read 8bit units to an OString until a zero terminator is /// encountered, returned OString's length is number of units *definitely* /// successfully read, check SvStream::good() to see if null terminator was @@ -650,6 +695,22 @@ sal_Size write_lenPrefixed_uInt8s_FromOUString(SvStream& rStrm, return write_lenPrefixed_uInt8s_FromOString<prefix>(rStrm, OUStringToOString(rStr, eEnc)); } +/// Attempt to write a pascal-style length (of type prefix) prefixed +/// sequence of units from a string-type, returned value is number of bytes +/// written (including byte-count of prefix) +TOOLS_DLLPUBLIC sal_Size write_uInt16_lenPrefixed_uInt8s_FromOString(SvStream& rStrm, + const OString &rStr); + +/// Attempt to write a pascal-style length (of type prefix) prefixed sequence +/// of 8bit units from an OUString, returned value is number of bytes written +/// (including byte-count of prefix) +TOOLS_DLLPUBLIC inline sal_Size write_uInt16_lenPrefixed_uInt8s_FromOUString(SvStream& rStrm, + const OUString &rStr, + rtl_TextEncoding eEnc) +{ + return write_uInt16_lenPrefixed_uInt8s_FromOString(rStrm, OUStringToOString(rStr, eEnc)); +} + // FileStream class TOOLS_DLLPUBLIC SvFileStream : public SvStream diff --git a/tools/source/stream/stream.cxx b/tools/source/stream/stream.cxx index 808008919398..f02b5a00e200 100644 --- a/tools/source/stream/stream.cxx +++ b/tools/source/stream/stream.cxx @@ -790,7 +790,7 @@ bool SvStream::WriteLine(const OString& rStr) bool SvStream::WriteUniOrByteChar( sal_Unicode ch, rtl_TextEncoding eDestCharSet ) { if ( eDestCharSet == RTL_TEXTENCODING_UNICODE ) - *this << ch; + WriteChar(ch); else { OString aStr(&ch, 1, eDestCharSet); @@ -805,7 +805,7 @@ bool SvStream::StartWritingUnicodeText() // BOM, Byte Order Mark, U+FEFF, see // http://www.unicode.org/faq/utf_bom.html#BOM // Upon read: 0xfeff(-257) => no swap; 0xfffe(-2) => swap - *this << sal_uInt16( 0xfeff ); + WriteUInt16( 0xfeff ); return nError == SVSTREAM_OK; } @@ -1055,7 +1055,7 @@ SvStream& SvStream::operator>> ( SvStream& rStream ) return *this; } -SvStream& SvStream::operator<< ( sal_uInt16 v ) +SvStream& SvStream::WriteUInt16( sal_uInt16 v ) { if( bSwap ) SwapUShort(v); @@ -1063,7 +1063,7 @@ SvStream& SvStream::operator<< ( sal_uInt16 v ) return *this; } -SvStream& SvStream::operator<< ( sal_uInt32 v ) +SvStream& SvStream::WriteUInt32( sal_uInt32 v ) { if( bSwap ) SwapULong(v); @@ -1071,7 +1071,7 @@ SvStream& SvStream::operator<< ( sal_uInt32 v ) return *this; } -SvStream& SvStream::operator<< ( sal_uInt64 v ) +SvStream& SvStream::WriteUInt64( sal_uInt64 v ) { if( bSwap ) SwapUInt64(v); @@ -1079,7 +1079,7 @@ SvStream& SvStream::operator<< ( sal_uInt64 v ) return *this; } -SvStream& SvStream::operator<< ( sal_Int16 v ) +SvStream& SvStream::WriteInt16( sal_Int16 v ) { if( bSwap ) SwapShort(v); @@ -1087,7 +1087,7 @@ SvStream& SvStream::operator<< ( sal_Int16 v ) return *this; } -SvStream& SvStream::operator<< ( sal_Int32 v ) +SvStream& SvStream::WriteInt32( sal_Int32 v ) { if( bSwap ) SwapLongInt(v); @@ -1103,7 +1103,7 @@ SvStream& SvStream::WriteInt64 (sal_Int64 v) return *this; } -SvStream& SvStream::operator<< ( signed char v ) +SvStream& SvStream::WriteSChar( signed char v ) { //SDO if(bIoWrite && sizeof(signed char) <= nBufFree ) @@ -1123,7 +1123,7 @@ SvStream& SvStream::operator<< ( signed char v ) // Special treatment for Chars due to PutBack -SvStream& SvStream::operator<< ( char v ) +SvStream& SvStream::WriteChar( char v ) { //SDO if(bIoWrite && sizeof(char) <= nBufFree ) @@ -1141,7 +1141,7 @@ SvStream& SvStream::operator<< ( char v ) return *this; } -SvStream& SvStream::operator<< ( unsigned char v ) +SvStream& SvStream::WriteUChar( unsigned char v ) { //SDO if(bIoWrite && sizeof(char) <= nBufFree ) @@ -1159,7 +1159,17 @@ SvStream& SvStream::operator<< ( unsigned char v ) return *this; } -SvStream& SvStream::operator<< ( float v ) +SvStream& SvStream::WriteUInt8( sal_uInt8 v ) +{ + return WriteUChar(v); +} + +SvStream& SvStream::WriteUnicode( sal_Unicode v ) +{ + return WriteUInt16(v); +} + +SvStream& SvStream::WriteFloat( float v ) { #ifdef UNX if( bSwap ) @@ -1169,7 +1179,7 @@ SvStream& SvStream::operator<< ( float v ) return *this; } -SvStream& SvStream::operator<< ( const double& r ) +SvStream& SvStream::WriteDouble ( const double& r ) { #if defined UNX if( bSwap ) @@ -1187,19 +1197,19 @@ SvStream& SvStream::operator<< ( const double& r ) return *this; } -SvStream& SvStream::operator<< ( const char* pBuf ) +SvStream& SvStream::WriteCharPtr( const char* pBuf ) { Write( pBuf, strlen( pBuf ) ); return *this; } -SvStream& SvStream::operator<< ( const unsigned char* pBuf ) +SvStream& SvStream::WriteUCharPtr( const unsigned char* pBuf ) { Write( (char*)pBuf, strlen( (char*)pBuf ) ); return *this; } -SvStream& SvStream::operator<< ( SvStream& rStream ) +SvStream& SvStream::WriteStream( SvStream& rStream ) { const sal_uInt32 cBufLen = 0x8000; char* pBuf = new char[ cBufLen ]; @@ -1225,9 +1235,9 @@ SvStream& SvStream::WriteUniOrByteString( const OUString& rStr, rtl_TextEncoding { // write UTF-16 string directly into stream ? if (eDestCharSet == RTL_TEXTENCODING_UNICODE) - write_lenPrefixed_uInt16s_FromOUString<sal_uInt32>(*this, rStr); + write_uInt32_lenPrefixed_uInt16s_FromOUString(*this, rStr); else - write_lenPrefixed_uInt8s_FromOUString<sal_uInt16>(*this, rStr, eDestCharSet); + write_uInt16_lenPrefixed_uInt8s_FromOUString(*this, rStr, eDestCharSet); return *this; } @@ -1627,11 +1637,11 @@ SvStream& endl( SvStream& rStr ) { LineEnd eDelim = rStr.GetLineDelimiter(); if ( eDelim == LINEEND_CR ) - rStr << '\r'; + rStr.WriteChar('\r'); else if( eDelim == LINEEND_LF ) - rStr << '\n'; + rStr.WriteChar('\n'); else - rStr << '\r' << '\n'; + rStr.WriteChar('\r').WriteChar('\n'); return rStr; } @@ -1640,13 +1650,13 @@ SvStream& endlu( SvStream& rStrm ) switch ( rStrm.GetLineDelimiter() ) { case LINEEND_CR : - rStrm << sal_Unicode('\r'); + rStrm.WriteUnicode('\r'); break; case LINEEND_LF : - rStrm << sal_Unicode('\n'); + rStrm.WriteUnicode('\n'); break; default: - rStrm << sal_Unicode('\r') << sal_Unicode('\n'); + rStrm.WriteUnicode('\r').WriteUnicode('\n'); } return rStrm; } @@ -2170,4 +2180,55 @@ OUString convertLineEnd(const OUString &rIn, LineEnd eLineEnd) return tmpl_convertLineEnd<OUString, OUStringBuffer>(rIn, eLineEnd); } +sal_Size write_uInt32_lenPrefixed_uInt16s_FromOUString(SvStream& rStrm, + const OUString &rStr) +{ + sal_Size nWritten = 0; + sal_uInt32 nUnits = std::min<sal_Size>(rStr.getLength(), std::numeric_limits<sal_uInt32>::max()); + SAL_WARN_IF(static_cast<sal_Size>(nUnits) != static_cast<sal_Size>(rStr.getLength()), + "tools.stream", + "string too long for prefix count to fit in output type"); + rStrm.WriteUInt32(nUnits); + if (rStrm.good()) + { + nWritten += sizeof(sal_uInt32); + nWritten += write_uInt16s_FromOUString(rStrm, rStr, nUnits); + } + return nWritten; +} + +sal_Size write_uInt16_lenPrefixed_uInt16s_FromOUString(SvStream& rStrm, + const OUString &rStr) +{ + sal_Size nWritten = 0; + sal_uInt16 nUnits = std::min<sal_Size>(rStr.getLength(), std::numeric_limits<sal_uInt16>::max()); + SAL_WARN_IF(nUnits != rStr.getLength(), + "tools.stream", + "string too long for prefix count to fit in output type"); + rStrm.WriteUInt16(nUnits); + if (rStrm.good()) + { + nWritten += sizeof(nUnits); + nWritten += write_uInt16s_FromOUString(rStrm, rStr, nUnits); + } + return nWritten; +} + +sal_Size write_uInt16_lenPrefixed_uInt8s_FromOString(SvStream& rStrm, + const OString &rStr) +{ + sal_Size nWritten = 0; + sal_uInt16 nUnits = std::min<sal_Size>(rStr.getLength(), std::numeric_limits<sal_uInt16>::max()); + SAL_WARN_IF(static_cast<sal_Size>(nUnits) != static_cast<sal_Size>(rStr.getLength()), + "tools.stream", + "string too long for sal_uInt16 count to fit in output type"); + rStrm.WriteUInt16( nUnits ); + if (rStrm.good()) + { + nWritten += sizeof(sal_uInt16); + nWritten += write_uInt8s_FromOString(rStrm, rStr, nUnits); + } + return nWritten; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |