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