Skip to content

Commit

Permalink
Analyze macro parameters for non-object values
Browse files Browse the repository at this point in the history
- Refactor related processing into a separate object
- Complete required map
- Update metrics in tests
  • Loading branch information
dspinellis committed Jul 28, 2024
1 parent deb53eb commit f50786c
Show file tree
Hide file tree
Showing 60 changed files with 1,646 additions and 1,492 deletions.
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ HEADERS=attr.h call.h compiledre.h cpp.h ctag.h ctconst.h ctoken.h \
macro.h mcall.h md5.h metrics.h mquery.h mscdefs.h mscincs.h obfuscate.h \
option.h os.h pager.h pdtoken.h pltoken.h ptoken.h query.h sql.h stab.h \
swill.h tchar.h timer.h token.h tokid.h tokmap.h type.h type2.h version.h \
wdefs.h wincs.h workdb.h ytoken.h
wdefs.h wincs.h workdb.h ytoken.h macro_arg_processor.h

OTHERSRC=style.css csmake.pl cswc.pl tokname.pl runtest.sh eval.y parse.y \
Makefile
Expand Down
25 changes: 25 additions & 0 deletions src/call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ Call::fun_map Call::all;
// Nested statements created from macro expansion
int Call::macro_nesting;

// All known macros
map<Call::name_identifier, Call *> Call::macros;

// The current function makes a call to f
void
Call::register_call(Call *f)
Expand Down Expand Up @@ -344,3 +347,25 @@ Call::dumpSql(Sql *db, ostream &of)
ptr_offset(*dest) << ");\n";
}
}

