--- /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<ParameterElement> FunctionParameter;
+
+class ParameterList : public vector<FunctionParameter>
+{};
+
+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 <code>std::string</code> 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<char, char> 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<char, char> 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
+ * <code>AreParametersCompatible</code> 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<ParameterElement>::const_iterator baseElem = basePar.begin();
+ vector<ParameterElement>::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();
+}
+
+// ----------------------------------------------------------------------------------------------------------