core: heap-use-after-free crash fixed
[scilab.git] / scilab / modules / core / src / cpp / runner.cpp
1 /*
2  *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  *  Copyright (C) 2011-2011 - DIGITEO - Bruno JOFRET
4  *  Copyright (C) 2014-2015 - Scilab Enterprises - Cedric Delamarre
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
8  * This file is hereby licensed under the terms of the GNU GPL v2.0,
9  * pursuant to article 5.3.4 of the CeCILL v.2.1.
10  * This file was originally licensed under the terms of the CeCILL v2.1,
11  * and continues to be available under such terms.
12  * For more information, see the COPYING file which you should have received
13  * along with this program.
14  *
15  */
16
17 #include "runner.hxx"
18 #include "threadmanagement.hxx"
19 #include "configvariable.hxx"
20 #include "debugmanager.hxx"
21
22 extern "C"
23 {
24 #include "HistoryManager.h"
25 #include "BrowseVarManager.h"
26 #include "FileBrowserChDir.h"
27 #include "scicurdir.h"
28 #include "Scierror.h"
29 #include "InitializeJVM.h"
30 }
31
32 std::atomic<Runner*> StaticRunner::m_RunMe(nullptr);
33 std::atomic<Runner*> StaticRunner::m_CurrentRunner(nullptr);
34 std::atomic<bool> StaticRunner::m_bInterruptibleCommand(true);
35
36 static bool initialJavaHooks = false;
37
38 void StaticRunner::sendExecDoneSignal()
39 {
40     switch (getCurrentRunner()->getCommandOrigin())
41     {
42         case DEBUGGER :
43         {
44             ThreadManagement::SendDebuggerExecDoneSignal();
45             break;
46         }
47         case CONSOLE :
48         case TCLSCI :
49         case NONE :
50         default :
51         {
52             ThreadManagement::SendConsoleExecDoneSignal();
53             break;
54         }
55     }
56 }
57
58 int StaticRunner::launch()
59 {
60     //set execution thread in java
61     if (!initialJavaHooks && getScilabMode() != SCILAB_NWNI)
62     {
63         initialJavaHooks = true;
64         // Execute the initial hooks registered in Scilab.java
65         ExecuteInitialHooks();
66     }
67
68     int iRet = 0;
69     // get the runner to execute
70     std::unique_ptr<Runner> runMe(getRunner());
71     // set if the current command is interruptible
72     setInterruptibleCommand(runMe->isInterruptible());
73     debugger::DebuggerManager* manager = debugger::DebuggerManager::getInstance();
74
75     ConfigVariable::resetExecutionBreak();
76
77     int oldMode = ConfigVariable::getPromptMode();
78     symbol::Context* pCtx = symbol::Context::getInstance();
79     int scope = pCtx->getScopeLevel();
80
81     // a TCL command display nothing
82     int iOldPromptMode = 0;
83     if (runMe->getCommandOrigin() == TCLSCI)
84     {
85         iOldPromptMode = ConfigVariable::getPromptMode();
86         ConfigVariable::setPromptMode(-1);
87     }
88
89     try
90     {
91         int level = ConfigVariable::getRecursionLevel();
92         try
93         {
94             runMe->getProgram()->accept(*(runMe->getVisitor()));
95         }
96         catch (const ast::RecursionException& re)
97         {
98             // management of pause
99             if (ConfigVariable::getPauseLevel())
100             {
101                 ConfigVariable::DecreasePauseLevel();
102                 throw re;
103             }
104
105             //close opened scope during try
106             while (pCtx->getScopeLevel() > scope)
107             {
108                 pCtx->scope_end();
109             }
110
111             //decrease recursion to init value and close where
112             while (ConfigVariable::getRecursionLevel() > level)
113             {
114                 ConfigVariable::where_end();
115                 ConfigVariable::decreaseRecursion();
116             }
117
118             ConfigVariable::resetWhereError();
119             ConfigVariable::setPromptMode(oldMode);
120
121             //print msg about recursion limit and trigger an error
122             wchar_t sz[1024];
123             os_swprintf(sz, 1024, _W("Recursion limit reached (%d).\n").data(), ConfigVariable::getRecursionLimit());
124             throw ast::InternalError(sz);
125         }
126     }
127     catch (const ast::InternalError& se)
128     {
129         if (runMe->getCommandOrigin() == TCLSCI)
130         {
131             ConfigVariable::setPromptMode(iOldPromptMode);
132         }
133
134         std::wostringstream ostr;
135         ConfigVariable::whereErrorToString(ostr);
136         scilabErrorW(ostr.str().c_str());
137         scilabErrorW(se.GetErrorMessage().c_str());
138         ConfigVariable::resetWhereError();
139         iRet = 1;
140     }
141     catch (const ast::InternalAbort& ia)
142     {
143         if (runMe->getCommandOrigin() == TCLSCI)
144         {
145             ConfigVariable::setPromptMode(iOldPromptMode);
146         }
147
148         // management of pause
149         if (ConfigVariable::getPauseLevel())
150         {
151             ConfigVariable::DecreasePauseLevel();
152             throw ia;
153         }
154
155         // close all scope before return to console scope
156         symbol::Context* pCtx = symbol::Context::getInstance();
157         while (pCtx->getScopeLevel() > scope)
158         {
159             pCtx->scope_end();
160         }
161
162         // debugger leave with abort state
163         manager->setAborted();
164
165         // send the good signal about the end of execution
166         sendExecDoneSignal();
167
168         throw ia;
169     }
170
171     if (runMe->getCommandOrigin() == TCLSCI)
172     {
173         ConfigVariable::setPromptMode(iOldPromptMode);
174     }
175
176     if (getScilabMode() != SCILAB_NWNI && getScilabMode() != SCILAB_API)
177     {
178         char *cwd = NULL;
179         int err = 0;
180
181         UpdateBrowseVar();
182         saveScilabHistoryToFile();
183         cwd = scigetcwd(&err);
184         if (cwd)
185         {
186             FileBrowserChDir(cwd);
187             FREE(cwd);
188         }
189     }
190
191     // reset error state when new prompt occurs
192     ConfigVariable::resetError();
193
194     // send the good signal about the end of execution
195     sendExecDoneSignal();
196
197     //clean debugger step flag if debugger is not interrupted ( end of debug )
198     manager->resetStep();
199     return iRet;
200 }
201
202 void StaticRunner::setRunner(Runner* _RunMe)
203 {
204     m_RunMe = _RunMe;
205 }
206
207 void StaticRunner::setCurrentRunner(Runner* _RunMe)
208 {
209     m_CurrentRunner = _RunMe;
210 }
211
212 Runner* StaticRunner::getRunner(void)
213 {
214     Runner* tmp = m_RunMe.exchange(nullptr);
215     setCurrentRunner(tmp);
216     ThreadManagement::SendAvailableRunnerSignal();
217     return tmp;
218 }
219
220 Runner* StaticRunner::getCurrentRunner(void)
221 {
222     return m_CurrentRunner.load();
223 }
224
225 // return true if a Runner is already set in m_RunMe.
226 bool StaticRunner::isRunnerAvailable(void)
227 {
228     return m_RunMe.load() != nullptr;
229 }
230
231 void StaticRunner::setInterruptibleCommand(bool _bInterruptibleCommand)
232 {
233     m_bInterruptibleCommand = _bInterruptibleCommand;
234 }
235
236 bool StaticRunner::isInterruptibleCommand()
237 {
238     return m_bInterruptibleCommand;
239 }
240
241 command_origin_t StaticRunner::getCommandOrigin()
242 {
243     return m_RunMe.load()->getCommandOrigin();
244 }
245
246 command_origin_t StaticRunner::getCurrentCommandOrigin()
247 {
248     return m_CurrentRunner.load()->getCommandOrigin();
249 }
250
251 void StaticRunner::setCurrentCommandOrigin(command_origin_t origin)
252 {
253     m_CurrentRunner.load()->setCommandOrigin(origin);
254 }
255
256 void StaticRunner::execAndWait(ast::Exp* _theProgram, ast::RunVisitor *_visitor,
257                                bool /*_isPrioritaryThread*/, bool _isInterruptible, command_origin_t _iCommandOrigin)
258 {
259     if (isRunnerAvailable())
260     {
261         // wait for managenement of last Runner
262         ThreadManagement::WaitForAvailableRunnerSignal();
263     }
264
265     // lock runner to be sure we are waiting for
266     // "AwakeRunner" signal before start execution
267     ThreadManagement::LockRunner();
268     Runner *runMe = new Runner(_theProgram, _visitor, _iCommandOrigin, _isInterruptible);
269     setRunner(runMe);
270
271     ThreadManagement::SendRunMeSignal();
272     ThreadManagement::WaitForAwakeRunnerSignal();
273 }
274
275 bool StaticRunner::exec(ast::Exp* _theProgram, ast::RunVisitor *_visitor)
276 {
277     Runner *runMe = new Runner(_theProgram, _visitor);
278     setRunner(runMe);
279
280     try
281     {
282         launch();
283     }
284     catch (const ast::InternalAbort& /*ia*/)
285     {
286         //catch exit command in .start or .quit
287         return false;
288     }
289
290     return true;
291 }
292
293 void StaticRunner_launch(void)
294 {
295     StaticRunner::launch();
296 }
297
298 int StaticRunner_isRunnerAvailable(void)
299 {
300     return StaticRunner::isRunnerAvailable() ? 1 : 0;
301 }
302
303 int StaticRunner_isInterruptibleCommand(void)
304 {
305     return StaticRunner::isInterruptibleCommand() ? 1 : 0;
306 }
307
308 void StaticRunner_setInterruptibleCommand(int val)
309 {
310     StaticRunner::setInterruptibleCommand(val == 1);
311 }
312
313 command_origin_t StaticRunner_getCommandOrigin(void)
314 {
315     return StaticRunner::getCommandOrigin();
316 }
317
318 command_origin_t StaticRunner_getCurrentCommandOrigin(void)
319 {
320     return StaticRunner::getCurrentCommandOrigin();
321 }
322
323 void StaticRunner_setCurrentCommandOrigin(command_origin_t _origin)
324 {
325     StaticRunner::setCurrentCommandOrigin(_origin);
326 }