/*
* After the projects have been processed populate a map from the name
* of each macro to its Call object.
*/
void
Call::populate_macro_map()
{
for (auto fit = all.begin(); fit != all.end(); ++fit) {
Call *f = fit->second;

if (!f->is_macro())
continue;

name_identifier name;
const auto& tparts(f->token.constituents());
for (auto t = tparts.begin(); t != tparts.end(); ++t)
name.push_back(t->get_tokid().get_ec());

macros.emplace(name, f);
}
}
15 changes: 15 additions & 0 deletions src/call.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ class Pltoken;
* macro names consisting of multiple parts
*/
class Call {
public:
// A unique full identifier of a macro or function name
typedef vector <Eclass *> name_identifier;
private:

// Container for storing called and calling functions
Expand All @@ -76,6 +79,7 @@ class Call {
*/
typedef multimap <Tokid, Call *> fun_map;


string name; // Function's name
fun_container call; // Functions this function calls
fun_container caller; // Functions that call this function
Expand All @@ -90,6 +94,8 @@ class Call {
void add_call(Call *f) { call.insert(f); }
void add_caller(Call *f) { caller.insert(f); }

// All known macros
static map<name_identifier, Call *> macros;
protected:
static fun_map all; // Set of all functions
static Call *current_fun; // Function currently being parsed
Expand Down Expand Up @@ -139,6 +145,15 @@ class Call {
// Dump the data in SQL format
static void dumpSql(Sql *db, ostream &of);

// Populate a map from ECs to macros
static void populate_macro_map();

// Return a macro corresponding to the specified name
static Call* get_macro(const name_identifier &name) {
auto it = macros.find(name);
return it == macros.end() ? nullptr : it->second;
}

const string &get_name() const { return name; }
bool contains(Eclass *e) const;

Expand Down
60 changes: 9 additions & 51 deletions src/cscout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
#include "fifstream.h"
#include "ctag.h"
#include "timer.h"
#include "macro_arg_processor.h"

#ifdef PICO_QL
#include "pico_ql_search.h"
Expand Down Expand Up @@ -299,13 +300,7 @@ file_analyze(Fileid fi)
exit(1);
}

bool analyze_param = false; // True if analyzing macro parameters
int bracket_nesting = 0; // Nesting during the analysis
int non_obj_param = 0; // Number of non-object parameters
// Check this or next parameter token for prohibited values
bool check_param_token = false;
bool check_next_param_token = false;
vector <Eclass *> macro_name_ecs;
MacroArgProcessor ma_proc;

// Go through the file character by character
for (;;) {
Expand Down Expand Up @@ -340,33 +335,7 @@ file_analyze(Fileid fi)
mapTokidEclass::iterator ei;
enum e_cfile_state cstate = Filedetails::get_pre_cpp_metrics(fi).get_state();

/*
* Analyze macro parameters for arguments that make them
* unsuitable for converting into a C function.
*/
if (analyze_param &&
cstate != s_block_comment &&
cstate != s_string &&
cstate != s_cpp_comment) {
// cout << "Got " << c << "\n";
if (check_next_param_token)
check_param_token = true;
check_next_param_token = false;
if (c == '(') {
if (bracket_nesting == 0)
check_next_param_token = true;
bracket_nesting++;
} else if (c == ')') {
bracket_nesting--;
if (bracket_nesting == 0) {
analyze_param = false;
Call* macro = Call::get_macro(macr_name_ecs);
if (macro)
macro->get_pre_cpp_metrics().add_metric(FunMetrics::em_nnoparam, non_obj_param);

} else if (c == ',' && bracket_nesting == 1)
check_next_param_token = true;
}
ma_proc.process_char(cstate, c);

// Mark identifiers
if (cstate != s_block_comment &&
Expand Down Expand Up @@ -410,10 +379,6 @@ file_analyze(Fileid fi)
has_unused = true;
else
; // TODO fi.set_associated_files(ec);

if (check_param_token
&& ec->get_attribute(is_macro)) {

} else {
/*
* This equivalence class is not needed.
Expand All @@ -425,18 +390,7 @@ file_analyze(Fileid fi)
delete ec;
ec = NULL;
}


if (ec && !analyze_param
&& ec->get_attribute(is_macro)) {
cout << "Macro " << *ec << "\n";
analyze_param = true;
bracket_nesting = 0;
non_obj_param = 0;
macro_ecs.clear();
}
if (ec && analyze_param)
macro_name_ecs.push_back(ec);
ma_proc.process_ec(ec, s);
}
Filedetails::get_pre_cpp_metrics(fi).process_char((char)val);
if (cfun)
Expand Down Expand Up @@ -3572,7 +3526,11 @@ main(int argc, char *argv[])
swill_handle("qexit.html", quit_page, 0);
}

// Populate the EC identifier member and the directory tree
/*
* Populate the EC identifier member and the directory tree.
* Set several file and function metrics.
*/
Call::populate_macro_map();
for (vector <Fileid>::iterator i = files.begin(); i != files.end(); i++) {
file_analyze(*i);
dir_add_file(*i);
Expand Down
1 change: 1 addition & 0 deletions src/funmetrics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ FunMetrics::metric_details_values()
v[em_ngnsoc] = MetricDetails(0, 1, 0, "NGNSOC", "Number of global namespace occupants at function's top");
v[em_nmparam] = MetricDetails(1, 0, 0, "NMPARAM", "Number of parameters (for macros)");
v[em_nfparam] = MetricDetails(0, 1, 0, "NFPARAM", "Number of parameters (for functions)");
v[em_nneparam] = MetricDetails(1, 0, 0, "NEPARAM", "Number of passed non-expression macro parameters");
// Metrics dynamically derived
v[em_fanin] = MetricDetails(1, 1, 0, "FANIN", "Fan-in (number of calling functions)");
v[em_fanout] = MetricDetails(1, 1, 0, "FANOUT", "Fan-out (number of called functions)");
Expand Down
1 change: 1 addition & 0 deletions src/funmetrics.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class FunMetrics : public Metrics {
Metrics::metric_max,
em_nmparam, // Number of macro parameters
em_nfparam, // Number of function parameters
em_nneparam, // Number of passed non-expression macro parameters
// Stored metrics stop here
stored_metric_max,
// The following metrics are dynamically derived
Expand Down
154 changes: 154 additions & 0 deletions src/macro_arg_processor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* (C) Copyright 2024 Diomidis Spinellis
*
* This file is part of CScout.
*
* CScout is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CScout is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CScout. If not, see <http://www.gnu.org/licenses/>.
*
*
* A class processing macro arguments when the files are being post-processed
* and setting the em_nneparam metric.
* This is incremented for the first token of each parameter that cannot
* be part of a C expression. This means C keywords, structure tags,
* structure members, and typedefs. Such macros definitely cannot
* be easily converted into C functions.
* Only the first token of each parameter is checked, because
* macro(int, double) is problematic, whereas
* macro((int)a, (double)b) isn't.
*
*/

#ifndef MACRO_ARG_PROCESSOR_
#define MACRO_ARG_PROCESSOR_

#include <vector>

using namespace std;

#include "attr.h"
#include "tokid.h"
#include "ctoken.h"
#include "eclass.h"
#include "funmetrics.h"
#include "call.h"

class MacroArgProcessor {
private:
int bracket_nesting; // Nesting during the analysis
int non_obj_param; // Number of non-object parameters

// State for processing macro arguments. Order matters!
enum e_mastate {
ma_scan_for_macro_name,
ma_collect_name,
ma_before_param_token, // From here onward we're processing
ma_first_param_token,
ma_in_params
} mstate;

// ECs for macro name
Call::name_identifier macro_name_ecs;

// Called after the macro's parameters have been processed
void finish_processing(void) {
mstate = ma_scan_for_macro_name;
Call* macro = Call::get_macro(macro_name_ecs);
if (macro)
macro->get_pre_cpp_metrics().add_metric(FunMetrics::em_nneparam, non_obj_param);
}

public:
// Construct object in initial state
MacroArgProcessor() : bracket_nesting(0), non_obj_param(0),
mstate(ma_scan_for_macro_name) {}

// Call for every character being processed
void process_char(enum e_cfile_state cstate, char c) {
/*
* Analyze macro parameters for arguments that make them
* unsuitable for converting into a C function.
*/
if (cstate == s_block_comment
|| cstate == s_string
|| cstate == s_cpp_comment)
return;

switch (mstate) {
case ma_scan_for_macro_name:
return;
case ma_collect_name:
// Check for open bracket
break;
case ma_before_param_token:
mstate = ma_first_param_token;
break;
case ma_first_param_token:
if (isspace(c)) {
mstate = ma_before_param_token;
return;
}
break;
case ma_in_params:
break;
}

if (c == '(') {
if (bracket_nesting == 0)
mstate = ma_before_param_token;
bracket_nesting++;
} else if (c == ')') {
bracket_nesting--;
if (bracket_nesting == 0)
finish_processing();

} else if (c == ',' && bracket_nesting == 1)
mstate = ma_before_param_token;
}

// Called for every ec being processed. s is the represented string
void process_ec(Eclass *ec, const string &s) {
switch (mstate) {
case ma_scan_for_macro_name:
if (!ec || !ec->get_attribute(is_macro))
break;
mstate = ma_collect_name;
bracket_nesting = 0;
non_obj_param = 0;
macro_name_ecs.assign(1, ec);
break;
case ma_collect_name:
if (ec && ec->get_attribute(is_macro))
// Multi-part macro name
macro_name_ecs.push_back(ec);
else
// Probably object macro
mstate = ma_scan_for_macro_name;
break;
case ma_first_param_token:
if ((ec && (ec->get_attribute(is_sumember)
|| ec->get_attribute(is_suetag)
|| ec->get_attribute(is_typedef)
|| ec->get_attribute(is_label)))
|| Ctoken::lookup_keyword(s) != -1) {
non_obj_param++;
}
mstate = ma_in_params;
break;
default:
break;
}
}
};

#endif /* MACRO_ARG_PROCESSOR_ */
Loading

0 comments on commit f50786c

Please sign in to comment.