From: Michal Sojka Date: Mon, 27 Aug 2012 06:05:09 +0000 (+0200) Subject: Rewrite - get all information from AST instead of manually parsing certain declarations X-Git-Url: https://rtime.felk.cvut.cz/gitweb/boost-statechart-viewer.git/commitdiff_plain/4f7a21f132d2f4c67b5a2280567cd1bd898ad911 Rewrite - get all information from AST instead of manually parsing certain declarations Also simplify .dot file generator. This version works with clang-3.1 on all examples in examples/ directory. Missing compared to previous version: - sub state handling - (maybe) ortoghonal states --- diff --git a/examples/Makefile b/examples/Makefile index 2706a64..b399af9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,10 +1,13 @@ +-include ../Makefile.config + .PHONY: all all: test.pdf StopWatch.pdf main.pdf +CLANG++ ?= clang++ VISUALIZER = -Xclang -load -Xclang ../src/visualizer.so -Xclang -plugin -Xclang visualize-statechart %.o %.dot: %.cpp ../src/visualizer.so - clang++ $(VISUALIZER) -c -o $(<:.cpp=.o) $< + $(CLANG++) $(VISUALIZER) -c -o $(<:.cpp=.o) $< %.eps: %.dot dot -Tps $< > $@ @@ -14,4 +17,3 @@ VISUALIZER = -Xclang -load -Xclang ../src/visualizer.so -Xclang -plugin -Xclang ../src/visualizer: $(MAKE) -C ../src - diff --git a/src/Makefile b/src/Makefile index b1bcb4b..7b078d6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,9 +3,10 @@ LLVM_CONFIG ?= llvm-config LLVM_FLAGS := $(shell $(LLVM_CONFIG) --cxxflags --ldflags --libs jit core) +LLVM_FLAGS := $(filter-out -DNDEBUG,$(LLVM_FLAGS)) visualizer.so: visualizer.cpp stringoper.h iooper.h - $(CXX) $< -o $@ -g -fno-rtti -shared -fpic -Wall -lclangParse -lclangFrontend -lclangSerialization \ + $(CXX) $< -o $@ -g -fno-rtti -shared -fpic -Wall --std=c++11 -lclangParse -lclangFrontend -lclangSerialization \ -lclangDriver -lclangCodeGen -lclangSema \ -lclangAnalysis -lclangRewrite -lclangAST -lclangLex -lclangBasic -lclangEdit \ $(LLVM_FLAGS) diff --git a/src/iooper.h b/src/iooper.h deleted file mode 100644 index a934ac5..0000000 --- a/src/iooper.h +++ /dev/null @@ -1,418 +0,0 @@ -/** @file */ -//////////////////////////////////////////////////////////////////////////////////////// -// -// This file is part of Boost Statechart Viewer. -// -// Boost Statechart Viewer 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. -// -// Boost Statechart Viewer 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 Boost Statechart Viewer. If not, see . -// -//////////////////////////////////////////////////////////////////////////////////////// - -#include -#include -#include - -#include "stringoper.h" - -using namespace std; - -/** -* This class provides saving information about state machine to a specified output file. It saves states and transitions and also it creates the transition table. -*/ -class IO_operations -{ - list transitions; /** list of transitions */ - list states; /** list of states */ - list events; /** list of events */ - string outputFilename; - string name_of_machine; - string name_of_first_state; - string *table; /** transition table. It is being allocated when starting the creation of output file. */ - int nState; - int cols, rows; - /** This function finds place in the transition table to put a transition there. */ - int find_place(string model, int type) - { - if(type == 1) - { - for(int i = 3;i trans, const list state, const list ev ) - { - outputFilename = outputFile; - name_of_machine = FSM_name; - name_of_first_state = firstState; - transitions = trans; - states = state; - events = ev; - } - - ~IO_operations() /** destructor. It deallocates the transition table.*/ - { - delete [] table; - } - - void setEvents(list events) /** Set list of events to an attribute */ - { - this->events = events; - } - - void setTransitions(list transitions) /** Set list of transitions to an attribute */ - { - this->transitions = transitions; - } - - void setStates(list states) /** Set list of states to an attribute */ - { - this->states = states; - } - - void setNameOfStateMachine(string name_of_FSM) /** Set name of FSM to an attribute */ - { - name_of_machine = name_of_FSM; - } - - void setNameOfFirstState(string first_state) /** Set name of start state to an attribute */ - { - name_of_first_state = first_state; - } - - void setOutputFilename(string outputFilename) /** Set name of an output file to an attribute */ - { - this->outputFilename = outputFilename; - } - - bool test_start(const string *sState, const string state, const int num) - { - for(int i = 0; i nstates = states; - context = name_of_machine; - table[0] = "S"; - table[1] = "Context"; - table[2] = "State"; - cnt = count(name_of_first_state,','); - sState = new string [cnt+1]; - str = name_of_first_state; - if(cnt>0) str = get_params(str); - num_start = cnt+1; - for(int i = 0;i<=cnt;i++) - { - if(i == cnt) sState[i] = str; - else - { - sState[i] = str.substr(0,str.find(',')); - str = str.substr(str.find(',')+1); - } - } - for(list::iterator i = nstates.begin();i!=nstates.end();i++) // write all states in the context of the automaton - { - state = *i; - cnt = count(state,','); - if(count(state,'>')==1) //ortho states - { - orto = 1; - if(cnt>1)cnt = 2; - } - if(cnt==1) - { - pos1 = state.find(","); - if(orto == 1) ctx = cut_namespaces(cut_namespaces(state.substr(pos1+1),1)); - else ctx = cut_namespaces(state.substr(pos1+1)); - if(ctx.compare(0,context.length(),context)==0 && context.length()==ctx.length()) - { - str = cut_namespaces(state.substr(0,pos1)); - if(test_start(sState,str,num_start)) - { - filestr<0) str = get_params(str); - num_start = cnt+1; - for(int i = 0;i<=cnt;i++) - { - if(i == cnt) sState[i] = str; - else - { - sState[i] = str.substr(0,str.find(',')); - str = str.substr(str.find(',')+1); - } - } - nstates.pop_front(); - for(list::iterator i = nstates.begin();i!=nstates.end();i++) - { - state = *i; - cnt = count(state,','); - if(count(state,'>')==1) //ortho states - { - orto = 1; - if(cnt>1)cnt = 2; - } - if(cnt==1) - { - pos1 = state.find(","); - if(orto == 1) ctx = cut_namespaces(cut_namespaces(state.substr(pos1+1),1)); - else ctx = cut_namespaces(state.substr(pos1+1)); - if(ctx.compare(0,context.length(),context)==0 && context.length()==ctx.length()) - { - str = cut_namespaces(state.substr(0,pos1)); - if(test_start(sState,str,num_start)) - { - filestr<::iterator i = transitions.begin();i!=transitions.end();i++) // write all transitions - { - params = *i; - if(count(params,',')==2) - { - pos1 = params.find(","); - if(pos1==0) - { - dest = "(deferred)"; - pos2 = params.rfind(","); - event = cut_namespaces(params.substr(pos2+1)); - state = cut_namespaces(params.substr(1,pos2-1)); - } - else - { - state = cut_namespaces(params.substr(0,pos1)); - filestr<"; - pos2 = params.rfind(","); - dest = cut_namespaces(params.substr(pos2+1)); - filestr<::iterator i = events.begin();i!=events.end();i++) - { - table[j] = *i; - j++; - } - } - - void save_to_file() /** Create output file stream and write there the name of state machine. It controls the whole process of writing into output files. */ - { - if(!name_of_first_state.empty()) - { - ofstream filestr(outputFilename.c_str()); - filestr<<"digraph "<< name_of_machine<< " {\n"; - cols = events.size()+3; - rows = states.size()+1; - table = new string [cols*rows]; - fill_table_with_events(); - if(!write_states(filestr)) - { - cerr<<"Error during writing states.\n"; - filestr<<"}"; - filestr.close(); - return; - } - write_transitions(filestr); - filestr<<"}"; - filestr.close(); - // call write_reactions(); - print_table(); - } - else cout<<"No state machine was found. So no output file was created.\n"; - return; - } - - void print_table() /** This function prints the transition table. At first it counts the size of all columns. */ - { - cout<<"\nTRANSITION TABLE\n"; - unsigned * len = new unsigned[cols]; - len[0] = 1; - int nbr, multiline; - string line = "-|---|-", str; - for(int i = 1; i1) - { - str = table[j*cols+i]; - for(int k = 1; k0) - { - cout<1) - { - table[i*cols+j] = str.substr(str.find(",")+1); - multiline = 1; - } - else table[i*cols+j]=""; - - } - else - { - cout<. -// -//////////////////////////////////////////////////////////////////////////////////////// - -#include - -using namespace std; -using namespace clang; - -string clean_spaces(const string line) /** Remove gaps from string.*/ -{ - unsigned i; - string new_line = ""; - for(i = 0;i0;i--) - { - if(line[i]=='<') brackets+=1; - if(line[i]=='>') brackets-=1; - if(line[i]==':' && line[i-1]==':') - { - if(brackets == 0) break; - else i--; - } - } - if(i==0) return line; - return line.substr(i+1); -} - -string cut_namespaces(string line, int ortho) /** Cut namespaces ORTHOGONAL STATES. */ -{ - int i; - int brackets = 0; - for(i = line.length()-1;i>0;i--) - { - if(line[i]=='<') brackets+=1; - if(line[i]=='>') brackets-=1; - if(line[i]==':' && line[i-1]==':') - { - if(brackets == 0) break; - else i--; - } - } - if(i==0) return line; - return line.substr(0,i-1); -} - -bool is_derived(const string line) /** Test whether this struct or class is derived */ -{ - unsigned i; - for(i = 0;i') brackets-=1; - if(line[i]==',' && brackets == 0) break; - } - return line.substr(i+1); -} - -string get_first_base(const string line) /** Get the first super class of this declarations. */ -{ - unsigned i; - int brackets = 0; - for(i = 0;i') brackets-=1; - if(line[i]==',' && brackets == 0) break; - } - return line.substr(0,i); -} - -bool is_state(const string line) /** Test if this declaration is a state. It is used to test the base classes. */ -{ - if(cut_namespaces(line).compare(0,12,"simple_state")==0) - { - return true; - } - else - { - if(cut_namespaces(line).compare(0,5,"state")==0) - { - return true; - } - return false; - } -} - -string cut_type(string line) /** This function cuts type of the declaration from the string. */ -{ - unsigned i; - for(i = 0;i') - { - if(pos==0) break; - else pos-=1; - } - } - return str.substr(0,i); -} - -int get_model(const string line) /** Test the string to has a specified model. */ -{ - string str = cut_namespaces(line); - if(str.find("<")"); - return line.substr(pos_front,pos_end-pos_front); -} - -string find_states(const CXXRecordDecl *cRecDecl, string line) /** test if the struct/class is he state (must be derived from simple_state or state). */ -{ - string super_class = get_super_class(line), base, params; - if(cRecDecl->getNumBases()>1) - { - - for(unsigned i = 0; igetNumBases();i++ ) - { - if(i!=cRecDecl->getNumBases()-1) base = get_first_base(super_class); - else base = super_class; - if(is_state(super_class)) - { - params = get_params(super_class); - } - else - { - super_class = get_next_base(super_class); - } - } - } - else - { - if(is_state(super_class)) - { - params = get_params(super_class); - } - else params = ""; - } - return params; -} - -string find_name_of_machine(const CXXRecordDecl *cRecDecl, string line) /** Find name of the state machine and the start state. */ -{ - string super_class = get_super_class(line), base, params; - if(cRecDecl->getNumBases()>1) - { - for(unsigned i = 0; igetNumBases();i++ ) - { - if(i!=cRecDecl->getNumBases()-1) base = get_first_base(super_class); - else base = super_class; - if(get_model(base)==3) - { - params = get_params(base); - } - else - { - super_class = get_next_base(super_class); - } - } - } - else - { - if(get_model(super_class)==3) - { - params = get_params(super_class); - } - } - return params; -} - -string find_transitions (const string name_of_state, string line) /** Traverse all methods for finding declarations of transitions. */ -{ - string dest, params, base, trans; - int num = count(line,'<'); - if(num>1) - { - num-=1; - if(is_list(line)) - { - line = get_inner_part(line); - } - } - for(int j = 0;j10) - { - if(get_model(base)==12) dest = ";"; - else if (get_model(base) == 13) dest = ","; - else dest = ""; - dest.append(name_of_state); - params = get_params(base); - dest.append(","); - dest.append(params); - trans.append(dest); - if(j!=num-1) trans.append(";"); - } - line = get_next_base(line); - } - if(trans[trans.length()-1]==';') return trans.substr(0,trans.length()-1); - else return trans; -} - -bool find_events(const CXXRecordDecl *cRecDecl, string line) /** This function provides testing if the decl is decl of event*/ -{ - string super_class = get_super_class(line), base, params; - if(cRecDecl->getNumBases()>1) - { - for(unsigned i = 0; igetNumBases();i++ ) - { - if(i!=cRecDecl->getNumBases()-1) base = get_first_base(super_class); - else base = super_class; - if(get_model(base)==1) return true; - else - { - super_class = get_next_base(super_class); - } - } - } - else - { - if(get_model(super_class)==1)return true; - } - return false; -} diff --git a/src/visualizer.cpp b/src/visualizer.cpp index 7112611..20105b6 100644 --- a/src/visualizer.cpp +++ b/src/visualizer.cpp @@ -19,428 +19,180 @@ //////////////////////////////////////////////////////////////////////////////////////// //standard header files -#include +#include //LLVM Header files #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Host.h" -#include "llvm/Config/config.h" +#include "llvm/Support/raw_os_ostream.h" //clang header files -#include "clang/Frontend/TextDiagnosticPrinter.h" -#include "clang/Lex/HeaderSearch.h" -#include "clang/Basic/FileManager.h" -#include "clang/Frontend/Utils.h" -#include "clang/Basic/TargetInfo.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/AST/ASTConsumer.h" -#include "clang/Sema/Lookup.h" -#include "clang/Parse/ParseAST.h" -#include "clang/Basic/Version.h" -#include "clang/Driver/Driver.h" -#include "clang/Driver/Compilation.h" - -#include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/AST/ASTConsumer.h" -#include "clang/AST/AST.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/Frontend/CompilerInstance.h" -#include "llvm/Support/raw_ostream.h" - -//my own header files -#include "iooper.h" +#include "clang/Frontend/FrontendPluginRegistry.h" using namespace clang; -using namespace clang::driver; using namespace std; -/** - * This class provides Simple diagnostic Client. It uses implementation in library for printing diagnostci information. - * Also it counts number of warnings, errors, ... When an error occurs the program is stopped. - */ -class MyDiagnosticClient : public TextDiagnosticPrinter +class Statechart { - int nwarnings; /** Save number of Warnings occured during diagnostic */ - int nnotes; - int nignored; - int nerrors; - public: - /** - * Initialize number of warnings, errors, ... - */ - MyDiagnosticClient(llvm::raw_ostream &os, const DiagnosticOptions &diags, bool OwnsOutputStream = false):TextDiagnosticPrinter(os, diags, OwnsOutputStream = false) - { - nwarnings=0; - nnotes=0; - nignored=0; - nerrors = 0; - } - /** - * This method prints diagnostic and counts diagnostic types. - */ - virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) - { - TextDiagnosticPrinter::HandleDiagnostic(DiagLevel, Info); // print diagnostic information using library implementation - switch (DiagLevel) // count number of all diagnostic information - { - case 0 : nignored+=1; break; - case 1 : nnotes+=1; break; - case 2 : nwarnings+=1; break; - default : nerrors+=1; - print_stats(); - exit(1); - } - } - /** - * Print statistics about diagnostic. - */ - void print_stats() - { - cout<<"\n--Diagnostic Info--\n"; - cout<<"Number of ignored: "< transitions; + list cReactions; /** list of custom reactions. After all files are traversed this list should be empty. */ + list events; + list states; + + void write_dot_file(string fn) + { + ofstream f(fn.c_str()); + f << "digraph " << name << " {\n"; + for (string& s : states) { + f << " " << s << "\n"; + } + + for (Transition &t : transitions) { + f << t.src << " -> " << t.dst << " [label = \"" << t.event << "\"]\n"; + } + + f << "}"; + } }; -/** - * My ASTConsumer provides interface for traversing AST. It uses recursive traversing in namespaces. - */ -class FindStates : public ASTConsumer -{ - list transitions; - list cReactions; /** list of custom reactions. After all files are traversed this list should be empty. */ - list events; - list states; - string name_of_machine; - string name_of_start; - string dest; - FullSourceLoc *fsloc; /** Full Source Location instance for holding Source Manager. */ - public: - - FindStates(string dest) : dest(dest) {} - - list getStates() /** Return list of states of the state machine. */ - { - return states; - } - list getTransitions() /** Return list of transitions. */ - { - return transitions; - } +class MyCXXRecordDecl : public CXXRecordDecl +{ + static bool FindBaseClassString(const CXXBaseSpecifier *Specifier, + CXXBasePath &Path, + void *qualName) + { + string qn(static_cast(qualName)); + const RecordType *rt = Specifier->getType()->getAs(); + assert(rt); + TagDecl *canon = rt->getDecl()->getCanonicalDecl(); + return canon->getQualifiedNameAsString() == qn; + } - list getEvents() /** Return list of events. */ - { - return events; - } +public: + bool isDerivedFrom(const char *baseStr) const { + CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/false, /*DetectVirtual=*/false); + Paths.setOrigin(const_cast(this)); + return lookupInBases(&FindBaseClassString, const_cast(baseStr), Paths); + } +}; - string getStateMachine() /** Return name of the state machine. */ - { - return name_of_machine; - } - string getNameOfFirstState() /** Return name of start state. */ - { - return name_of_start; +class Visitor : public RecursiveASTVisitor +{ + ASTContext *Context; + Statechart ≻ +public: + bool shouldVisitTemplateInstantiations() const { return true; } + + explicit Visitor(ASTContext *Context, Statechart &sc) + : Context(Context), sc(sc) {} + + void HandleReaction(const Type *T, CXXRecordDecl *SrcState) + { + if (const ElaboratedType *ET = dyn_cast(T)) + HandleReaction(ET->getNamedType().getTypePtr(), SrcState); + else if (const TemplateSpecializationType *TST = dyn_cast(T)) { + string name = TST->getTemplateName().getAsTemplateDecl()->getQualifiedNameAsString(); + if (name == "boost::statechart::transition") { + const Type *EventType = TST->getArg(0).getAsType().getTypePtr(); + const Type *DstStateType = TST->getArg(1).getAsType().getTypePtr(); + CXXRecordDecl *Event = EventType->getAsCXXRecordDecl(); + CXXRecordDecl *DstState = DstStateType->getAsCXXRecordDecl(); + + sc.transitions.push_back(Statechart::Transition(SrcState->getName(), DstState->getName(), + Event->getName())); + //llvm::errs() << EventRec->getName() << << "\n"; + } else if (name == "boost::mpl::list") { + for (TemplateSpecializationType::iterator Arg = TST->begin(), End = TST->end(); Arg != End; ++Arg) + HandleReaction(Arg->getAsType().getTypePtr(), SrcState); + } + //->getDecl()->getQualifiedNameAsString(); + } else { + llvm::errs() << "Unhandled reaction Type: " << T->getTypeClassName() << "\n"; + assert(0); } + } - virtual void Initialize(ASTContext &ctx)/** Run after the AST is constructed before the consumer starts to work. So this function works like constructor. */ - { - fsloc = new FullSourceLoc(* new SourceLocation(), ctx.getSourceManager()); - name_of_start = ""; - name_of_machine = ""; + void HandleReaction(const NamedDecl *Decl, CXXRecordDecl *SrcState) + { + if (const TypedefDecl *r = dyn_cast(Decl)) + HandleReaction(r->getCanonicalDecl()->getUnderlyingType().getTypePtr(), SrcState); + else { + llvm::errs() << "Unhandled reaction Decl: " << Decl->getDeclKindName() << "\n"; + assert(0); } + } -/** -* Traverse global decls using DeclGroupRef for handling all global decls. But only interesting decls are processed. Interesting decls are Struct, Class, C++ methods and Namespace. -* When Namespace is found it recursively traverse all decls inside this Namespace using method recursive_visit. -*/ - virtual bool HandleTopLevelDecl(DeclGroupRef DGR) - { - SourceLocation loc; - string line, output, event; - llvm::raw_string_ostream x(output); - for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i) - { - const Decl *decl = *i; - loc = decl->getLocation(); - if(loc.isValid()) - { - if(dyn_cast(decl)) - { - method_decl(decl); - } - if (const TagDecl *tagDecl = dyn_cast(decl)) - { - if(tagDecl->isStruct() || tagDecl->isClass()) //is it a struct or class - { - struct_class(decl); - } - } - if(const NamespaceDecl *namespaceDecl = dyn_cast(decl)) - { - - DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl); - recursive_visit(declCont); - - } - } - output = ""; - } - return true; - } -/** -* It is used to recursive traverse decls in Namespaces. This method do the same as HandleTopLevelDecl. -*/ - void recursive_visit(const DeclContext *declCont) - { - string line, output, event; - llvm::raw_string_ostream x(output); - SourceLocation loc; - for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i) - { - const Decl *decl = *i; - loc = decl->getLocation(); - if(loc.isValid()) - { - if(dyn_cast(decl)) - { - method_decl(decl); - } - else if (const TagDecl *tagDecl = dyn_cast(decl)) - { - if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class - { - struct_class(decl); - } - } - else if(const NamespaceDecl *namespaceDecl = dyn_cast(decl)) - { - DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl); - recursive_visit(declCont); - } - } - output = ""; - } - } + bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) + { + if (!Declaration->isCompleteDefinition()) + return true; -/** -* This function works with class or struct. It splits the decl into 3 interesting parts. -* The state machine decl, state decl and event decl. -*/ - void struct_class(const Decl *decl) - { - string output, line, ret, trans, event; - llvm::raw_string_ostream x(output); - decl->print(x); - line = get_line_of_code(x.str()); - - output = ""; - int pos; - const NamedDecl *namedDecl = dyn_cast(decl); - if(is_derived(line)) - { - const CXXRecordDecl *cRecDecl = dyn_cast(decl); - - if(find_events(cRecDecl, line)) - { - events.push_back(namedDecl->getNameAsString()); - } - else if(name_of_machine == "") - { - ret = find_name_of_machine(cRecDecl, line); - if(!ret.empty()) - { - pos = ret.find(","); - name_of_machine = ret.substr(0,pos); - name_of_start = ret.substr(pos+1); - } - } - else - { - ret = find_states(cRecDecl, line); - if(!ret.empty()) - { - states.push_back(ret); - methods_in_class(decl,namedDecl->getNameAsString()); - } - } - } - } + MyCXXRecordDecl *StateDecl = static_cast(Declaration); -/** -* This function provides traversing all methods and other context indide class. If -* typedef or classic method decl is found. If typedef is found then it is being testted for transitions and custom reactions. -*/ - void methods_in_class(const Decl *decl, const string state) + if (StateDecl->isDerivedFrom("boost::statechart::simple_state")) { - string output, line, ret, trans, event; - llvm::raw_string_ostream x(output); - int pos, num; - const TagDecl *tagDecl = dyn_cast(decl); - const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl); - output=""; - std::cout<<"Found state: "<decls_begin(), e = declCont->decls_end(); i != e; ++i) - { - if (i->getKind()==26) // typedefs - { - i->print(x); - output = x.str(); - line = clean_spaces(cut_type(output)); - ret = find_transitions(state,line); - if(!ret.empty()) - { - num = count(ret,';')+1; - for(int i = 0;i(*i)) method_decl(*i);// C++ method - } + string state(StateDecl->getName()); //getQualifiedNameAsString()); + llvm::errs() << "Found state " << state << "\n"; + sc.states.push_back(state); + IdentifierInfo& II = Context->Idents.get("reactions"); + for (DeclContext::lookup_result Reactions = StateDecl->lookup(DeclarationName(&II)); + Reactions.first != Reactions.second; ++Reactions.first) + HandleReaction(*Reactions.first, StateDecl); } - - /** - * Traverse method declaration using classes with main class Stmt. - */ - void method_decl(const Decl *decl) + else if (StateDecl->isDerivedFrom("boost::statechart::state_machine")) { - const FunctionDecl *fDecl = dyn_cast(decl); - string output, line, event; - llvm::raw_string_ostream x(output); - - if(fDecl->hasBody() && fDecl->getNumParams() > 0) - { - decl->print(x); - line = get_return(x.str()); - if(get_model(line)==5) - { - //std::cout<<"metodass"<getParamDecl(0); - QualType qt = pvd->getOriginalType(); - event = qt.getAsString(); - if(event[event.length()-1]=='&') event = event.substr(0,event.length()-2); - event = event.substr(event.rfind(" ")+1); - line = dyn_cast(decl)->getQualifiedNameAsString(); - line = cut_namespaces(line.substr(0,line.rfind("::"))); - line.append(","); - line.append(event); - find_return_stmt(decl->getBody(),line); - for(list::iterator i = cReactions.begin();i!=cReactions.end();i++) // erase info about it from list of custom reactions - { - event = *i; - if(line.compare(event)==0) - { - cReactions.erase(i); - break; - } - } - } - } + sc.name = StateDecl->getQualifiedNameAsString(); + sc.name_of_start = "tmp"; //StateDecl->getStateMachineInitialStateAsString() + llvm::errs() << "Found state_machine " << sc.name << "\n"; } - - void find_return_stmt(Stmt *statemt,string event) /** Traverse all statements in function for finding all return Statements.*/ + else if (StateDecl->isDerivedFrom("boost::statechart::event")) { - if(statemt->getStmtClass() == 99) test_stmt(dyn_cast(statemt)->getSubStmt(), event); - else - { - for (Stmt::child_range range = statemt->children(); range; ++range) - { - test_stmt(*range, event); - } - } + sc.events.push_back(StateDecl->getNameAsString()); } + return true; + } +}; - void test_stmt(Stmt *stmt, string event) /** test statement for its kind Using number as identifier for all Statement Classes.*/ - { - const SourceManager &sman = fsloc->getManager(); - int type; - string line, param; - type = stmt->getStmtClass(); - switch(type) - { - case 8 : find_return_stmt(dyn_cast(stmt)->getBody(), event); // do - break; - case 86 : find_return_stmt(dyn_cast(stmt)->getBody(), event); // for - break; - case 88 : find_return_stmt(dyn_cast(stmt)->getThen(), event); //if then - find_return_stmt(dyn_cast(stmt)->getElse(), event); //if else - break; - case 90 : find_return_stmt(dyn_cast(stmt)->getSubStmt(), event); //label - break; - case 98 : line = sman.getCharacterData(dyn_cast(stmt)->getReturnLoc()); - line = get_line_of_code(line).substr(6); - line = line.substr(0,line.find("(")); - if(get_model(line)==6) - { - param = get_params(line); - transitions.push_back(event.append(",").append(param)); - } - if(get_model(line) == 7) - { - param = ","; - transitions.push_back(param.append(event)); - } - break; - case 99 : find_return_stmt(stmt, event); - break; - case 101 : find_return_stmt(dyn_cast(stmt)->getBody(), event); // switch - break; - case 102 : find_return_stmt(dyn_cast(stmt)->getBody(), event); // while - break; - } - } - virtual void HandleTranslationUnit(clang::ASTContext &Context) { - IO_operations io(dest, getStateMachine(), getNameOfFirstState(), getTransitions(), getStates(), getEvents()); - io.save_to_file(); - io.print_stats(); - } +class VisualizeStatechartConsumer : public clang::ASTConsumer +{ + Statechart statechart; + Visitor visitor; + string destFileName; +public: + explicit VisualizeStatechartConsumer(ASTContext *Context, std::string destFileName) + : visitor(Context, statechart), destFileName(destFileName) {} + + virtual void HandleTranslationUnit(clang::ASTContext &Context) { + visitor.TraverseDecl(Context.getTranslationUnitDecl()); + statechart.write_dot_file(destFileName); + } }; -class VisualizeStatechartAction : public PluginASTAction { +class VisualizeStatechartAction : public PluginASTAction +{ protected: ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef) { size_t dot = getCurrentFile().find_last_of('.'); std::string dest = getCurrentFile().substr(0, dot); dest.append(".dot"); - return new FindStates(dest); + return new VisualizeStatechartConsumer(&CI.getASTContext(), dest); } bool ParseArgs(const CompilerInstance &CI, @@ -469,3 +221,7 @@ protected: }; static FrontendPluginRegistry::Add X("visualize-statechart", "visualize statechart"); + +// Local Variables: +// c-basic-offset: 4 +// End: