Command history: Save in file before each execution to avoid to lose it in case of...
[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<bool> StaticRunner::m_bInterruptibleCommand(true);
34
35 static bool initialJavaHooks = false;
36
37 static void sendExecDoneSignal(Runner* _pRunner)
38 {
39     switch (_pRunner->getCommandOrigin())
40     {
41         case CONSOLE :
42         {
43             ThreadManagement::SendConsoleExecDoneSignal();
44             break;
45         }
46         case TCLSCI :
47         case NONE :
48         default :
49         {}
50     }
51 }
52
53 int StaticRunner::launch()
54 {
55     //set execution thread in java
56     if (!initialJavaHooks && getScilabMode() != SCILAB_NWNI)
57     {
58         initialJavaHooks = true;
59         // Execute the initial hooks registered in Scilab.java
60         ExecuteInitialHooks();
61     }
62
63     int iRet = 0;
64     // get the runner to execute
65     std::unique_ptr<Runner> runMe(getRunner());
66     // set if the current comment is interruptible
67     setInterruptibleCommand(runMe->isInterruptible());
68     debugger::DebuggerMagager* manager = debugger::DebuggerMagager::getInstance();
69
70     ConfigVariable::resetExecutionBreak();
71
72     int oldMode = ConfigVariable::getPromptMode();
73     symbol::Context* pCtx = symbol::Context::getInstance();
74     int scope = pCtx->getScopeLevel();
75
76     // a TCL command display nothing
77     int iOldPromptMode = 0;
78     if (runMe->getCommandOrigin() == TCLSCI)
79     {
80         iOldPromptMode = ConfigVariable::getPromptMode();
81         ConfigVariable::setPromptMode(-1);
82     }
83
84     try
85     {
86         int level = ConfigVariable::getRecursionLevel();
87         try
88         {
89             runMe->getProgram()->accept(*(runMe->getVisitor()));
90         }
91         catch (const ast::RecursionException& re)
92         {
93             // management of pause
94             if (ConfigVariable::getPauseLevel())
95             {
96                 ConfigVariable::DecreasePauseLevel();
97                 throw re;
98             }
99
100             //close opened scope during try
101             while (pCtx->getScopeLevel() > scope)
102             {
103                 pCtx->scope_end();
104             }
105
106             //decrease recursion to init value and close where
107             while (ConfigVariable::getRecursionLevel() > level)
108             {
109                 ConfigVariable::where_end();
110                 ConfigVariable::decreaseRecursion();
111             }
112
113             ConfigVariable::resetWhereError();
114             ConfigVariable::setPromptMode(oldMode);
115
116             //print msg about recursion limit and trigger an error
117             wchar_t sz[1024];
118             os_swprintf(sz, 1024, _W("Recursion limit reached (%d).\n").data(), ConfigVariable::getRecursionLimit());
119             throw ast::InternalError(sz);
120         }
121     }
122     catch (const ast::InternalError& se)
123     {
124         if (runMe->getCommandOrigin() == TCLSCI)
125         {
126             ConfigVariable::setPromptMode(iOldPromptMode);
127         }
128
129         std::wostringstream ostr;
130         ConfigVariable::whereErrorToString(ostr);
131         scilabErrorW(ostr.str().c_str());
132         scilabErrorW(se.GetErrorMessage().c_str());
133         ConfigVariable::resetWhereError();
134         iRet = 1;
135     }
136     catch (const ast::InternalAbort& ia)
137     {
138         if (runMe->getCommandOrigin() == TCLSCI)
139         {
140             ConfigVariable::setPromptMode(iOldPromptMode);
141         }
142
143         // management of pause
144         if (ConfigVariable::getPauseLevel())
145         {
146             ConfigVariable::DecreasePauseLevel();
147             throw ia;
148         }
149
150         // close all scope before return to console scope
151         symbol::Context* pCtx = symbol::Context::getInstance();
152         while (pCtx->getScopeLevel() > scope)
153         {
154             pCtx->scope_end();
155         }
156
157         // send the good signal about the end of execution
158         sendExecDoneSignal(runMe.get());
159
160         //clean debugger step flag if debugger is not interrupted ( end of debug )
161         manager->resetStep();
162         throw ia;
163     }
164
165     if (runMe->getCommandOrigin() == TCLSCI)
166     {
167         ConfigVariable::setPromptMode(iOldPromptMode);
168     }
169
170     if (getScilabMode() != SCILAB_NWNI && getScilabMode() != SCILAB_API)
171     {
172         char *cwd = NULL;
173         int err = 0;
174
175         UpdateBrowseVar();
176         saveScilabHistoryToFile();
177         cwd = scigetcwd(&err);
178         if (cwd)
179         {
180             FileBrowserChDir(cwd);
181             FREE(cwd);
182         }
183     }
184
185     // reset error state when new prompt occurs
186     ConfigVariable::resetError();
187
188     // send the good signal about the end of execution
189     sendExecDoneSignal(runMe.get());
190
191     //clean debugger step flag if debugger is not interrupted ( end of debug )
192     manager->resetStep();
193     return iRet;
194 }
195
196 void StaticRunner::setRunner(Runner* _RunMe)
197 {
198     m_RunMe = _RunMe;
199 }
200
201 Runner* StaticRunner::getRunner(void)
202 {
203     Runner* tmp = m_RunMe.exchange(nullptr);
204     ThreadManagement::SendAvailableRunnerSignal();
205     return tmp;
206 }
207
208 // return true if a Runner is already set in m_RunMe.
209 bool StaticRunner::isRunnerAvailable(void)
210 {
211     return m_RunMe.load() != nullptr;
212 }
213
214 void StaticRunner::setInterruptibleCommand(bool _bInterruptibleCommand)
215 {
216     m_bInterruptibleCommand = _bInterruptibleCommand;
217 }
218
219 bool StaticRunner::isInterruptibleCommand()
220 {
221     return m_bInterruptibleCommand;
222 }
223
224 command_origin_t StaticRunner::getCommandOrigin()
225 {
226     return m_RunMe.load()->getCommandOrigin();
227 }
228
229 void StaticRunner::execAndWait(ast::Exp* _theProgram, ast::RunVisitor *_visitor,
230                                bool /*_isPrioritaryThread*/, bool _isInterruptible, command_origin_t _iCommandOrigin)
231 {
232     if (isRunnerAvailable())
233     {
234         // wait for managenement of last Runner
235         ThreadManagement::WaitForAvailableRunnerSignal();
236     }
237
238     // lock runner to be sure we are waiting for
239     // "AwakeRunner" signal before start execution
240     ThreadManagement::LockRunner();
241     Runner *runMe = new Runner(_theProgram, _visitor, _iCommandOrigin, _isInterruptible);
242     setRunner(runMe);
243
244     ThreadManagement::SendRunMeSignal();
245     ThreadManagement::WaitForAwakeRunnerSignal();
246 }
247
248 bool StaticRunner::exec(ast::Exp* _theProgram, ast::RunVisitor *_visitor)
249 {
250     Runner *runMe = new Runner(_theProgram, _visitor);
251     setRunner(runMe);
252
253     try
254     {
255         launch();
256     }
257     catch (const ast::InternalAbort& /*ia*/)
258     {
259         //catch exit command in .start or .quit
260         return false;
261     }
262
263     return true;
264 }
265
266 void StaticRunner_launch(void)
267 {
268     StaticRunner::launch();
269 }
270
271 int StaticRunner_isRunnerAvailable(void)
272 {
273     return StaticRunner::isRunnerAvailable() ? 1 : 0;
274 }
275
276 int StaticRunner_isInterruptibleCommand(void)
277 {
278     return StaticRunner::isInterruptibleCommand() ? 1 : 0;
279 }
280
281 void StaticRunner_setInterruptibleCommand(int val)
282 {
283     StaticRunner::setInterruptibleCommand(val == 1);
284 }
285
286 command_origin_t StaticRunner_getCommandOrigin(void)
287 {
288     return StaticRunner::getCommandOrigin();
289 }