]> rtime.felk.cvut.cz Git - boost-statechart-viewer.git/blob - src/visualizer.cpp
Move the creation of output file into new class and file. Create methods set and...
[boost-statechart-viewer.git] / src / visualizer.cpp
1
2 ////////////////////////////////////////////////////////////////////////////////////////  
3 //    
4 //    This file is part of Boost Statechart Viewer.
5 //
6 //    Boost Statechart Viewer is free software: you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation, either version 3 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Boost Statechart Viewer is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License
17 //    along with Boost Statechart Viewer.  If not, see <http://www.gnu.org/licenses/>.
18 //
19 ////////////////////////////////////////////////////////////////////////////////////////
20
21 //standard header files
22 #include <iostream>
23
24 //LLVM Header files
25 #include "llvm/Support/raw_ostream.h"
26 #include "llvm/Support/Host.h"
27 #include "llvm/Config/config.h"
28
29 //clang header files
30 #include "clang/Frontend/TextDiagnosticPrinter.h"
31 #include "clang/Lex/HeaderSearch.h"
32 #include "clang/Basic/FileManager.h"
33 #include "clang/Frontend/Utils.h"
34 #include "clang/Basic/TargetInfo.h"
35 #include "clang/Lex/Preprocessor.h"
36 #include "clang/Frontend/CompilerInstance.h"
37 #include "clang/AST/ASTConsumer.h"
38 #include "clang/Sema/Lookup.h"
39 #include "clang/Parse/ParseAST.h"
40 #include "clang/Basic/Version.h"
41 #include "clang/Driver/Driver.h"
42 #include "clang/Driver/Compilation.h"
43
44 //my own header files
45 #include "iooper.h"
46
47 using namespace clang;
48 using namespace clang::driver;
49 using namespace std;
50
51 class MyDiagnosticClient : public TextDiagnosticPrinter // My diagnostic Client
52 {
53         public:
54         MyDiagnosticClient(llvm::raw_ostream &os, const DiagnosticOptions &diags, bool OwnsOutputStream = false):TextDiagnosticPrinter(os, diags, OwnsOutputStream = false){}
55         virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, const DiagnosticInfo &Info)
56         {
57                 TextDiagnosticPrinter::HandleDiagnostic(DiagLevel, Info); // print diagnostic information
58                 if(DiagLevel > 2) // if error/fatal error stop the program
59                 {               
60                         exit(1);
61                 }       
62         }
63 };
64
65 class FindStates : public ASTConsumer
66 {
67         list<string> transitions;
68         list<string> cReactions;
69         list<string> events;
70         list<string> states;
71         string name_of_machine;
72         string name_of_start;
73         FullSourceLoc *fsloc;
74         
75         public:
76
77         list<string> getStates()
78         {
79                 return states;
80         }
81         
82         list<string> getTransitions()
83         {
84                 return transitions;
85         }
86                 
87         list<string> getEvents()
88         {
89                 return events;
90         }
91
92         string getStateMachine()
93         {
94                 return name_of_machine;
95         }
96
97         string getNameOfFirstState()
98         {
99                 return name_of_start;
100         }
101         
102         virtual void Initialize(ASTContext &ctx)//run after the AST is constructed before the consumer starts to work
103         {       
104                 fsloc = new FullSourceLoc(* new SourceLocation(), ctx.getSourceManager());
105                 name_of_start = "";
106                 name_of_machine = "";
107         }
108
109         virtual void HandleTopLevelDecl(DeclGroupRef DGR)// traverse all top level declarations
110         {
111                 SourceLocation loc;
112       string line, output, event;
113                 llvm::raw_string_ostream x(output);
114                 for (DeclGroupRef::iterator i = DGR.begin(), e = DGR.end(); i != e; ++i) 
115                 {
116                         const Decl *decl = *i;
117                         loc = decl->getLocation();
118                         if(loc.isValid())
119                         {
120                                 //cout<<decl->getKind()<<"ss\n";
121                                 if(decl->getKind()==35)
122                                 {                                       
123                                         method_decl(decl);
124                                 }
125                                 if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
126                                 {
127                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a struct or class 
128                                         {
129                                                 struct_class(decl);
130                                         }
131                                 }       
132                                 if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
133                                 {
134                                         
135                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
136                                         //cout<<namedDecl->getNameAsString()<<"   sss\n";
137                                         recursive_visit(declCont);
138                                 
139                                 }
140                         }
141                         output = "";
142                 }
143         }
144         void recursive_visit(const DeclContext *declCont) //recursively visit all decls hidden inside namespaces
145         {
146       string line, output, event;
147                 llvm::raw_string_ostream x(output);
148                 SourceLocation loc;
149                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i)
150                 {
151                         const Decl *decl = *i;
152                         //std::cout<<"a "<<decl->getDeclKindName()<<"\n";
153                         loc = decl->getLocation();
154                         if(loc.isValid())
155                         {       
156                                 if(decl->getKind()==35)
157                                 {
158                                         method_decl(decl);
159                                 }
160                                 else if (const TagDecl *tagDecl = dyn_cast<TagDecl>(decl))
161                                 {
162                                         if(tagDecl->isStruct() || tagDecl->isClass()) //is it a structure or class      
163                                         {
164                                                 struct_class(decl);
165                                         }       
166                                 }
167                                 else if(const NamespaceDecl *namespaceDecl = dyn_cast<NamespaceDecl>(decl))
168                                 {
169                                         DeclContext *declCont = namespaceDecl->castToDeclContext(namespaceDecl);
170                                         //cout<<namedDecl->getNameAsString()<<"  sss\n";                        
171                                         recursive_visit(declCont);
172                                 }
173                         }
174                         output = "";
175                 } 
176         }
177                 
178         void struct_class(const Decl *decl) // works with struct or class decl
179         {
180                 string output, line, ret, trans, event; 
181                 llvm::raw_string_ostream x(output);
182                 decl->print(x);
183                 line = get_line_of_code(x.str());
184                 output = "";
185                 int pos;
186                 const NamedDecl *namedDecl = dyn_cast<NamedDecl>(decl);         
187                 if(is_derived(line))
188                 {
189                         const CXXRecordDecl *cRecDecl = dyn_cast<CXXRecordDecl>(decl);
190                                         
191                         if(find_events(cRecDecl, line))
192                         {
193                                 events.push_back(namedDecl->getNameAsString());
194                                 cout<<"New event: "<<namedDecl->getNameAsString()<<"\n";
195                         }
196                         else if(name_of_machine == "")
197                         {
198                                 ret = find_name_of_machine(cRecDecl, line);
199                                 if(!ret.empty())
200                                 {
201                                         pos = ret.find(",");
202                                         name_of_machine = ret.substr(0,pos);
203                                         name_of_start = ret.substr(pos+1);
204                                         cout<<"Name of the state machine: "<<name_of_machine<<"\n";
205                                         cout<<"Name of the first state: "<<name_of_start<<"\n";
206                                 }
207                         }
208                         else
209                         {
210                                 ret = find_states(cRecDecl, line);      
211                                 if(!ret.empty())
212                                 {       
213                                         cout << "New state: " << namedDecl->getNameAsString() << "\n";
214                                         states.push_back(ret);                  
215                                         methods_in_class(decl,namedDecl->getNameAsString());
216                                 }
217                         }
218                 }
219         }
220
221         void methods_in_class(const Decl *decl, const string state)
222         {
223                 string output, line, ret, trans, event; 
224                 llvm::raw_string_ostream x(output);
225                 int pos, num;
226                 const TagDecl *tagDecl = dyn_cast<TagDecl>(decl);
227                 const DeclContext *declCont = tagDecl->castToDeclContext(tagDecl);              
228                 //states.push_back(namedDecl->getNameAsString());
229                 
230                 output="";
231                 for (DeclContext::decl_iterator i = declCont->decls_begin(), e = declCont->decls_end(); i != e; ++i) 
232                 {
233                         if (i->getKind()==26) 
234                         {
235                                 i->print(x);
236                                 output = x.str();
237                                 line = clean_spaces(cut_type(output));          
238                                 ret = find_transitions(state,line);
239                                 if(!ret.empty()) 
240                                 {
241                                         num = count(ret,';')+1;
242                                         for(int i = 0;i<num;i++)
243                                         {
244                                                 pos = ret.find(";");
245                                                 if(pos == 0)
246                                                 {
247                                                         ret = ret.substr(1);
248                                                         pos = ret.find(";");
249                                                         if(pos==-1) cReactions.push_back(ret);
250                                                         else cReactions.push_back(ret.substr(0,pos));   
251                                                         num-=1;
252                                                 }
253                                                 else 
254                                                 {
255                                                         if(pos==-1) transitions.push_back(ret);
256                                                         else transitions.push_back(ret.substr(0,pos));
257                                                 }
258                                                 //cout<<ret<<"\n";
259                                                 if(i!=num-1) ret = ret.substr(pos+1);
260                                         }
261                                         output="";
262                                 }
263                         }
264                         if(i->getKind()==35) method_decl(decl);
265                 }
266         }
267
268         void method_decl(const Decl *decl)
269         {
270                 string output, line, event;     
271                 llvm::raw_string_ostream x(output);
272                 if(decl->hasBody())
273                 {
274                         decl->print(x);
275                         line = get_return(x.str());
276                         if(test_model(line,"result"))
277                         {
278                                 const FunctionDecl *fDecl = dyn_cast<FunctionDecl>(decl);
279                                 const ParmVarDecl *pvd = fDecl->getParamDecl(0);
280                                 QualType qt = pvd->getOriginalType();                           
281                                 event = qt.getAsString();
282                                 if(event[event.length()-1]=='&') event = event.substr(0,event.length()-2);
283                                 event = event.substr(event.rfind(" ")+1);
284                                 line = dyn_cast<NamedDecl>(decl)->getQualifiedNameAsString();
285                                 line = cut_namespaces(line.substr(0,line.rfind("::")));
286                                 line.append(",");
287                                 line.append(event);
288                                 find_return_stmt(decl->getBody(),line); 
289                                 for(list<string>::iterator i = cReactions.begin();i!=cReactions.end();i++)
290                                 {
291                                         event = *i;
292                                         if(line.compare(event)==0) 
293                                         {
294                                                 cReactions.erase(i);
295                                                 break;
296                                         }
297                                 }
298                         }
299                 }
300         }
301
302         void find_return_stmt(Stmt *statemt,string event)
303         {
304                 if(statemt->getStmtClass() == 99) test_stmt(dyn_cast<CaseStmt>(statemt)->getSubStmt(), event);
305                 else
306                 {
307                         for (Stmt::child_range range = statemt->children(); range; ++range)    
308                         {
309                                 test_stmt(*range, event);
310                         }
311                 }
312         }
313         
314         void test_stmt(Stmt *stmt, string event)
315         {
316                 const SourceManager &sman = fsloc->getManager();
317                 int type;
318                 string line, param;
319                 type = stmt->getStmtClass();
320                 switch(type)
321                 {       
322                         case 8 :                find_return_stmt(dyn_cast<DoStmt>(stmt)->getBody(), event); // do
323                                                         break;
324                         case 86 :       find_return_stmt(dyn_cast<ForStmt>(stmt)->getBody(), event); // for
325                                                         break;
326                         case 88 :   find_return_stmt(dyn_cast<IfStmt>(stmt)->getThen(), event); //if then
327                                                         find_return_stmt(dyn_cast<IfStmt>(stmt)->getElse(), event); //if else
328                                                         break;
329                         case 90 :       find_return_stmt(dyn_cast<LabelStmt>(stmt)->getSubStmt(), event); //label
330                                                         break;
331                         case 98 :       line = sman.getCharacterData(dyn_cast<ReturnStmt>(stmt)->getReturnLoc()); 
332                                                         line = get_line_of_code(line).substr(6);
333                                                         line = line.substr(0,line.find("("));
334                                                         if(test_model(line,"transit"))
335                                                         {
336                                                                 param = get_params(line);
337                                                                 transitions.push_back(event.append(",").append(param));
338                                                         }
339                                                         break;
340                         case 99 :       find_return_stmt(stmt, event);
341                                                         break;
342                         case 101 :      find_return_stmt(dyn_cast<SwitchStmt>(stmt)->getBody(), event); // switch
343                                                         break;
344                         case 102 :      find_return_stmt(dyn_cast<WhileStmt>(stmt)->getBody(), event); // while
345                                                         break;
346                 }
347         }
348
349 };
350
351 int main(int argc, char **argv)
352
353         string inputFilename = "";
354         string outputFilename = "graph.dot"; // initialize output Filename
355         DiagnosticOptions dopts;
356         dopts.ShowColors=1;
357         MyDiagnosticClient *mdc = new MyDiagnosticClient(llvm::errs(), dopts);
358         llvm::IntrusiveRefCntPtr<DiagnosticIDs> dis(new DiagnosticIDs());       
359         Diagnostic diag(dis,mdc);
360         FileManager fm( * new FileSystemOptions());
361         SourceManager sm (diag, fm);
362         HeaderSearch *headers = new HeaderSearch(fm);
363         
364         Driver TheDriver(LLVM_BINDIR, llvm::sys::getHostTriple(), "", false, false, diag);
365         TheDriver.setCheckInputsExist(true);
366         TheDriver.CCCIsCXX = 1; 
367         TheDriver.ResourceDir = LLVM_PREFIX "/lib/clang/" CLANG_VERSION_STRING;
368
369         CompilerInvocation compInv;
370         llvm::SmallVector<const char *, 16> Args(argv, argv + argc);
371         llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args.size(),
372                                                             Args.data()));
373         const driver::JobList &Jobs = C->getJobs();
374         const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
375         const driver::ArgStringList &CCArgs = Cmd->getArguments();
376         for(unsigned i = 0; i<Args.size();i++) // find -o in ArgStringList
377         {       
378                 if(strncmp(Args[i],"-o",2)==0) 
379                 {
380                         if(strlen(Args[i])>2)
381                         {
382                                 string str = Args[i];
383                                 outputFilename = str.substr(2);
384                         }
385                         else outputFilename = Args[i+1];
386                         break;
387                 }
388         }
389                 
390         CompilerInvocation::CreateFromArgs(compInv,
391                                           const_cast<const char **>(CCArgs.data()),
392                                           const_cast<const char **>(CCArgs.data())+CCArgs.size(),
393                                           diag);
394
395         HeaderSearchOptions hsopts = compInv.getHeaderSearchOpts();
396         LangOptions lang = compInv.getLangOpts();
397         CompilerInvocation::setLangDefaults(lang, IK_ObjCXX);
398         TargetInfo *ti = TargetInfo::CreateTargetInfo(diag, compInv.getTargetOpts());
399         FrontendOptions f = compInv.getFrontendOpts();
400         inputFilename = f.Inputs[0].second;
401
402         cout<<"Input filename: "<<inputFilename<<"\n"; // print Input filename
403         cout<<"Output filename: "<<outputFilename<<"\n"; // print Output filename
404
405
406         Preprocessor pp(diag, lang, *ti, sm, *headers);
407         pp.getBuiltinInfo().InitializeBuiltins(pp.getIdentifierTable(), lang);
408                 
409         InitializePreprocessor(pp, compInv.getPreprocessorOpts(),hsopts,f);
410         
411         const FileEntry *file = fm.getFile(inputFilename);
412         sm.createMainFileID(file);
413         IdentifierTable tab(lang);
414         Builtin::Context builtins(*ti);
415         FindStates c;
416         ASTContext ctx(lang, sm, *ti, tab, * new SelectorTable(), builtins,0);
417         mdc->BeginSourceFile(lang, &pp);//start using diagnostic
418         ParseAST(pp, &c, ctx, false, false);
419         mdc->EndSourceFile(); //end using diagnostic
420         IO_operations *io = new IO_operations(outputFilename, c.getStateMachine(), c.getNameOfFirstState(), c.getTransitions(), c.getStates(), c.getEvents());  
421         io->save_to_file();
422         io->print_stats();
423         return 0;
424 }