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