From 187d4942b512956bcfc3144d0eab7696343f48d5 Mon Sep 17 00:00:00 2001 From: Peter Pirkelbauer Date: Mon, 13 May 2024 15:42:30 -0700 Subject: [PATCH 1/3] (ROSE) integrate with newer SageInterface - prepare for SageInterface updates - process files resiliently by ignore unknown constructs --- include/traverse.hpp | 132 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 113 insertions(+), 19 deletions(-) diff --git a/include/traverse.hpp b/include/traverse.hpp index 7d826a8..90bc25e 100644 --- a/include/traverse.hpp +++ b/include/traverse.hpp @@ -38,6 +38,31 @@ class Attribute; template class CalledMethod; +namespace sagehelper +{ + // replace with SageInterface::Ada::declOf + // when available + inline + SgInitializedName& declOf(const SgEnumVal& n) + { + SgEnumDeclaration& dcl = SG_DEREF(n.get_declaration()); + SgInitializedNamePtrList& lst = dcl.get_enumerators(); + + const auto lim = lst.end(); + const auto pos = std::find_if( lst.begin(), lim, + [&n](sg::NotNull nm)->bool + { + return boost::iequals( nm->get_name().getString(), + n.get_name().getString() + ); + } + ); + + ASSERT_require(pos != lim); + return SG_DEREF(*pos); + } +} + // Attribute type. class AType { public: @@ -53,13 +78,33 @@ class AType { std::vector attrs(exps.size()); std::transform(exps.cbegin(), exps.cend(), attrs.begin(), [](SgExpression* exp) { - SgVarRefExp* vre = is(exp); - if (!vre) + T d = nullptr; + + if (SgVarRefExp* vre = is(exp)) + { + d = is(vre->get_symbol()->get_declaration()); + } + else if (SgEnumVal* enm = is(exp)) + { + d = &sagehelper::declOf(*enm); + } + else + { + if (false) + { + ASSERT_require(exp); + std::cerr << exp->get_parent()->unparseToString() + << " -> " << exp->unparseToString() + << " : " << typeid(*exp).name() + << std::endl; + } + LOG(FATAL) << "Conversion of " << NPrint::p(exp) << " to SgVarRefExp* failed" << std::endl; - T d = is(vre->get_symbol()->get_declaration()); + } + if (!d) - LOG(FATAL) << "Declaration of " << NPrint::p(vre) + LOG(FATAL) << "Declaration of " << exp->unparseToString() << " is null." << std::endl; return d; }); @@ -245,26 +290,49 @@ static T GetScope(AType n) { template std::vector GetRecords(SgNode* n) { + namespace siada = SageInterface::Ada; + std::vector owningClassIds; + + // functor to store all type declarations associated with some function + auto storeRecordAssociation = + [&owningClassIds,n] + (const SgDeclarationStatement* tydcl) -> void + { + ASSERT_require(tydcl); + + SgDeclarationStatement* typeDeclaration = tydcl->get_firstNondefiningDeclaration(); + + if (C classId = is(typeDeclaration)) { + LOG(DEBUG) << NPrint::p(n) << " is a method within the class, " + << NPrint::p(classId) << std::endl; + owningClassIds.push_back(classId); + } + }; + // Tagged records approach. // Functions/procedures that are tied to a tagged record will list the tagged // record as a parameter. This code will find the classes associated with // those parameters. if (SgFunctionDeclaration* fd = is(n)) { - std::vector records; - records = SageInterface::Ada::primitiveParameterPositions(fd); - for (auto& record : records) { +#if NEW_SIGNATURE_PROCESSING + siada::PrimitiveSignatureElementsDesc elements = siada::primitiveSignatureElements(fd); + + if (elements.result()) + storeRecordAssociation(elements.result()); + + for (auto& record : elements.parameters()) { // Get the type declaration of this record. // TODO: There seem to be some dupes. Ask Peter about it. - SgDeclarationStatement* typeDeclaration = - record.typeDeclaration()->get_firstNondefiningDeclaration(); - // If it is a class, add it to the list. - if (C classId = is(typeDeclaration)) { - LOG(DEBUG) << NPrint::p(n) << " is a method within the class, " - << NPrint::p(classId) << std::endl; - owningClassIds.push_back(classId); - } + storeRecordAssociation(record.typeDeclaration()); } +#else /* NEW_SIGNATURE_PROCESSING */ + for (auto& record : siada::primitiveParameterPositions(fd)) { + // Get the type declaration of this record. + // TODO: There seem to be some dupes. Ask Peter about it. + storeRecordAssociation(record.typeDeclaration()); + } +#endif /* NEW_SIGNATURE_PROCESSING */ } return owningClassIds; } @@ -693,12 +761,31 @@ std::vector GetRootExp(SgExpression* exp) { return GetRootExp(pare->get_lhs_operand()); } + // + if (SgCastExp* castexp = is(exp)) { + return GetRootExp(castexp->get_operand()); + } + + // PP: 05/13/24 not sure how to handle function calls.. + // ignore them for now? + // case: + // x : Integer renames Identity(1); -- x renames result of function call + // -- similar to variable + if (/*SgFunctionCallExp* callexp =*/ is(exp)) { + return {}; + } + // No further unwrapping match found. - std::vector ret{exp}; - return ret; + return { exp }; } SgExpression* GetBaseRootExp(std::vector rootExp) { - if (!rootExp.size()) LOG(FATAL) << "Empty root list found." << std::endl; + if (!rootExp.size()) + { + // PP 05/13/24 return nullptr instead of failing.. + LOG(WARNING) << "Empty root list found." << std::endl; + return nullptr; + } + return rootExp[0]; } template @@ -874,6 +961,9 @@ class VisitorTraversal : public AstTopDownProcessing> { std::vector root = GetRootExp(id); SgVarRefExp* baseRootExp = is(GetBaseRootExp(root)); + // PP 05/13/24 added null test + if (baseRootExp == nullptr) return IA(ia); + Class* cPtr = GetOwningClass(baseRootExp); if (!cPtr) return IA(ia); Class& owningClass = *cPtr; @@ -959,6 +1049,9 @@ class VisitorTraversal : public AstTopDownProcessing> { std::vector root = GetRootExp(id); SgFunctionRefExp* baseRootExp = is(GetBaseRootExp(root)); + // PP 05/13/24 added null test + if (baseRootExp == nullptr) return IA(ia); + Class* cPtr = GetOwningClass(baseRootExp); if (!cPtr) return IA(ia); Class& owningClass = *cPtr; @@ -1136,7 +1229,8 @@ class RenamingTraversal : public AstTopDownProcessing> { // If the root expression is an SgVoidVal or SgNullExpression, then it // must be part of a generic function declaration, which can be safely // ignored. - if (is(baseRootExp) || is(baseRootExp)) { + // PP: 05/13/24 - added null test + if ((baseRootExp == nullptr) || is(baseRootExp) || is(baseRootExp)) { LOG(DEBUG) << NPrint::p(baseRootExp) << " is part of a generic function declaration. Ignoring" << std::endl; From 83c3a332859b32c3f0505ecc0e0dcd145071f756 Mon Sep 17 00:00:00 2001 From: Peter Pirkelbauer Date: Mon, 3 Jun 2024 10:10:46 -0700 Subject: [PATCH 2/3] (merge) merge updates from main line --- Makefile | 2 ++ include/traverse.hpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 72397c9..ddb6fd8 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +-include env.mk + # Note: This Makefile is no longer maintained. Please use the cmake build system to build this tool instead. This Makefile is kept here for reference. # Makefile for LCOM metrics tool using the ROSE compiler framework. diff --git a/include/traverse.hpp b/include/traverse.hpp index e9b96c0..e1902cd 100644 --- a/include/traverse.hpp +++ b/include/traverse.hpp @@ -294,6 +294,8 @@ std::vector GetRecords(SgNode* n) { LOG(DEBUG) << "Finding additional records for " << NPrint::p(n) << std::endl; + std::vector owningClassIds; + // functor to store all type declarations associated with some function auto storeRecordAssociation = [&owningClassIds,n] From cacd1761ab802f50ba78213cbc576e7815c6f618 Mon Sep 17 00:00:00 2001 From: Peter Pirkelbauer Date: Mon, 3 Jun 2024 12:51:18 -0700 Subject: [PATCH 3/3] (expr) provide node handling - handle attributes: handle the left hand side of an attr. x'size - handle type expression: ignore --- include/node-print.hpp | 15 +++++++++++++++ include/traverse.hpp | 8 ++++++++ src/lcom.cpp | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/include/node-print.hpp b/include/node-print.hpp index 3bdd86b..bd3babb 100644 --- a/include/node-print.hpp +++ b/include/node-print.hpp @@ -98,6 +98,21 @@ std::string print(const SgNode* n) { return ss.str(); } +std::string simple_name(const SgNode* n) { + if (n == nullptr) return ""; + + std::stringstream ss; + + if (anonymous) + { + ss << n; + return ss.str(); + } + + return sg::dispatch(NPrint{}, n); +} + + #define p(n) print(n) } // namespace NPrint diff --git a/include/traverse.hpp b/include/traverse.hpp index e1902cd..a184e1b 100644 --- a/include/traverse.hpp +++ b/include/traverse.hpp @@ -776,11 +776,19 @@ std::vector GetRootExp(SgExpression* exp) { return GetRootExp(pare->get_lhs_operand()); } + if (SgAdaAttributeExp* attr = is(exp)) { + return GetRootExp(attr->get_object()); + } + // if (SgCastExp* castexp = is(exp)) { return GetRootExp(castexp->get_operand()); } + if (/*SgTypeExpression* typeex =*/ is(exp)) { + return {}; + } + // PP: 05/13/24 not sure how to handle function calls.. // ignore them for now? // case: diff --git a/src/lcom.cpp b/src/lcom.cpp index 72618f7..c1b44b8 100644 --- a/src/lcom.cpp +++ b/src/lcom.cpp @@ -109,8 +109,28 @@ std::tuple, Settings> parseArgs( return std::make_tuple(cmdline.unparsedArgs(), settings); } +/* +std::string sourceLocation(const SgNode*, const std::string& alt) +{ + return alt; +} + +std::string sourceLocation(const SgLocatedNode* n, const std::string& alt) +{ + if (n == nullptr) return alt; + + const Sg_File_Info* fi = n->get_file_info(); + if (fi == nullptr) return alt; + + return fi->get_filenameString(); +} +*/ + +bool specHasBody(const SgNode*) { return true; } +bool specHasBody(const SgAdaPackageSpec* spec) { return spec->get_body() != nullptr; } + template -std::string ProcessLCOM(SgProject*& project) { +std::string ProcessLCOM(SgProject* project) { std::stringstream ss; const std::vector> LCOMInput = Traverse::GetClassData(project); @@ -118,11 +138,17 @@ std::string ProcessLCOM(SgProject*& project) { for (const auto& LCOMClass : LCOMInput) { std::string className = "null"; boost::filesystem::path sourceFile = Traverse::sourceFile; - if (is(LCOMClass.GetId())) { + + // true, iff package spec & body were available or !package + bool hasBody = true; + if (C elem = is(LCOMClass.GetId())) { Traverse::Class& classObj = Traverse::IA::classData.at(LCOMClass.GetId()); - className = NPrint::p(classObj.GetId()); + className = NPrint::simple_name(classObj.GetId()); + //~ className = NPrint::p(classObj.GetId()); + //~ sourceFile = sourceLocation(elem, classObj.sourceFile); sourceFile = classObj.sourceFile; + hasBody = specHasBody(elem); } std::cout << "Class: " << className << std::endl; LCOM::LCOM1Data data1; @@ -148,6 +174,9 @@ std::string ProcessLCOM(SgProject*& project) { // https://www.tusharma.in/yalcom-yet-another-lcom-metric.html std::cout << "LCOM4Norm: " << (double)lcom4 / (double)data5.k << std::endl; + // PP: reporting LCOM on a spec w/o function bodies may not be very meaningful. + if (!hasBody) continue; + // Generate a line of CSV. ss << sourceFile << ",\"" << className << "\",\"" << typeid(C).name() << "\",\"" << dotBehavior << "\",";