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