Debugger fixed about command interruption by another one
[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 "filemanager_interface.h"
29 #include "FileExist.h"
30 }
31
32 static bool getMacroSourceFile(std::string* filename = nullptr);
33
34 namespace ast
35 {
36 void DebuggerVisitor::visit(const SeqExp  &e)
37 {
38     std::list<Exp *>::const_iterator itExp;
39
40     debugger::DebuggerManager* manager = debugger::DebuggerManager::getInstance();
41     if(manager->isAborted())
42     {
43         // abort a running execution
44         throw ast::InternalAbort();
45     }
46
47     for (const auto & exp : e.getExps())
48     {
49         if (exp->isCommentExp())
50         {
51             continue;
52         }
53
54         if (e.isBreakable())
55         {
56             exp->resetBreak();
57             exp->setBreakable();
58         }
59
60         if (e.isContinuable())
61         {
62             exp->resetContinue();
63             exp->setContinuable();
64         }
65
66         if (e.isReturnable())
67         {
68             exp->setReturnable();
69         }
70
71         //debugger check !
72         int iBreakPoint = -1;
73         if (ConfigVariable::getEnableDebug())
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                                         pIT->isBool() == false ||
184                                         ((types::Bool*)pIT)->isScalar() == false ||
185                                         ((types::Bool*)pIT)->get(0) == 0)
186                                 {
187                                     pCtx->scope_end();
188                                     //not a boolean, not scalar or false
189                                     stopExecution = false;
190                                     continue;
191                                 }
192
193                                 pCtx->scope_end();
194                                 //ok condition is valid and true
195                             }
196                             catch (ast::ScilabException &/*e*/)
197                             {
198                                 pCtx->scope_end();
199                                 stopExecution = false;
200                                 //not work !
201                                 //invalid breakpoint
202                                 continue;
203                             }
204                         }
205
206                         //we have a breakpoint !
207                         //stop execution and wait signal from debugger to restart
208                         iBreakPoint = i;
209
210                         //only one breakpoint can be "call" on same exp
211                         break;
212                     }
213                 }
214             }
215
216             if(stopExecution)
217             {
218                 manager->stop(exp, iBreakPoint);
219                 if (manager->isAborted())
220                 {
221                     throw ast::InternalAbort();
222                 }
223             }
224         }
225
226         // interrupt me to execute a prioritary command
227         while (StaticRunner_isInterruptibleCommand() == 1 && StaticRunner_isRunnerAvailable() == 1)
228         {
229             // save the origin of the actual running command
230             command_origin_t origin = StaticRunner_getCurrentCommandOrigin();
231             StaticRunner_launch();
232             StaticRunner_setInterruptibleCommand(1);
233             // restore the origin of the actual running command
234             StaticRunner_setCurrentCommandOrigin(origin);
235         }
236
237         //copy from runvisitor::seqexp
238         try
239         {
240             //reset default values
241             setResult(NULL);
242             int iExpectedSize = getExpectedSize();
243             setExpectedSize(-1);
244             exp->accept(*this);
245             setExpectedSize(iExpectedSize);
246             types::InternalType * pIT = getResult();
247
248             // In case of exec file, set the file name in the Macro to store where it is defined.
249             int iFileID = ConfigVariable::getExecutedFileID();
250             if (iFileID && exp->isFunctionDec())
251             {
252                 types::InternalType* pITMacro = symbol::Context::getInstance()->get(exp->getAs<ast::FunctionDec>()->getSymbol());
253                 if (pITMacro)
254                 {
255                     types::Macro* pMacro = pITMacro->getAs<types::Macro>();
256                     const wchar_t* filename = getfile_filename(iFileID);
257                     // scilab.quit is not open with mopen
258                     // in this case filename is NULL because FileManager have not been filled.
259                     if (filename)
260                     {
261                         pMacro->setFileName(filename);
262                     }
263                 }
264             }
265
266             if (pIT != NULL)
267             {
268                 bool bImplicitCall = false;
269                 if (pIT->isCallable()) //to manage call without ()
270                 {
271                     types::Callable *pCall = pIT->getAs<types::Callable>();
272                     types::typed_list out;
273                     types::typed_list in;
274                     types::optional_list opt;
275
276                     try
277                     {
278                         //in this case of calling, we can return only one value
279                         int iSaveExpectedSize = getExpectedSize();
280                         setExpectedSize(1);
281
282                         pCall->invoke(in, opt, getExpectedSize(), out, *exp);
283                         setExpectedSize(iSaveExpectedSize);
284
285                         if (out.size() == 0)
286                         {
287                             setResult(NULL);
288                         }
289                         else
290                         {
291                             setResult(out[0]);
292                         }
293
294                         bImplicitCall = true;
295                     }
296                     catch (const InternalError& ie)
297                     {
298                         if (ConfigVariable::getLastErrorFunction() == L"")
299                         {
300                             ConfigVariable::setLastErrorFunction(pCall->getName());
301                             ConfigVariable::setLastErrorLine(e.getLocation().first_line);
302                         }
303
304                         throw ie;
305                     }
306                 }
307
308                 //don't output Simplevar and empty result
309                 if (getResult() != NULL && (!exp->isSimpleVar() || bImplicitCall))
310                 {
311                     //symbol::Context::getInstance()->put(symbol::Symbol(L"ans"), *execMe.getResult());
312                     types::InternalType* pITAns = getResult();
313                     symbol::Context::getInstance()->put(m_pAns, pITAns);
314                     if (exp->isVerbose() && ConfigVariable::isPrintOutput())
315                     {
316                         //TODO manage multiple returns
317                         scilabWriteW(L" ans  =\n\n");
318                         std::wostringstream ostrName;
319                         ostrName << L"ans";
320                         types::VariableToString(pITAns, ostrName.str().c_str());
321                     }
322                 }
323
324                 pIT->killMe();
325             }
326
327             if ((&e)->isBreakable() && exp->isBreak())
328             {
329                 const_cast<SeqExp *>(&e)->setBreak();
330                 exp->resetBreak();
331                 break;
332             }
333
334             if ((&e)->isContinuable() && exp->isContinue())
335             {
336                 const_cast<SeqExp *>(&e)->setContinue();
337                 exp->resetContinue();
338                 break;
339             }
340
341             if ((&e)->isReturnable() && exp->isReturn())
342             {
343                 const_cast<SeqExp *>(&e)->setReturn();
344                 exp->resetReturn();
345                 break;
346             }
347
348             // Stop execution at the end of the seqexp of the caller
349             // Do it at the end of the seqexp will make the debugger stop
350             // even if the caller is at the last line
351             // ie: the caller is followed by endfunction
352             if(manager->isStepOut())
353             {
354                 manager->resetStepOut();
355                 if(getMacroSourceFile() == false)
356                 {
357                     // no sources
358                     manager->setStepOut();
359                 }
360                 else
361                 {
362                     manager->stop(exp, iBreakPoint);
363                 }
364
365                 if (manager->isAborted())
366                 {
367                     throw ast::InternalAbort();
368                 }
369             }
370
371         }
372         catch (const InternalError& ie)
373         {
374             ConfigVariable::fillWhereError(ie.GetErrorLocation().first_line);
375
376             const std::vector<ConfigVariable::WhereEntry>& lWhereAmI = ConfigVariable::getWhere();
377
378             //where can be empty on last stepout, on first calling expression
379             if (lWhereAmI.size())
380             {
381                 const std::wstring* filename = lWhereAmI.back().m_file_name;
382
383                 if (filename == nullptr)
384                 {
385                     //error in a console script
386                     std::wstring functionName = lWhereAmI.back().call->getName();
387                     manager->errorInScript(functionName, exp);
388                 }
389                 else
390                 {
391                     manager->errorInFile(*filename, exp);
392                 }
393
394                 // Debugger just restart after been stopped on an error.
395                 if (manager->isAborted())
396                 {
397                     throw ast::InternalAbort();
398                 }
399             }
400
401             throw ie;
402         }
403
404         // If something other than NULL is given to setResult, then that would imply
405         // to make a cleanup in visit(ForExp) for example (e.getBody().accept(*this);)
406         setResult(NULL);
407
408     }
409
410     if (e.getParent() == NULL && e.getExecFrom() == SeqExp::SCRIPT && (manager->isStepNext() || manager->isStepIn()))
411     {
412         const std::vector<ConfigVariable::WhereEntry>& lWhereAmI = ConfigVariable::getWhere();
413         if (lWhereAmI.size())
414         {
415             std::wstring functionName = lWhereAmI.back().call->getName();
416             types::InternalType* pIT = symbol::Context::getInstance()->get(symbol::Symbol(functionName));
417             if (pIT && (pIT->isMacro() || pIT->isMacroFile()))
418             {
419                 types::Macro* m = nullptr;
420                 if (pIT->isMacroFile())
421                 {
422                     types::MacroFile* mf = pIT->getAs<types::MacroFile>();
423                     m = mf->getMacro();
424                 }
425                 else
426                 {
427                     m = pIT->getAs<types::Macro>();
428                 }
429
430                 //create a fake exp to represente end/enfunction
431
432                 //will be deleted by CommentExp
433                 std::wstring* comment = new std::wstring(L"end of function");
434                 Location loc(m->getLastLine(), m->getLastLine(), 0, 0);
435                 CommentExp fakeExp(loc, comment);
436                 manager->stop(&fakeExp, -1);
437
438                 if (manager->isAborted())
439                 {
440                     throw ast::InternalAbort();
441                 }
442
443                 //transform stepnext after endfunction as a stepout to show line marker on current statement
444                 if (manager->isStepNext())
445                 {
446                     manager->resetStepNext();
447                     manager->setStepOut();
448                 }
449                 else if (manager->isStepIn())
450                 {
451                     manager->resetStepIn();
452                     manager->setStepOut();
453                 }
454             }
455         }
456     }
457 }
458 }
459
460 // return false if a file .sci of a file .bin doesn't exists
461 // return true for others files or existing .sci
462 bool getMacroSourceFile(std::string* filename)
463 {
464     const std::vector<ConfigVariable::WhereEntry>& lWhereAmI = ConfigVariable::getWhere();
465     std::string file = scilab::UTF8::toUTF8(*lWhereAmI.back().m_file_name);
466     if (file.rfind(".bin") != std::string::npos)
467     {
468         file.replace(file.size() - 4, 4, ".sci");
469         // stop on bp only if the file exist
470         if (!FileExist(file.data()))
471         {
472             return false;
473         }
474     }
475
476     if(filename != nullptr)
477     {
478         filename->assign(file);
479     }
480
481     return true;
482 }