1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2015 - Scilab Enterprises - Calixte DENIZET
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15 
16 #ifdef _MSC_VER
17 #pragma comment(lib, "../../bin/libxml2.lib")
18 #endif
19 
20 #include <libxml/xmlreader.h>
21 #include <libxml/xpath.h>
22 
23 #include <algorithm>
24 
25 #include "CovHTMLCodePrinter.hxx"
26 #include "CoverModule.hxx"
27 #include "allexp.hxx"
28 #include "alltypes.hxx"
29 #include "allvar.hxx"
30 #include "cover_tools.hxx"
31 #include "coverage_instance.hxx"
32 
33 extern "C"
34 {
35 #include "copyfile.h"
36 #include "createdirectory.h"
37 #include "expandPathVariable.h"
38 #include "findfiles.h"
39 #include "freeArrayOfString.h"
40 #include "isdir.h"
41 #include "os_string.h"
42 #include "sci_malloc.h"
43 }
44 
45 #ifdef _MSC_VER
46 #define DEFAULT_FILESPEC L"*.*"
47 #else
48 #define DEFAULT_FILESPEC L"*"
49 #endif
50 
51 namespace
52 {
expandPathVariable(const std::wstring & p)53 std::wstring expandPathVariable(const std::wstring& p )
54 {
55 
56     wchar_t* localPath = expandPathVariableW((wchar_t*)p.c_str());
57     std::wstring l(localPath);
58     FREE(localPath);
59     return l;
60 }
61 }
62 
63 namespace coverage
64 {
65 
66 CoverModule* CoverModule::instance = nullptr;
67 
CoverModule(const std::vector<std::pair<std::wstring,std::wstring>> & paths_mods)68 CoverModule::CoverModule(const std::vector<std::pair<std::wstring, std::wstring>>& paths_mods) : visitor(*this)
69 {
70     getMacros(paths_mods);
71     getBuiltins(paths_mods);
72     ast::CoverageInstance::setCoverage(this);
73 }
74 
CoverModule(const std::vector<std::wstring> & moduleNames)75 CoverModule::CoverModule(const std::vector<std::wstring>& moduleNames) : CoverModule(getModule(moduleNames))
76 {
77     ast::CoverageInstance::setCoverage(this);
78 }
79 
CoverModule()80 CoverModule::CoverModule() : visitor(*this)
81 {
82     ast::CoverageInstance::setCoverage(this);
83 }
84 
~CoverModule()85 CoverModule::~CoverModule()
86 {
87     for (auto& counter : counters)
88     {
89         counter.getExp()->setCoverId(0);
90     }
91     for (auto& p : callCounters)
92     {
93         p.first->DecreaseRef();
94         //p.first->killMe();
95     }
96 
97     ast::CoverageInstance::setCoverage(nullptr);
98 }
99 
getModule(const std::vector<std::wstring> & moduleNames)100 const std::vector<std::pair<std::wstring, std::wstring>> CoverModule::getModule(const std::vector<std::wstring>& moduleNames)
101 {
102     if (moduleNames.size() == 0)
103     {
104         // nothing to do, return empty
105         return {};
106     }
107 
108     if (std::all_of(moduleNames.begin(), moduleNames.end(),
109                     [](const std::wstring & p)
110 {
111     return isdirW(expandPathVariable(p).c_str()) == TRUE;
112     }))
113     {
114         // all the provided modulesNames are directories, name them and remove "\macros"
115         std::vector<std::pair<std::wstring, std::wstring>> paths;
116 
117         // remove any potential "\macros" suffix
118         for (const auto& name : moduleNames)
119         {
120             const std::wstring remove(std::wstring(DIR_SEPARATORW) + std::wstring(L"macros"));
121             std::wstring localName;
122             size_t n = name.rfind(remove);
123             if (n > 0)
124             {
125                 localName = name.substr(0, n) + name.substr(n + remove.length());
126             }
127             else
128             {
129                 localName = name;
130             }
131             n = localName.rfind(DIR_SEPARATORW);
132             if (n > 0)
133             {
134                 localName = localName.substr(n + 1, localName.length() - n);
135             }
136 
137             paths.emplace_back(expandPathVariable(name), std::move(localName));
138         }
139         return paths;
140     }
141 
142     // Look for the names as Scilab-related libraries
143     const std::wstring _path = std::wstring(L"SCI") + DIR_SEPARATORW + L"modules" + DIR_SEPARATORW;
144     const std::wstring path(expandPathVariable(_path));
145 
146     if (moduleNames.size() == 1 && moduleNames.back() == L"all")
147     {
148         // "all" keyword, parse all the Scilab library files
149         int size = -1;
150         wchar_t** files = findfilesW(path.c_str(), DEFAULT_FILESPEC, &size, FALSE);
151         if (size > 0 && files)
152         {
153             std::vector<std::pair<std::wstring, std::wstring>> paths;
154             for (int i = 0; i < size; ++i)
155             {
156                 const std::wstring modulePath = path + files[i];
157                 if (isdirW(modulePath.c_str()))
158                 {
159                     paths.emplace_back(modulePath, files[i]);
160                 }
161             }
162             freeArrayOfWideString(files, size);
163             return paths;
164         }
165 
166         return {};
167     }
168     else
169     {
170         // a list of Scilab libraries
171         std::vector<std::pair<std::wstring, std::wstring>> paths;
172         for (const auto& name : moduleNames)
173         {
174             paths.emplace_back(path + name, name);
175         }
176         return paths;
177     }
178 }
179 
getMacros(const std::vector<std::pair<std::wstring,std::wstring>> & paths_mods)180 void CoverModule::getMacros(const std::vector<std::pair<std::wstring, std::wstring>>& paths_mods)
181 {
182     for (const auto& p : paths_mods)
183     {
184         getMacrosFromDir(p.first, p.second);
185     }
186 }
187 
getMacrosFromDir(const std::wstring & path,const std::wstring & module)188 void CoverModule::getMacrosFromDir(const std::wstring& path, const std::wstring& module)
189 {
190     const std::wstring resolvedPath(expandPathVariable(path));
191 
192     std::wstring _path = resolvedPath + DIR_SEPARATORW + L"lib";
193     getMacros(_path, module);
194 
195     int size = -1;
196     _path = path + DIR_SEPARATORW;
197 
198     wchar_t** files = findfilesW(_path.c_str(), DEFAULT_FILESPEC, &size, FALSE);
199     if (size > 0 && files)
200     {
201         for (int i = 0; i < size; ++i)
202         {
203             std::wstring _file = _path + files[i];
204             if (isdirW(_file.c_str()))
205             {
206                 getMacrosFromDir(_file, module);
207             }
208         }
209         freeArrayOfWideString(files, size);
210     }
211 }
212 
getMacros(const std::wstring & path,const std::wstring & module)213 void CoverModule::getMacros(const std::wstring& path, const std::wstring& module)
214 {
215     std::unordered_set<std::wstring> _macros;
216     std::wstring libPath(expandPathVariable(path));
217 
218     char* libFile = wide_string_to_UTF8(libPath.c_str());
219 
220     if (getStringFromXPath(libFile, "//scilablib/macro/@name", _macros))
221     {
222         for (const auto& name : _macros)
223         {
224             types::InternalType* pIT = symbol::Context::getInstance()->get(symbol::Symbol(name));
225             if (pIT && pIT->isMacroFile())
226             {
227                 types::MacroFile* pMF = static_cast<types::MacroFile*>(pIT);
228                 if (types::Macro* macro = pMF->getMacro())
229                 {
230                     const std::wstring& file = pMF->getPath();
231                     std::size_t pos = file.find_last_of(L'.');
232                     if (pos != std::string::npos)
233                     {
234                         instrumentMacro(module, file.substr(0, pos) + L".sci", macro);
235                     }
236                     else
237                     {
238                         instrumentMacro(module, file, macro);
239                     }
240                 }
241             }
242         }
243     }
244     FREE(libFile);
245 }
246 
getBuiltins(const std::vector<std::pair<std::wstring,std::wstring>> & paths_mods)247 void CoverModule::getBuiltins(const std::vector<std::pair<std::wstring, std::wstring>>& paths_mods)
248 {
249     for (const auto& p : paths_mods)
250     {
251         std::list<types::Callable*> lst;
252         if (symbol::Context::getInstance()->getFunctionList(lst, p.second))
253         {
254             for (auto pCall : lst)
255             {
256                 if (pCall->isFunction())
257                 {
258                     pCall->IncreaseRef();
259                     functions.emplace(p.second, static_cast<types::Function*>(pCall));
260                     callCounters.emplace(pCall, CallCounter());
261                 }
262             }
263         }
264     }
265 }
266 
instrumentMacro(const std::wstring & module,const std::wstring & path,types::Macro * macro)267 void CoverModule::instrumentMacro(const std::wstring& module, const std::wstring& path, types::Macro* macro)
268 {
269     const std::map<symbol::Variable*, types::Macro*>& submacros = macro->getSubMacros();
270     for (const auto& p : submacros)
271     {
272         instrumentSingleMacro(module, path, p.second, true);
273     }
274 
275     instrumentSingleMacro(module, path, macro, true);
276 }
277 
instrumentSingleMacro(const std::wstring & module,const std::wstring & path,types::Macro * macro,bool instrumentInners)278 void CoverModule::instrumentSingleMacro(const std::wstring& module, const std::wstring& path, types::Macro* macro, bool instrumentInners)
279 {
280     macro->IncreaseRef();
281     visitor.setMacro(macro);
282     macro->getBody()->accept(visitor);
283     macros.emplace(macro, CoverMacroInfo(module, path, visitor.getInstrsCount(), visitor.getBranchesCount(), visitor.getPathsCount()));
284     callCounters.emplace(static_cast<types::Callable*>(macro), CallCounter());
285     functions.emplace(module, static_cast<types::Callable*>(macro));
286 
287     if (instrumentInners)
288     {
289         // We make a copy since the call to instrumentSingleMacro will modify visitor.getInnerMacros()
290         const std::vector<types::Macro*> inners = visitor.getInnerMacros();
291         for (auto inner : inners)
292         {
293             instrumentSingleMacro(module, path, inner, true);
294         }
295     }
296 }
297 
add(types::Macro * macro,ast::Exp * e)298 void CoverModule::add(types::Macro* macro, ast::Exp* e)
299 {
300     if (e)
301     {
302         counters.emplace_back(macro, e);
303         const std::size_t id = counters.size() + 1;
304         e->setCoverId(id);
305     }
306 }
307 
invoke(types::Callable * f)308 void CoverModule::invoke(types::Callable* f)
309 {
310     if (f->isMacroFile())
311     {
312         f = static_cast<types::MacroFile*>(f)->getMacro();
313     }
314     auto i = callCounters.find(f);
315     if (i != callCounters.end())
316     {
317         i->second.inc();
318     }
319 }
320 
invoke(const uint64_t id)321 void CoverModule::invoke(const uint64_t id)
322 {
323     counters[id - 2].inc();
324 }
325 
getStringFromXPath(char * filePath,const char * xpquery,std::unordered_set<std::wstring> & set)326 bool CoverModule::getStringFromXPath(char* filePath, const char* xpquery, std::unordered_set<std::wstring>& set)
327 {
328     xmlDocPtr doc = xmlReadFile(filePath, "utf-8", XML_PARSE_NOWARNING);
329     if (!doc)
330     {
331         return false;
332     }
333 
334     if (!doc->encoding || stricmp((const char*)doc->encoding, "utf-8") != 0)
335     {
336         xmlFreeDoc(doc);
337         return false;
338     }
339 
340     xmlXPathContextPtr ctxt = xmlXPathNewContext(doc);
341     xmlXPathObjectPtr xp = xmlXPathEval((const xmlChar*)xpquery, ctxt);
342     xmlNodeSetPtr nodeSet = xp->nodesetval;
343 
344     if (nodeSet)
345     {
346         for (unsigned int i = 0; i < nodeSet->nodeNr; ++i)
347         {
348             const char* content = (const char*)xmlNodeGetContent(nodeSet->nodeTab[i]);
349             wchar_t* ws = to_wide_string(content);
350             xmlFree(const_cast<char*>(content));
351             set.emplace(ws);
352             FREE(ws);
353         }
354     }
355 
356     xmlXPathFreeObject(xp);
357     xmlXPathFreeContext(ctxt);
358     xmlFreeDoc(doc);
359 
360     return nodeSet;
361 }
362 
lower_bound(const std::vector<Counter>::const_iterator & first,const std::vector<Counter>::const_iterator & last,types::Macro * value)363 std::vector<Counter>::const_iterator CoverModule::lower_bound(const std::vector<Counter>::const_iterator& first, const std::vector<Counter>::const_iterator& last, types::Macro* value)
364 {
365     // binary search could not be performed as the counters are not sorted, linear lookup is OK
366     for (auto it = first; it < last; it++)
367     {
368         if (it->getMacro() == value)
369         {
370             return it;
371         }
372     }
373 
374     return last;
375 }
376 
upper_bound(const std::vector<Counter>::const_iterator & first,const std::vector<Counter>::const_iterator & last,types::Macro * value)377 std::vector<Counter>::const_iterator CoverModule::upper_bound(const std::vector<Counter>::const_iterator& first, const std::vector<Counter>::const_iterator& last, types::Macro* value)
378 {
379     // binary search could not be performed as the counters are not sorted, linear lookup is OK
380     for (auto it = lower_bound(first, last, value); it < last; it++)
381     {
382         if (it->getMacro() != value)
383         {
384             return it;
385         }
386     }
387 
388     return last;
389 }
390 
collect()391 void CoverModule::collect()
392 {
393     if (!counters.empty())
394     {
395         std::vector<Counter>::const_iterator first = counters.begin();
396         std::vector<Counter>::const_iterator last = upper_bound(first, counters.end(), first->getMacro());
397         collect(first, last);
398         while (last != counters.end())
399         {
400             first = last;
401             last = upper_bound(first, counters.end(), first->getMacro());
402             collect(first, last);
403         }
404 
405         for (const auto& p : functions)
406         {
407             if (p.second->isFunction())
408             {
409                 allCounters[p.first][p.second->getName()] = std::pair<bool, uint64_t>(false, callCounters[p.second].get());
410             }
411         }
412     }
413 }
414 
collect(const std::vector<Counter>::const_iterator & first,const std::vector<Counter>::const_iterator & last)415 void CoverModule::collect(const std::vector<Counter>::const_iterator& first, const std::vector<Counter>::const_iterator& last)
416 {
417     types::Macro* current = first->getMacro();
418 
419     const std::wstring& name = current->getName();
420     CoverMacroInfo& info = macros.find(current)->second;
421     const uint64_t counter = callCounters[current].get();
422     auto& map1 = results[info.macroModule];
423     auto& map2 = map1[info.macroFilePath];
424     MacroLoc ml(name, current->getBody()->getLocation());
425     auto j = map2.find(ml);
426     if (j == map2.end())
427     {
428         CoverResult& result = map2.emplace(ml, CoverResult(name, info)).first->second;
429         result.setCounter(counter);
430 
431         result.populate(first, last);
432     }
433     allCounters[info.macroModule][name] = std::pair<bool, uint64_t>(true, counter);
434 }
435 
print()436 void CoverModule::print()
437 {
438     /*std::wcerr << L"Builtin calls" << std::endl << tools::getUnderline(L"Builtin calls") << std::endl;
439       for (const auto & p : callCounters)
440       {
441       if (p.second.get())
442       {
443       std::wcerr << p.first->getName() << L" called " << p.second.get() << L" time" << tools::getPlural(p.second.get()) << L"." << std::endl;
444       }
445       }
446 
447       std::wcerr << std::endl;*/
448 
449     /*      for (const auto & counter : counters)
450             {
451             if (counter.get())
452             {
453 
454             const std::wstring & name = counter.getMacro()->getName();
455             std::wcerr << L"Macro " << name << L" visited at " << counter.getExp()->getLocation() << std::endl;
456 
457 
458             //<< L"Seq not visited:" << std::endl;
459 
460             }
461             }
462             */
463     //collect();
464 }
465 
toHTML(const std::wstring & outputDir)466 void CoverModule::toHTML(const std::wstring& outputDir)
467 {
468     bool nomodules = false;
469     wchar_t* _outputDir = expandPathVariableW((wchar_t*)outputDir.c_str());
470     createdirectoryW(_outputDir);
471 
472     if (results.size() == 1 && results.begin()->first == L"" && results.begin()->second.size() == 1 && results.begin()->second.begin()->first == L"")
473     {
474         for (const auto& p : macros)
475         {
476             writeMacroHTMLReport(p.first, results.begin()->second.begin()->second, _outputDir);
477         }
478         nomodules = true;
479     }
480 
481     if (!nomodules)
482     {
483         std::map<std::wstring, std::pair<uint64_t, double>> modulesStats;
484 
485         // We make all the reports for the macros
486         for (auto& p1 : results)
487         {
488             const std::wstring& moduleName = p1.first;
489             const std::wstring __outputDir = std::wstring(_outputDir) + DIR_SEPARATORW + moduleName;
490             createdirectoryW((wchar_t*)__outputDir.c_str());
491             uint64_t totalCalls = 0;
492             uint64_t totalInstrs = 0;
493             uint64_t uncoveredInstrs = 0;
494 
495             for (auto& p2 : p1.second)
496             {
497                 writeMacroHTMLReport(p2.first, moduleName, p2.second, __outputDir);
498             }
499 
500             // Now we make the module's reports
501             std::wostringstream out;
502 
503             out << L"<html lang=\'en\'>\n"
504                 << L"<meta charset=\'UTF-8\'>\n"
505                 << L"<head>\n"
506                 << L"<link rel=\'icon\' href=\'../favicon.ico\'/>\n"
507                 << L"<title>Scilab | Module " << moduleName << L" | Scilab\'s code coverage" << L"</title>\n"
508                 << L"<style type=\'text/css\' media=\'all\'>\n"
509                 << L"@import url(\'../mod_style.css\');\n"
510                 << L"</style>\n"
511                 << L"<script type=\'text/javascript\' src=\'../module.js\'></script>"
512                 << L"</head>\n"
513                 << L"<body>\n";
514 
515             bool altern = false;
516             unsigned int tableid = 0;
517             unsigned int buttonid = 0;
518 
519             out << L"<h2 class=\'title\'>Macros calls</h2>\n"
520                 << L"<table class='module'>\n"
521                 << L"<tr><td><div class=\'macros_stats\'>\n"
522                 << L"<div class=\'macros_cell\'><table id=\'table" << tableid << L"\'>\n"
523                 << L"<tr class=\'col_name\'><td>Name&nbsp;" << CovHTMLCodePrinter::getOrderButton(tableid, buttonid, 0, false) << L"</td><td>File</td><td>Calls&nbsp;" << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 1, 2, true) << L"</td><td>Covered&nbsp" << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 2, 3, false) << L"</td></tr>\n";
524 
525             for (const auto& t : getOrderedResults(moduleName))
526             {
527                 const CoverResult& res = *std::get<0>(t);
528                 const std::wstring& macroFilename = *std::get<1>(t);
529                 const std::wstring& macroName = *std::get<2>(t);
530 
531                 totalInstrs += res.getInfo().instrsCount;
532                 uncoveredInstrs += res.getUncInstrs();
533 
534                 std::size_t pos = macroFilename.find_last_of(L'.');
535                 std::wstring filename = macroFilename.substr(0, pos);
536                 pos = filename.find_last_of(L"\\/");
537                 if (pos != std::string::npos)
538                 {
539                     filename = filename.substr(pos + 1);
540                 }
541                 std::wstring filepath = encodeFilename(filename);
542 
543                 const std::wstring countercls = res.counter == 0 ? L"null_stats" : L"stats";
544                 const std::wstring trcls = altern ? L"altern1" : L"altern2";
545                 out << L"<tr class=\'" << trcls << L"\'><td><span class=\'cmp\'>" << macroName << L"</span></td>"
546                     << L"<td><a class=\'filepath' href=\'" << filepath << L".html\'>" << moduleName << L"/macros/" << filename << L".sci</a></td>"
547                     << L"<td class=\'" << countercls << L"\'><span class=\'cmp\'>" << res.counter << L"</span></td>"
548                     << L"<td><span class=\'inline_percent\'><span class=\'cmp\'>" << res.getCovInstrsPercent() << L"</span> %</span>";
549                 CovHTMLCodePrinter::getDivPercent(out, res.getCovInstrsPercent());
550                 out << L"</td></tr>\n";
551 
552                 altern = !altern;
553             }
554 
555             ++tableid;
556             buttonid += 3;
557 
558             out << L"</table></div></div></td></tr>\n"
559                 << L"</table>\n";
560 
561             // Now we print the number of calls for builtins.
562 
563             altern = false;
564 
565             out << L"<h2 class=\'title\'>Builtins calls</h2>\n"
566                 << L"<table class='module'>\n"
567                 << L"<tr><td><div class='modulePath'>" << moduleName << L"</div></td></tr>\n"
568                 << L"<tr><td><div class=\'builtins_stats\'>\n"
569                 << L"<div class=\'builtins_cell\'><table id=\'table" << tableid << L"\'>\n"
570                 << L"<tr class=\'col_name\'><td>Name&nbsp;" << CovHTMLCodePrinter::getOrderButton(tableid, buttonid, 0, false) << L"</td><td class=\'col_name\'>Calls&nbsp;" << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 1, 1, true) << L"</td></tr>\n";
571 
572             for (const auto& p : getBuiltinStats(moduleName))
573             {
574                 const std::wstring countercls = p.second == 0 ? L"null_stats" : L"stats";
575                 const std::wstring trcls = altern ? L"altern1" : L"altern2";
576                 out << L"<tr class=\'" << trcls << L"\'><td><span class=\'cmp\'>" << *p.first << L"</span></td><td class=\'" << countercls << L"\'><span class=\'cmp\'>" << p.second << L"</span></td></tr>\n";
577                 altern = !altern;
578                 totalCalls += p.second;
579             }
580 
581             out << L"</table></div>\n"
582                 << L"</div></td></tr>\n"
583                 << L"</table>\n"
584                 << L"</body>\n"
585                 << L"</html>\n";
586 
587             out.flush();
588             writeFile(out, __outputDir, moduleName + L".html");
589 
590             const double percent = totalInstrs ? std::round(100. * (1. - (double)uncoveredInstrs / (double)totalInstrs)) : 100.;
591             modulesStats.emplace(moduleName, std::pair<uint64_t, double>(totalCalls, percent));
592         }
593 
594         // Now we must output the results for all the modules
595         const std::wstring __outputDir(_outputDir);
596         std::wostringstream out;
597 
598         out << L"<html lang=\'en\'>\n"
599             << L"<meta charset=\'UTF-8\'>\n"
600             << L"<head>\n"
601             << L"<link rel=\'icon\' href=\'favicon.ico\'/>\n"
602             << L"<title>Scilab\'s code coverage" << L"</title>\n"
603             << L"<style type=\'text/css\' media=\'all\'>\n"
604             << L"@import url(\'mod_style.css\');\n"
605             << L"</style>\n"
606             << L"<script type=\'text/javascript\' src=\'module.js\'></script>"
607             << L"</head>\n"
608             << L"<body>\n";
609 
610         bool altern = false;
611         unsigned int tableid = 0;
612         unsigned int buttonid = 0;
613 
614         out << L"<h2 class=\'title\'>Modules results</h2>\n"
615             << L"<table class='module'>\n"
616             << L"<tr><td><div class=\'macros_stats\'>\n"
617             << L"<div class=\'macros_cell\'><table id=\'table" << tableid << L"\'>\n"
618             << L"<tr class=\'col_name\'><td>Name&nbsp;" << CovHTMLCodePrinter::getOrderButton(tableid, buttonid, 0, false) << L"</td><td>Builtin calls&nbsp;" << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 1, 1, true) << L"</td><td>Covered&nbsp" << CovHTMLCodePrinter::getOrderButton(tableid, buttonid + 2, 2, false) << L"</td></tr>\n";
619 
620         for (const auto& p : modulesStats)
621         {
622             const std::wstring& moduleName = p.first;
623             const uint64_t builtinCalls = p.second.first;
624             const double percent = p.second.second;
625             const std::wstring countercls = builtinCalls ? L"stats" : L"null_stats";
626             const std::wstring trcls = altern ? L"altern1" : L"altern2";
627             out << L"<tr class=\'" << trcls << L"\'>"
628                 << L"<td><span class=\'cmp\'><a class=\'filepath' href=\'" << moduleName << L"/" << moduleName << L".html\'>" << moduleName << L"</a></span></td>"
629                 << L"<td class=\'" << countercls << L"\'><span class=\'cmp\'>" << builtinCalls << L"</span></td>"
630                 << L"<td><span class=\'inline_percent\'><span class=\'cmp\'>" << percent << L"</span> %</span>";
631 
632             CovHTMLCodePrinter::getDivPercent(out, percent);
633             out << L"</tr>\n";
634             altern = !altern;
635         }
636 
637         out << L"</table></div>\n"
638             << L"</div></td></tr>\n"
639             << L"</table>\n"
640             << L"</body>\n"
641             << L"</html>\n";
642 
643         out.flush();
644         writeFile(out, __outputDir, L"report.html");
645     }
646 
647     FREE(_outputDir);
648     copyDataFiles(outputDir);
649 }
650 
copyDataFiles(const std::wstring & outputDir)651 void CoverModule::copyDataFiles(const std::wstring& outputDir)
652 {
653     const std::wstring _outputDir = outputDir + DIR_SEPARATORW;
654     const std::wstring _inputDir = std::wstring(L"SCI") + DIR_SEPARATORW + L"modules" + DIR_SEPARATORW + L"coverage" + DIR_SEPARATORW + L"data";
655     copyFile(_inputDir, _outputDir, L"scilab_code.css");
656     copyFile(_inputDir, _outputDir, L"src_style.css");
657     copyFile(_inputDir, _outputDir, L"mod_style.css");
658     copyFile(_inputDir, _outputDir, L"favicon.ico");
659     copyFile(_inputDir, _outputDir, L"module.js");
660 }
661 
copyFile(const std::wstring & inDir,const std::wstring & outDir,const std::wstring & filename)662 void CoverModule::copyFile(const std::wstring& inDir, const std::wstring& outDir, const std::wstring& filename)
663 {
664     const std::wstring in = inDir + DIR_SEPARATORW + filename;
665     const std::wstring out = outDir + DIR_SEPARATORW + filename;
666     wchar_t* _input = expandPathVariableW((wchar_t*)in.c_str());
667     wchar_t* _output = expandPathVariableW((wchar_t*)out.c_str());
668     CopyFileFunction(_output, _input);
669     FREE(_input);
670     FREE(_output);
671 }
672 
writeFile(const std::wostringstream & out,const std::wstring & outputDir,const std::wstring & filename)673 void CoverModule::writeFile(const std::wostringstream& out, const std::wstring& outputDir, const std::wstring& filename)
674 {
675     const std::string code = scilab::UTF8::toUTF8(out.str().c_str());
676     const std::string _filename = scilab::UTF8::toUTF8(outputDir + DIR_SEPARATORW + filename);
677     std::fstream file(_filename, std::ios::out | std::ios::trunc);
678     file.write(code.c_str(), code.size());
679     file.close();
680 }
681 
getOrderedResults(const std::wstring & moduleName) const682 std::set<CoverModule::__Res1, CoverModule::__Compare1> CoverModule::getOrderedResults(const std::wstring& moduleName) const
683 {
684     std::set<__Res1, __Compare1> set;
685     auto i = results.find(moduleName);
686     if (i != results.end())
687     {
688         for (const auto& p : i->second)
689         {
690             const std::wstring& macroFilename = p.first;
691             for (const auto& pp : p.second)
692             {
693                 const std::wstring& macroName = pp.first.name;
694                 const Location& macroLoc = pp.first.loc;
695                 const CoverResult& res = pp.second;
696                 set.emplace(__Res1(&res, &macroFilename, &macroName, &macroLoc));
697             }
698         }
699     }
700 
701     return set;
702 }
703 
getBuiltinStats(const std::wstring & moduleName) const704 std::set<CoverModule::__Res2, CoverModule::__Compare2> CoverModule::getBuiltinStats(const std::wstring& moduleName) const
705 {
706     std::set<__Res2, __Compare2> set;
707     auto i = allCounters.find(moduleName);
708     if (i != allCounters.end())
709     {
710         for (const auto& p : i->second)
711         {
712             if (!p.second.first)
713             {
714                 set.emplace(__Res2(&p.first, p.second.second));
715             }
716         }
717     }
718 
719     return set;
720 }
721 
getFunctionCalls(const std::wstring & moduleName,const bool builtin) const722 std::vector<std::pair<types::Callable*, uint64_t>> CoverModule::getFunctionCalls(const std::wstring& moduleName, const bool builtin) const
723 {
724     struct _Res
725     {
726         types::Callable* const fptr;
727         const uint64_t counter;
728 
729         _Res(types::Callable* const _fptr, const uint64_t _counter) : fptr(_fptr), counter(_counter) {}
730         inline bool operator<(const _Res& res) const
731         {
732             return (counter < res.counter) || (counter == res.counter && fptr->getName() < res.fptr->getName());
733         }
734     };
735     std::set<_Res> set;
736     std::vector<std::pair<types::Callable*, uint64_t>> calls;
737     auto range = functions.equal_range(moduleName);
738     for (auto fptr = range.first; fptr != range.second; ++fptr)
739     {
740         if ((builtin && fptr->second->isFunction()) || (!builtin && fptr->second->isMacro()))
741         {
742             auto i = callCounters.find(static_cast<types::Callable*>(fptr->second));
743             if (i != callCounters.end())
744             {
745                 set.emplace(fptr->second, i->second.get());
746             }
747         }
748     }
749 
750     calls.reserve(set.size());
751     for (const auto r : set)
752     {
753         calls.emplace_back(r.fptr, r.counter);
754     }
755 
756     return calls;
757 }
758 
getTree(const std::wstring & path)759 ast::Exp* CoverModule::getTree(const std::wstring& path)
760 {
761     if (!path.empty())
762     {
763         std::ifstream src(scilab::UTF8::toUTF8(path), std::ios::in | std::ios::binary | std::ios::ate);
764         if (src.is_open())
765         {
766             src.seekg(0, src.end);
767             int len = src.tellg();
768             src.seekg(0, src.beg);
769             char* buffer = new char[len + 1];
770             buffer[len] = '\0';
771             src.read(buffer, len);
772             src.close();
773 
774             wchar_t* _wstr = to_wide_string(buffer);
775             delete[] buffer;
776             Parser parser;
777             parser.parse(_wstr);
778             FREE(_wstr);
779 
780             return parser.getTree();
781         }
782     }
783     return nullptr;
784 }
785 
getName(const std::wstring & path)786 const std::wstring CoverModule::getName(const std::wstring& path)
787 {
788     std::size_t pos = path.find_last_of(L'.');
789     std::wstring name = path.substr(0, pos);
790     pos = name.find_last_of(L"\\/");
791     if (pos != std::string::npos)
792     {
793         name = name.substr(pos + 1);
794     }
795     return name;
796 }
797 
writeMacroHTMLReport(types::Macro * macro,std::map<MacroLoc,CoverResult> & results,const std::wstring & outputDir)798 void CoverModule::writeMacroHTMLReport(types::Macro* macro, std::map<MacroLoc, CoverResult>& results, const std::wstring& outputDir)
799 {
800     std::list<symbol::Variable*>* in = macro->getInputs();
801     std::list<symbol::Variable*>* out = macro->getOutputs();
802     ast::SeqExp& body = *macro->getBody()->clone();
803 
804     ast::exps_t& _in = *new ast::exps_t();
805     ast::exps_t& _out = *new ast::exps_t();
806     for (const auto i : *in)
807     {
808         _in.emplace_back(new ast::SimpleVar(Location(), i->getSymbol()));
809     }
810     for (const auto o : *out)
811     {
812         _out.emplace_back(new ast::SimpleVar(Location(), o->getSymbol()));
813     }
814     ast::FunctionDec* fdec = new ast::FunctionDec(Location(), symbol::Symbol(macro->getName()), *new ast::ArrayListVar(Location(), _in), *new ast::ArrayListVar(Location(), _out), body);
815 
816     writeMacroHTMLReport(fdec, macro->getName() + L".html", L"", L"", results, outputDir);
817 }
818 
writeMacroHTMLReport(ast::Exp * tree,const std::wstring & filename,const std::wstring & path,const std::wstring & moduleName,std::map<MacroLoc,CoverResult> & results,const std::wstring & outputDir)819 void CoverModule::writeMacroHTMLReport(ast::Exp* tree, const std::wstring& filename, const std::wstring& path, const std::wstring& moduleName, std::map<MacroLoc, CoverResult>& results, const std::wstring& outputDir)
820 {
821     std::wostringstream out;
822     std::wstring mod, prev;
823     if (!moduleName.empty())
824     {
825         mod = L" | Module " + moduleName;
826         prev = L"../";
827     }
828     std::wstring pa;
829     if (!path.empty())
830     {
831         pa = L" | " + path;
832     }
833 
834     out << L"<html lang=\'en\'>\n"
835         << L"<meta charset=\'UTF-8\'>\n"
836         << L"<head>\n"
837         << L"<link rel=\'icon\' href=\'../favicon.ico\'/>\n"
838         << L"<title>Scilab" << mod << pa << L" | Scilab\'s code coverage" << L"</title>\n"
839         << L"<style type=\'text/css\' media=\'all\'>\n"
840         << L"@import url(\'" << prev << "scilab_code.css\');\n"
841         << L"@import url(\'" << prev << "src_style.css\');\n"
842         << L"</style>\n"
843         << L"<script>\n"
844         << L"function show(did,fid) {\n"
845         << L"  x = document.getElementById(did).style;\n"
846         << L"  y = document.getElementById(fid);\n"
847         << L"  x.visibility = 'visible';\n"
848         << L"  x.display = 'block';\n"
849         << L"  x.height = 'auto';\n"
850         << L"  x.left = y.offsetLeft + 'px';\n"
851         << L"  x.top = y.offsetTop + y.offsetHeight + 'px';\n"
852         << L"}\n"
853         << L"function hide(did) {\n"
854         << L"  document.getElementById(did).style.visibility = \'hidden\';\n"
855         << L"}\n"
856         << L"</script>\n"
857         << L"</head>\n"
858         << L"<body>\n"
859         << L"<h2 class=\'title\'>Coverage and Profiling report</h2>\n"
860         << L"<table class='sourceFile'>\n";
861 
862     if (!path.empty())
863     {
864         out << L"<tr><td><div class='sourcePath'>" << path << L"</div></td></tr>\n";
865     }
866     if (!results.empty())
867     {
868         for (const auto& p : results)
869         {
870             out << L"<tr><td><div class=\'allmacstats\'>\n";
871             CovHTMLCodePrinter::getFunctionStats(out, p.first, p.second);
872             out << L"</div></td></tr\n";
873         }
874     }
875 
876     out << L"<tr><td><div class=\'source\'>\n"
877         << L"<table>\n";
878 
879     CovHTMLCodePrinter printer(out, results);
880     CodePrinterVisitor visitor(printer);
881     tree->accept(visitor);
882     printer.close();
883     delete tree;
884 
885     out << L"</table>\n"
886         << L"</div></td></tr>\n"
887         << L"</table>\n"
888         << L"</body>\n"
889         << L"</html>\n";
890 
891     out.flush();
892     writeFile(out, outputDir, filename);
893 }
894 
writeMacroHTMLReport(const std::wstring & path,const std::wstring & moduleName,std::map<MacroLoc,CoverResult> & results,const std::wstring & outputDir)895 bool CoverModule::writeMacroHTMLReport(const std::wstring& path, const std::wstring& moduleName, std::map<MacroLoc, CoverResult>& results, const std::wstring& outputDir)
896 {
897     if (ast::Exp* tree = getTree(path))
898     {
899         writeMacroHTMLReport(tree, getName(path) + L".html", path, moduleName, results, outputDir);
900         return true;
901     }
902 
903     return false;
904 }
905 
encodeFilename(const std::wstring & name)906 std::wstring CoverModule::encodeFilename(const std::wstring& name)
907 {
908     std::wostringstream wos;
909     for (const auto c : name)
910     {
911         URLEncoder::replace(wos, c);
912     }
913     return wos.str();
914 }
915 
merge(const std::vector<std::wstring> & paths,const std::wstring & out)916 void CoverModule::merge(const std::vector<std::wstring>& paths, const std::wstring& out)
917 {
918     CoverModule cm;
919     for (const auto path : paths)
920     {
921         cm.load(path);
922     }
923     cm.save(out);
924 }
925 
toHTML(const std::wstring & inBin,const std::wstring & outDir)926 void CoverModule::toHTML(const std::wstring& inBin, const std::wstring& outDir)
927 {
928     CoverModule cm;
929     cm.load(inBin);
930     cm.toHTML(outDir);
931 }
932 
save(const std::wstring & path) const933 void CoverModule::save(const std::wstring& path) const
934 {
935     if (!path.empty())
936     {
937         std::fstream out(scilab::UTF8::toUTF8(path), std::ios::out | std::ios::binary);
938         if (out.is_open())
939         {
940             toBin(out);
941             out.close();
942         }
943     }
944 }
945 
load(const std::wstring & path)946 void CoverModule::load(const std::wstring& path)
947 {
948     if (!path.empty())
949     {
950         std::fstream in(scilab::UTF8::toUTF8(path), std::ios::in | std::ios::binary);
951         if (in.is_open())
952         {
953             fromBin(*this, in);
954             in.close();
955         }
956     }
957 }
958 
toBin(std::fstream & out) const959 void CoverModule::toBin(std::fstream& out) const
960 {
961     // Save results
962     CoverModule::write(out, (uint64_t)results.size());
963     for (const auto& p : results)
964     {
965         CoverModule::write(out, p.first);
966         CoverModule::write(out, (uint64_t)p.second.size());
967         for (const auto& pp : p.second)
968         {
969             CoverModule::write(out, pp.first);
970             CoverModule::write(out, (uint64_t)pp.second.size());
971             for (const auto& ppp : pp.second)
972             {
973                 CoverModule::write(out, ppp.first.name);
974                 CoverModule::write(out, ppp.first.loc);
975                 ppp.second.toBin(out);
976             }
977         }
978     }
979 
980     // Save allcounters
981     CoverModule::write(out, (uint64_t)allCounters.size());
982     for (const auto& p : allCounters)
983     {
984         CoverModule::write(out, p.first);
985         CoverModule::write(out, (uint64_t)p.second.size());
986         for (const auto& pp : p.second)
987         {
988             CoverModule::write(out, pp.first);
989             CoverModule::write(out, pp.second.first);
990             CoverModule::write(out, pp.second.second);
991         }
992     }
993 }
994 
fromBin(std::fstream & in)995 void CoverModule::fromBin(std::fstream& in)
996 {
997     fromBin(*this, in);
998 }
999 
fromBin(CoverModule & cm,std::fstream & in)1000 void CoverModule::fromBin(CoverModule& cm, std::fstream& in)
1001 {
1002     // Load results
1003     uint64_t size = CoverModule::readUint64_t(in);
1004     for (uint64_t i = 0; i < size; ++i)
1005     {
1006         const std::wstring moduleName = CoverModule::readWstring(in);
1007         const uint64_t sizee = CoverModule::readUint64_t(in);
1008         auto it = cm.results.find(moduleName);
1009         if (it == cm.results.end())
1010         {
1011             it = cm.results.emplace(moduleName, std::unordered_map<std::wstring, std::map<MacroLoc, CoverResult>>()).first;
1012         }
1013         auto& map = it->second;
1014         for (uint64_t j = 0; j < sizee; ++j)
1015         {
1016             const std::wstring macroFilename = CoverModule::readWstring(in);
1017             const uint64_t sizeee = CoverModule::readUint64_t(in);
1018             auto it = map.find(macroFilename);
1019             if (it == map.end())
1020             {
1021                 it = map.emplace(macroFilename, std::map<MacroLoc, CoverResult>()).first;
1022             }
1023             auto& mapp = it->second;
1024             for (uint64_t k = 0; k < sizeee; ++k)
1025             {
1026                 const std::wstring macroName = CoverModule::readWstring(in);
1027                 const Location macroLoc = CoverModule::readLocation(in);
1028                 const MacroLoc ml(macroName, macroLoc);
1029                 auto it = mapp.find(ml);
1030                 if (it == mapp.end())
1031                 {
1032                     mapp.emplace(ml, CoverResult::fromBin(in));
1033                 }
1034                 else
1035                 {
1036                     it->second.merge(CoverResult::fromBin(in));
1037                 }
1038             }
1039         }
1040     }
1041 
1042     // Load allcounters
1043     size = CoverModule::readUint64_t(in);
1044     for (uint64_t i = 0; i < size; ++i)
1045     {
1046         const std::wstring moduleName = CoverModule::readWstring(in);
1047         const uint64_t sizee = CoverModule::readUint64_t(in);
1048         auto it = cm.allCounters.find(moduleName);
1049         if (it == cm.allCounters.end())
1050         {
1051             it = cm.allCounters.emplace(moduleName, std::unordered_map<std::wstring, std::pair<bool, uint64_t>>()).first;
1052         }
1053         auto& map = it->second;
1054         for (uint64_t j = 0; j < sizee; ++j)
1055         {
1056             const std::wstring funName = CoverModule::readWstring(in);
1057             const bool ismacro = CoverModule::readBool(in);
1058             const uint64_t counter = CoverModule::readUint64_t(in);
1059             auto it = map.find(funName);
1060             if (it == map.end())
1061             {
1062                 map.emplace(funName, std::pair<bool, uint64_t>(ismacro, counter));
1063             }
1064             else
1065             {
1066                 auto& p = it->second;
1067                 if (p.first == ismacro)
1068                 {
1069                     p.second += counter;
1070                 }
1071             }
1072         }
1073     }
1074 }
1075 
1076 } // namespace coverage
1077