diff -r 000000000000 -r 638b9c697799 apicompatanamdw/compatanalysercmd/libraryanalyser/src/la_functionanalysis.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/apicompatanamdw/compatanalysercmd/libraryanalyser/src/la_functionanalysis.cpp Tue Jan 12 14:52:39 2010 +0530 @@ -0,0 +1,628 @@ +/* +* Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Functionality of function analysis +* +*/ + + +#include "la.hpp" + +// ---------------------------------------------------------------------------------------------------------- + +/** + * This flags affects to the function comparison policy. + */ +// Default policy (=exact match): +#define COMPARE_FLAG_NONE 0x00000000 + +// This flag means that adding a 'const' qualifier to a function +// parameter does not generate BBC break issue: +#define COMPARE_FLAG_ALLOW_CONST_ADDITION 0x00000001 + +// ---------------------------------------------------------------------------------------------------------- + +// Forward declaration +class ParameterList; + +// ParameterElement class represents a parameter element, which has an actual +// value and optional sub-parameters. +class ParameterElement +{ +public: + // Default constructor and destructor + ParameterElement(); + virtual ~ParameterElement(); + + // Copy constructor + ParameterElement(const ParameterElement& rhs); + // Assignment operator + const ParameterElement& operator= (const ParameterElement& rhs); + + // Methods for getting the element value. + string& Value() { return val; }; + const string& Value() const { return val; }; + + // Methods for getting the sub-parameters. + ParameterList* SubParameters() { return subPars; }; + const ParameterList* SubParameters() const { return subPars; }; + + // Sets the sub-parameters. Takes the ownership to the given + // sub-parameter list pointer. + void SetSubParameters( ParameterList* pPars ); + + // Clears the value and sub-parameters. + void Clear(); + +private: + string val; + ParameterList* subPars; +}; + +// ---------------------------------------------------------------------------------------------------------- + +typedef vector FunctionParameter; + +class ParameterList : public vector +{}; + +ParameterElement::ParameterElement() +{ + subPars = new ParameterList(); +} +ParameterElement::~ParameterElement() +{ + delete subPars; +} +ParameterElement::ParameterElement(const ParameterElement& rhs) +{ + val = rhs.val; + if( rhs.subPars ) + subPars = new ParameterList(*(rhs.subPars)); + else + subPars = 0; +} + +void ParameterElement::Clear() +{ + val.clear(); + if( subPars ) + subPars->clear(); +} + +const ParameterElement& ParameterElement::operator= (const ParameterElement& rhs) + { + if( this != &rhs ) + { + val = rhs.val; + delete subPars; + if( rhs.subPars ) + subPars = new ParameterList(*(rhs.subPars)); + else + subPars = 0; + } + return *this; + } + +void ParameterElement::SetSubParameters( ParameterList* pPars ) +{ + delete subPars; + subPars = pPars; +} + +// ---------------------------------------------------------------------------------------------------------- + +/** + * This structure represents the C++ cv-qualifier. + */ +struct CVQualifier +{ + static const string constQualifier; + static const string volatileQualifier; + static const string constVolatileQualifier; + + CVQualifier() { isConst = false; isVolatile = false; }; + + /** + * Set function gets std::string as an input and sets const + * and/or volatile flags according to the input. + * @param cvStr String representing the const and / or volatile qualifier + * i.e "const", "volatile" or "const volatile". + */ + void Set(string const& cvStr) + { + if( cvStr == constQualifier ) + { + isConst = true; + isVolatile = false; + } + else if( cvStr == volatileQualifier ) + { + isConst = false; + isVolatile = true; + } + else if( cvStr == constVolatileQualifier ) + { + isConst = true; + isVolatile = true; + } + else + { + isConst = false; + isVolatile = false; + } + }; + + /** + * Sets const and/or volatile flags + * @param c 'const' qualifier. If TRUE, isConst flag is set to TRUE + * @param v 'volatile' qualifier. If TRUE, isVolatile flag is set to TRUE + */ + void Set( bool c, bool v ) + { + isConst = c; + isVolatile = v; + }; + bool isConst; + bool isVolatile; +}; + +// ---------------------------------------------------------------------------------------------------------- + +const string CVQualifier::constQualifier = string("const"); +const string CVQualifier::volatileQualifier = string("volatile"); +const string CVQualifier::constVolatileQualifier = string("const volatile"); + +// ---------------------------------------------------------------------------------------------------------- + + +// This represents the function signature +struct FuncSignature +{ + string nestedName; // name part + ParameterList parameters; // parameters + CVQualifier cvQualifier; // cv-qualifier of function +}; + +// ---------------------------------------------------------------------------------------------------------- + + +bool AreParametersCompatible(const FunctionParameter& basePar, const FunctionParameter& currPar, unsigned int CompareFlags = COMPARE_FLAG_NONE); +string::const_iterator RemoveSpaces(string::const_iterator start, string::const_iterator end, string::const_iterator& result); +string::size_type ReadCharsInTag(const string& sig, string& argElem, string::size_type startPos, pair const& tag); + + +// ---------------------------------------------------------------------------------------------------------- + +/** + * Reads characters inside the given tag. Starts looking for the + * starting tag at startPos. + * @param sig Reference to the string representing the function signature. + * @param argElem Reference to the string in which the parameter list is read. + * @param startPos Tag starting point (e.g. '<' or '(') is startet to be searched + * after this location. + * @param tag This object defines the starting and ending characters of the tag (e.g. '<' and '>'). + * @return Size of the string between the start and end characters of the tag. + */ +string::size_type ReadCharsInTag(const string& sig, string& argElem, string::size_type startPos, pair const& tag) +{ + string::size_type sigLen = sig.size(); + if( sigLen == 0 ) + { + return 0; + } + + if( sig.at(startPos) != tag.first ) + { + startPos = sig.find(tag.first, startPos); + } + + string::size_type currPos = startPos; + + int tagCnt = 0; // This counter will be increased by 1 when tag starts and + // decreased by 1 when the tag ends. When it reaches zero, + // the tag has been completely read. + + do + { + const char current = sig.at(currPos); // throws, if out of bounds + + if( current == tag.first ) + ++tagCnt; // tag starts + else if( current == tag.second ) + --tagCnt; // tag ends + + ++currPos; + } + while( tagCnt > 0 ); + + string::size_type len = currPos - startPos; + + if( len > 0 ) + { + argElem.append(sig.begin() + startPos, sig.begin() + currPos); + } + + return len; +} + +// ---------------------------------------------------------------------------------------------------------- + +/** + * ParseParameterList. Recursively parses the given sub-string into + * parameter list object. + * @param funcSignature Reference to the string representing the function + * signature. + * @param parBegin Location where the parameter list starts. + * @param parEnd Location where the parameter list ends. + * @param parameters Parameter list of the function is returned here. + */ +void ParseParameterList(const string& funcSignature, + string::size_type parBegin, + string::size_type parEnd, + ParameterList& parameters) +{ + FunctionParameter par; // Object holding the current function parameter during parsing + ParameterElement parElem; // Object holding the current parameter element during parsing + + while( parBegin <= parEnd ) + { + char current = funcSignature.at(parBegin); + switch( current ) + { + case '(': + case '<': + { + // Character '(' or '<' means that a new parameter list starts. + // New parameter list is stored as a sub-parameter list of the current parameter. + + if( parElem.Value().size() > 0 ) + { + par.push_back(parElem); // Put the previous element in the list... + parElem.Clear(); // ...and clear the object for the next round. + } + + string parListStr; // String representing the sub-parameterlist is stored here. + int len = 0; + char tagEndMark = current == '(' ? ')' : '>'; + + // Put the value "<>" or "()" + parElem.Value().push_back(current); + parElem.Value().push_back(tagEndMark); + + // Read sub-parameters... + len += ReadCharsInTag( funcSignature, parListStr, parBegin, make_pair(current, tagEndMark) ); + // ...and parse them + if( parElem.SubParameters() == 0 ) + { + parElem.SetSubParameters(new ParameterList()); + } + ParseParameterList(parListStr, 1, len-1, *(parElem.SubParameters()) ); + + par.push_back(parElem); // Put the current element in the list... + parElem.Clear(); // ...and clear the object for the next round. + + parBegin += len; + break; + } + case ' ': + { + // space means that we have a new parameter element + + if( parElem.Value().size() > 0 ) + { + par.push_back(parElem); // Put the previous element in the list... + parElem.Clear(); // ...and clear the object for the next round. + } + ++parBegin; + break; + } + case ',': + case ')': + case '>': + { + // Parameter completed + + if( parElem.Value().size() > 0 ) + { + par.push_back(parElem); // Put the previous element in the list... + parElem.Clear(); //...and clear the object for the next round. + } + if( par.size() > 0 ) + { + // Put the parameter in the parameter list... + parameters.push_back(par); + par.clear(); //...and clear the object for the next round. + } + ++parBegin; + break; + } + case '*': + case '&': + { + // We have a pointer or reference + + if( parElem.Value().size() > 0 ) + { + par.push_back(parElem); // Put the previous element in the list... + parElem.Clear(); //...and clear the object for the next round. + } + // Put also the current element in the list (i.e. '*' or '&') + parElem.Value().push_back(current); + par.push_back(parElem); + parElem.Clear(); // ...and clear the object for the next round. + ++parBegin; + break; + } + default: + { + parElem.Value().push_back(funcSignature.at(parBegin)); + ++parBegin; + break; + } + } + } +} + +// ---------------------------------------------------------------------------------------------------------- + +/** + * ParseFunctionSignature + * Parses function signature and returns following elements for the given + * function: + * - name + * - parameters, which are splitted in elements and sub-parameters + * - cv-qualifier of the function + * + * Parameters are splitted in following elements: + * - type (int, char, etc...) + * - cv-qualifier (const and/or volatile) + * - pointer symbol (*) + * - reference symbol (&) + * - parameter list "()" + * - template parameter list "<>". + * + * Parameter lists are further splitted into sub-parameters, which also consists + * of elements and sub-parameters. + * + * @param funcSignature Reference to the string representing the function signature + * @param signature Parsed function signature is returned in this object. + */ +void ParseFunctionSignature( const string& funcSignature, FuncSignature& signature ) +{ + // Find the start and end positions of the "main" parameter list + string::size_type openBracketIndex = funcSignature.find_first_of('('); + string::size_type closeBracketIndex = funcSignature.find_last_of(')'); + + if(openBracketIndex == string::npos || closeBracketIndex == string::npos ) + { + // No parameter list, so the given string may be for example + // "virtual table for MyClass" + signature.nestedName = funcSignature; + return; + } + + // Remove preceding and trailing spaces from the function name part: + string::const_iterator start; + string::const_iterator end = RemoveSpaces( + funcSignature.begin(), + funcSignature.begin() + openBracketIndex, + start); + + // Store the name part + signature.nestedName.append(start, end); + + // Parse function parameters + ParseParameterList(funcSignature, openBracketIndex+1, closeBracketIndex, signature.parameters); + + // Parse the cv-qualifier of the function. + end = RemoveSpaces(funcSignature.begin() + (closeBracketIndex+1), funcSignature.end(), start); + signature.cvQualifier.Set(string(start,end)); +} + +// ---------------------------------------------------------------------------------------------------------- + +/** + * AreFunctionsCompatible + * Compares two function signatures for backward binary compatibility. + * This function first parses the function signature into name, parameter list + * and cv-qualifier parts. The name part is compared first and if names don't + * match, return false. Then parameter lists are given to + * AreParametersCompatible function for comparison. + * And finally cv-qualifiers of functions are compared. This comparison function + * allows changing non-const function to const (i.e adding 'const'-qualifier for + * a function). + * @param baselineFunc Reference to the string representing the baseline + * platform's function signature + * @param currentFunc Reference to the string representing the current + * platform's function signature + * @return bool value indicating whether the two functions are backward binary + * compatible. + */ +TypeOfSeverity AreFunctionsCompatible(const string& baselineFunc, const string& currentFunc) +{ + TypeOfSeverity retSeverity = NO_BREAK; + // First split functions into name part, parameter list and possible cv-qualifier: + FuncSignature baseFunc; + FuncSignature currFunc; + + ParseFunctionSignature(baselineFunc, baseFunc); + ParseFunctionSignature(currentFunc, currFunc); + + // Check the const qualifier of the function: + if( baseFunc.cvQualifier.isConst != currFunc.cvQualifier.isConst ) + { + // const qualifier of the function has been removed(either const to non-const or vise versa) + // Results in SC break + retSeverity = CONFIRMED_SC_BREAK; + goto EXIT_POINT; + } + + // Check the number of parameters: + if( baseFunc.parameters.size() != currFunc.parameters.size() ) + { + retSeverity = POSSIBLE_BC_CONFIRMED_SC_BREAK; // Number of parameters does not match. + goto EXIT_POINT; + } + + // Then check name part: + if( baseFunc.nestedName.compare(currFunc.nestedName) != 0 ) + { + retSeverity = POSSIBLE_BC_SC_BREAK; // Names do not match. + goto EXIT_POINT; + } + + // Check the parameters: + for( unsigned int i = 0; i < baseFunc.parameters.size(); ++i ) + { + if( AreParametersCompatible(baseFunc.parameters[i], currFunc.parameters[i], COMPARE_FLAG_ALLOW_CONST_ADDITION) == false ) + { + retSeverity = POSSIBLE_BC_SC_BREAK; // Parameters do not match + goto EXIT_POINT; + } + } + + if( baseFunc.cvQualifier.isVolatile != currFunc.cvQualifier.isVolatile ) + { + retSeverity = POSSIBLE_SC_BREAK; // volatile qualifier does not match + goto EXIT_POINT; + } + +EXIT_POINT: + return retSeverity; +} + +// ---------------------------------------------------------------------------------------------------------- + +/** + * Removes preceding and trailing spaces from the given string + * @return iterator to the end of the trimmed string. + * @param start Iterator to the beginning of the string. + * @param end Iterator to the end of the string. + * @param result Iterator to the beginning of the trimmed string. + */ +string::const_iterator RemoveSpaces(string::const_iterator start, + string::const_iterator end, + string::const_iterator& result) +{ + // Remove spaces from the beginning of the string: + result = start; + while( result != end && *result == ' ' ) + { + ++result; + } + + // Remove spaces from the end of the string: + string::const_iterator resultEnd = end; + while( resultEnd != result && *(resultEnd-1) == ' ' ) + { + --resultEnd; + } + + return resultEnd; +} + +// ---------------------------------------------------------------------------------------------------------- + +/** + * AreParametersCompatible + * Checks if two function parameters are compatible with each other. + * Allows added const qualifiers in current version of the parameter. + * This function loops through parameters, that are splitted into elements, + * and compares them element by element. If the elements differ and current + * platform's element is 'const' qualifier, skip the current platform's element + * and compare next one to the baseline platform's element. + * @param basePar Reference to the parameter object representing the parameter + * of the baseline platform's function. + * @param currPar Reference to the parameter object representing the parameter + * of the current platform's function. + * @param compareFlags Comparison policy flags that should be used when comparing + * the parameters. + * @return bool value indicating if the two parameters are backward binary compatible. + */ +bool AreParametersCompatible( const FunctionParameter& basePar, + const FunctionParameter& currPar, + unsigned int compareFlags ) +{ + const string cvQualConst("const"); // const qualifier + const string ptrStr("*"); // pointer + const string refStr("&"); // reference + if( basePar.size() > currPar.size() ) + { + // Something has been removed: + return false; + } + + vector::const_iterator baseElem = basePar.begin(); + vector::const_iterator currElem = currPar.begin(); + while( baseElem != basePar.end() && currElem != currPar.end() ) + { + if( baseElem->Value().compare(currElem->Value()) != 0 ) + { + if( currElem->Value().compare(cvQualConst) == 0 && + (compareFlags & COMPARE_FLAG_ALLOW_CONST_ADDITION) ) + { + // COMPARE_FLAG_ALLOW_CONST_ADDITION used, so it is acceptable that + // 'const' qualifier has been added to current parameter. So let's skip + // the 'const' qualifier -element and compare next elements to see if + // the parameter is otherwise compatible. + ++currElem; + continue; + } + else + { + return false; // Parameter elements do not match + } + } + + // Now, lets take the sub-parameters (if any) and call this function recursively: + const ParameterList* baseSubPars = baseElem->SubParameters(); + const ParameterList* currSubPars = currElem->SubParameters(); + if( baseSubPars != 0 && currSubPars != 0 ) + { + if( baseSubPars->size() != currSubPars->size() ) + return false; // Number of sub-parameters do not match + + for( unsigned int subI = 0; subI < baseSubPars->size(); ++subI ) + { + // Here we are dealing with sub-parameters (e.g parameter list of a function + // pointer parameter), and no 'const addition' flags etc. are used anymore. + if( AreParametersCompatible( baseSubPars->at(subI), currSubPars->at(subI)) == false ) + return false; + } + } + else if( baseSubPars != currSubPars ) + { + return false; // Other one's subparameter list pointer is NULL + } + + ++baseElem; + ++currElem; + } + + // Check for the const pointer. Actually in this case the 'const' qualifier + // is left out from the mangled/demangled signature, but just to be sure... + // For example: 'int *' changed to 'int * const' + if( baseElem == basePar.end() && currElem != currPar.end() ) + { + if( currElem->Value().compare(cvQualConst) == 0 ) + { + ++currElem; + } + } + + // Check that all the elements have been "consumed": + return baseElem == basePar.end() && currElem == currPar.end(); +} + +// ----------------------------------------------------------------------------------------------------------