6b75291c4acf2deefa93a48659bf9ed2d2550638
[scilab.git] / scilab / modules / ast / src / cpp / ast / debuggervisitor.cpp
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2015 - Scilab Enterprises - Antoine ELIAS
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 #include "debuggervisitor.hxx"
17 #include "debugmanager.hxx"
18 #include "printvisitor.hxx"
19 #include "execvisitor.hxx"
20 #include "threadId.hxx"
21 #include "macrofile.hxx"
22 #include "commentexp.hxx"
23 #include "UTF8.hxx"
24
25 extern "C"
26 {
27 #include "filemanager_interface.h"
28 #include "FileExist.h"
29 }
30
31 static bool getMacroSourceFile(std::string* filename = nullptr);
32
33 namespace ast
34 {
35 void DebuggerVisitor::visit(const SeqExp  &e)
36 {
37     std::list<Exp *>::const_iterator itExp;
38     debugger::DebuggerManager* manager = debugger::DebuggerManager::getInstance();
39     manager->resetAborted();
40
41     for (const auto & exp : e.getExps())
42     {
43         if (exp->isCommentExp())
44         {
45             continue;
46         }
47
48         if (e.isBreakable())
49         {
50             exp->resetBreak();
51             exp->setBreakable();
52         }
53
54         if (e.isContinuable())
55         {
56             exp->resetContinue();
57             exp->setContinuable();
58         }
59
60         if (e.isReturnable())
61         {
62             exp->setReturnable();
63         }
64
65         //debugger check !
66         int iBreakPoint = -1;
67         if (ConfigVariable::getEnableDebug())
68         {
69             bool stopExecution = false;
70             if (manager->isStepIn())
71             {
72                 manager->resetStepIn();
73                 stopExecution = true;
74                 if(getMacroSourceFile() == false)
75                 {
76                     stopExecution = false;
77                     manager->setStepIn();
78                 }
79             }
80             else if (manager->isStepNext())
81             {
82                 manager->resetStepNext();
83                 stopExecution = true;
84                 if(getMacroSourceFile() == false)
85                 {
86                     stopExecution = false;
87                     manager->setStepOut();
88                 }
89             }
90             else
91             {
92                 const std::vector<ConfigVariable::WhereEntry>& lWhereAmI = ConfigVariable::getWhere();
93                 //set information from debugger commands
94                 if (lWhereAmI.size() != 0 && manager->getBreakPointCount() != 0)
95                 {
96                     debugger::Breakpoints& bps = manager->getAllBreakPoint();
97
98                     int i = -1;
99                     for (const auto & bp : bps)
100                     {
101                         ++i;
102                         if (bp->isEnable() == false)
103                         {
104                             continue;
105                         }
106
107                         // look for a breakpoint on this line and update breakpoint information when possible
108                         int iLine = exp->getLocation().first_line - ConfigVariable::getMacroFirstLines();
109                         if (bp->hasMacro())
110                         {
111                             char* functionName = wide_string_to_UTF8(lWhereAmI.back().call->getName().data());
112                             if (bp->getFunctioName().compare(functionName) == 0)
113                             {
114                                 if (bp->getMacroLine() == 0)
115                                 {
116                                     //first pass in macro.
117                                     //update first line with real value
118                                     bp->setMacroLine(iLine);
119                                 }
120
121                                 stopExecution = bp->getMacroLine() == iLine;
122                             }
123
124                             FREE(functionName);
125                         }
126                         else if (bp->hasFile())
127                         {
128                             if (bp->getFileLine() == exp->getLocation().first_line)
129                             {
130                                 std::string pFileName;
131                                 bool hasSource = getMacroSourceFile(&pFileName);
132                                 if (hasSource && bp->getFileName() == pFileName)
133                                 {
134                                     stopExecution = true;
135                                     // set function information
136                                     if (lWhereAmI.back().call->getFirstLine())
137                                     {
138                                         bp->setFunctionName(scilab::UTF8::toUTF8(lWhereAmI.back().call->getName()));
139                                         bp->setMacroLine(iLine);
140                                     }
141                                 }
142                             }
143                         }
144
145                         if(stopExecution == false)
146                         {
147                             // no breakpoint for this line
148                             continue;
149                         }
150
151                         // Set the begin of line if not yet done
152                         if(bp->getBeginLine() == 0)
153                         {
154                             bp->setBeginLine(exp->getLocation().first_column);
155                         }
156                         // check if this exp is at the begin of the breakpoint line
157                         else if(bp->getBeginLine() != exp->getLocation().first_column)
158                         {
159                             // stop only if we are at the line begins
160                             stopExecution = false;
161                             continue;
162                         }
163
164                         //check condition
165                         if (bp->getConditionExp() != NULL)
166                         {
167                             //do not use debuggervisitor !
168                             symbol::Context* pCtx = symbol::Context::getInstance();
169                             try
170                             {
171                                 ExecVisitor execCond;
172                                 //protect current env during condition execution
173                                 pCtx->scope_begin();
174                                 bp->getConditionExp()->accept(execCond);
175                                 types::InternalType* pIT = pCtx->getCurrentLevel(symbol::Symbol(L"ans"));
176                                 if (pIT == NULL ||
177                                         pIT->isBool() == false ||
178                                         ((types::Bool*)pIT)->isScalar() == false ||
179                                         ((types::Bool*)pIT)->get(0) == 0)
180                                 {
181                                     pCtx->scope_end();
182                                     //not a boolean, not scalar or false
183                                     stopExecution = false;
184                                     continue;
185                                 }
186
187                                 pCtx->scope_end();
188                                 //ok condition is valid and true
189                             }
190                             catch (ast::ScilabException &/*e*/)
191                             {
192                                 pCtx->scope_end();
193                                 stopExecution = false;
194                                 //not work !
195                                 //invalid breakpoint
196                                 continue;
197                             }
198                         }
199
200                         //we have a breakpoint !
201                         //stop execution and wait signal from debugger to restart
202                         iBreakPoint = i;
203
204                         //only one breakpoint can be "call" on same exp
205                         break;
206                     }
207                 }
208             }
209
210             if(stopExecution)
211             {
212                 manager->stop(exp, iBreakPoint);
213                 if (manager->isAborted())
214                 {
215                     throw ast::InternalAbort();
216                 }
217             }
218         }
219
220         //copy from runvisitor::seqexp
221         try
222         {
223             //reset default values
224             setResult(NULL);
225             int iExpectedSize = getExpectedSize();
226             setExpectedSize(-1);
227             exp->accept(*this);
228             setExpectedSize(iExpectedSize);
229             types::InternalType * pIT = getResult();
230
231             // In case of exec file, set the file name in the Macro to store where it is defined.
232             int iFileID = ConfigVariable::getExecutedFileID();
233             if (iFileID && exp->isFunctionDec())
234             {
235                 types::InternalType* pITMacro = symbol::Context::getInstance()->get(exp->getAs<ast::FunctionDec>()->getSymbol());
236                 if (pITMacro)
237                 {
238                     types::Macro* pMacro = pITMacro->getAs<types::Macro>();
239                     const wchar_t* filename = getfile_filename(iFileID);
240                     // scilab.quit is not open with mopen
241                     // in this case filename is NULL because FileManager have not been filled.
242                     if (filename)
243                     {
244                         pMacro->setFileName(filename);
245                     }
246                 }
247             }
248
249             if (pIT != NULL)
250             {
251                 bool bImplicitCall = false;
252                 if (pIT->isCallable()) //to manage call without ()
253                 {
254                     types::Callable *pCall = pIT->getAs<types::Callable>();
255                     types::typed_list out;
256                     types::typed_list in;
257                     types::optional_list opt;
258
259                     try
260                     {
261                         //in this case of calling, we can return only one value
262                         int iSaveExpectedSize = getExpectedSize();
263                         setExpectedSize(1);
264
265                         pCall->invoke(in, opt, getExpectedSize(), out, *exp);
266                         setExpectedSize(iSaveExpectedSize);
267
268                         if (out.size() == 0)
269                         {
270                             setResult(NULL);
271                         }
272                         else
273                         {
274                             setResult(out[0]);
275                         }
276
277                         bImplicitCall = true;
278                     }
279                     catch (const InternalError& ie)
280                     {
281                         if (ConfigVariable::getLastErrorFunction() == L"")
282                         {
283                             ConfigVariable::setLastErrorFunction(pCall->getName());
284                             ConfigVariable::setLastErrorLine(e.getLocation().first_line);
285                         }
286
287                         throw ie;
288                     }
289                 }
290
291                 //don't output Simplevar and empty result
292                 if (getResult() != NULL && (!exp->isSimpleVar() || bImplicitCall))
293                 {
294                     //symbol::Context::getInstance()->put(symbol::Symbol(L"ans"), *execMe.getResult());
295                     types::InternalType* pITAns = getResult();
296                     symbol::Context::getInstance()->put(m_pAns, pITAns);
297                     if (exp->isVerbose() && ConfigVariable::isPrintOutput())
298                     {
299                         //TODO manage multiple returns
300                         scilabWriteW(L" ans  =\n\n");
301                         std::wostringstream ostrName;
302                         ostrName << L"ans";
303                         types::VariableToString(pITAns, ostrName.str().c_str());
304                     }
305                 }
306
307                 pIT->killMe();
308             }
309
310             if ((&e)->isBreakable() && exp->isBreak())
311             {
312                 const_cast<SeqExp *>(&e)->setBreak();
313                 exp->resetBreak();
314                 break;
315             }
316
317             if ((&e)->isContinuable() && exp->isContinue())
318             {
319                 const_cast<SeqExp *>(&e)->setContinue();
320                 exp->resetContinue();
321                 break;
322             }
323
324             if ((&e)->isReturnable() && exp->isReturn())
325             {
326                 const_cast<SeqExp *>(&e)->setReturn();
327                 exp->resetReturn();
328                 break;
329             }
330
331             // Stop execution at the end of the seqexp of the caller
332             // Do it at the end of the seqexp will make the debugger stop
333             // even if the caller is at the last line
334             // ie: the caller is followed by endfunction
335             if(manager->isStepOut())
336             {
337                 manager->resetStepOut();
338                 if(getMacroSourceFile() == false)
339                 {
340                     // no sources
341                     manager->setStepOut();
342                 }
343                 else
344                 {
345                     manager->stop(exp, iBreakPoint);
346                 }
347
348                 if (manager->isAborted())
349                 {
350                     throw ast::InternalAbort();
351                 }
352             }
353
354         }
355         catch (const InternalError& ie)
356         {
357             ConfigVariable::fillWhereError(ie.GetErrorLocation().first_line);
358
359             const std::vector<ConfigVariable::WhereEntry>& lWhereAmI = ConfigVariable::getWhere();
360
361             //where can be empty on last stepout, on first calling expression
362             if (lWhereAmI.size())
363             {
364                 const std::wstring* filename = lWhereAmI.back().m_file_name;
365
366                 if (filename == nullptr)
367                 {
368                     //error in a console script
369                     std::wstring functionName = lWhereAmI.back().call->getName();
370                     manager->errorInScript(functionName, exp);
371                 }
372                 else
373                 {
374                     manager->errorInFile(*filename, exp);
375                 }
376
377                 // Debugger just restart after been stopped on an error.
378                 if (manager->isAborted())
379                 {
380                     throw ast::InternalAbort();
381                 }
382             }
383
384             throw ie;
385         }
386
387         // If something other than NULL is given to setResult, then that would imply
388         // to make a cleanup in visit(ForExp) for example (e.getBody().accept(*this);)
389         setResult(NULL);
390
391     }
392
393     if (e.getParent() == NULL && e.getExecFrom() == SeqExp::SCRIPT && (manager->isStepNext() || manager->isStepIn()))
394     {
395         const std::vector<ConfigVariable::WhereEntry>& lWhereAmI = ConfigVariable::getWhere();
396         if (lWhereAmI.size())
397         {
398             std::wstring functionName = lWhereAmI.back().call->getName();
399             types::InternalType* pIT = symbol::Context::getInstance()->get(symbol::Symbol(functionName));
400             if (pIT && (pIT->isMacro() || pIT->isMacroFile()))
401             {
402                 types::Macro* m = nullptr;
403                 if (pIT->isMacroFile())
404                 {
405                     types::MacroFile* mf = pIT->getAs<types::MacroFile>();
406                     m = mf->getMacro();
407                 }
408                 else
409                 {
410                     m = pIT->getAs<types::Macro>();
411                 }
412
413                 //create a fake exp to represente end/enfunction
414
415                 //will be deleted by CommentExp
416                 std::wstring* comment = new std::wstring(L"end of function");
417                 Location loc(m->getLastLine(), m->getLastLine(), 0, 0);
418                 CommentExp fakeExp(loc, comment);
419                 manager->stop(&fakeExp, -1);
420
421                 if (manager->isAborted())
422                 {
423                     throw ast::InternalAbort();
424                 }
425
426                 //transform stepnext after endfunction as a stepout to show line marker on current statement
427                 if (manager->isStepNext())
428                 {
429                     manager->resetStepNext();
430                     manager->setStepOut();
431                 }
432                 else if (manager->isStepIn())
433                 {
434                     manager->resetStepIn();
435                     manager->setStepOut();
436                 }
437             }
438         }
439     }
440 }
441 }
442
443 // return false if a file .sci of a file .bin doesn't exists
444 // return true for others files or existing .sci
445 bool getMacroSourceFile(std::string* filename)
446 {
447     const std::vector<ConfigVariable::WhereEntry>& lWhereAmI = ConfigVariable::getWhere();
448     std::string file = scilab::UTF8::toUTF8(*lWhereAmI.back().m_file_name);
449     if (file.rfind(".bin") != std::string::npos)
450     {
451         file.replace(file.size() - 4, 4, ".sci");
452         // stop on bp only if the file exist
453         if (!FileExist(file.data()))
454         {
455             return false;
456         }
457     }
458
459     if(filename != nullptr)
460     {
461         filename->assign(file);
462     }
463
464     return true;
465 }