memory leak fixed in library management.
[scilab.git] / scilab / modules / ast / src / cpp / symbol / context.cpp
1 /*
2 *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 *  Copyright (C) 2008-2008 - DIGITEO - Antoine ELIAS
4 *
5 *  This file must be used under the terms of the CeCILL.
6 *  This source file is licensed as described in the file COPYING, which
7 *  you should have received as part of this distribution.  The terms
8 *  are also available at
9 *  http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
10 *
11 */
12 #include <iomanip>
13 #include <cctype>
14
15 #include "context.hxx"
16 #include "internal.hxx"
17 #include "function.hxx"
18 #include "macro.hxx"
19 #include "macrofile.hxx"
20 #include "variables.hxx"
21 #include "configvariable.hxx"
22
23 extern "C"
24 {
25 #include "getmemory.h"
26 #include "os_string.h"
27 }
28
29 namespace symbol
30 {
31 Context* Context::me;
32
33 Context::Context()
34 {
35     m_iLevel = 0;
36     varStack.push(new VarList());
37     globals = new std::list<Symbol>();
38 }
39
40 Context::~Context()
41 {
42     while (!varStack.empty())
43     {
44         VarList * pVL = varStack.top();
45         pVL->clear();
46         delete pVL;
47         varStack.pop();
48     }
49     if (globals)
50     {
51         delete globals;
52     }
53 }
54
55 Context* Context::getInstance(void)
56 {
57     if (me == 0)
58     {
59         me = new Context();
60     }
61     return me;
62 }
63
64 void Context::destroyInstance(void)
65 {
66     if (me)
67     {
68         delete me;
69     }
70 }
71
72 void Context::scope_begin()
73 {
74     m_iLevel++;
75     varStack.push(new VarList());
76 }
77
78 void Context::clearAll()
79 {
80     libraries.clearAll();
81     variables.clearAll();
82 }
83
84 void Context::scope_end()
85 {
86     //clear varList of current scope
87     if (varStack.empty() == false)
88     {
89         clearCurrentScope(true);
90     }
91
92     m_iLevel--;
93 }
94
95 bool Context::clearCurrentScope(bool _bClose)
96 {
97     if (varStack.empty())
98     {
99         return true;
100     }
101
102     VarList* varList = varStack.top();
103     std::list<Symbol> toremove;
104     for (auto var : *varList)
105     {
106         if (var.second->empty() == false)
107         {
108             ScopedVariable* pSV = var.second->top();
109             if (pSV->m_iLevel == m_iLevel && (_bClose || pSV->protect == false))
110             {
111                 types::InternalType * pIT = pSV->m_pIT;
112                 if (pIT->isLibrary())
113                 {
114                     // at scilab exit, pIT have a ref == 2 because
115                     // it is contains in libraries and variables.
116                     // call remove will set ref to 1 then the next
117                     // pIT->DecreaseRef(); pIT->killMe(); will delete it.
118                     libraries.remove(var.first, m_iLevel);
119                 }
120
121                 pIT->DecreaseRef();
122                 pIT->killMe();
123
124                 var.second->pop();
125                 delete pSV;
126                 toremove.push_back(var.first);
127             }
128         }
129     }
130
131     if (_bClose)
132     {
133         delete varList;
134         varStack.pop();
135     }
136     else
137     {
138         for (auto var : toremove)
139         {
140             varList->erase(var);
141         }
142     }
143     return true;
144 }
145
146 Variable* Context::getOrCreate(const Symbol& _key)
147 {
148     return variables.getOrCreate(_key);
149 }
150
151 types::InternalType* Context::get(const Symbol& _key)
152 {
153     return get(_key, -1);
154 }
155
156 types::InternalType* Context::get(const Variable* _var)
157 {
158     types::InternalType* pIT = _var->get();
159     if (pIT == NULL)
160     {
161         //look in libraries
162         pIT = libraries.get(_var->getSymbol(), -1);
163         if (pIT && pIT->isLibrary() == false)
164         {
165             put((Variable*)_var, pIT);
166         }
167     }
168
169     return pIT;
170 }
171
172 types::InternalType* Context::get(const Symbol& _key, int _iLevel)
173 {
174     types::InternalType* pIT = NULL;
175     if (_iLevel == m_iLevel || _iLevel == -1)
176     {
177         //look for in current VarList
178         VarList::iterator it = varStack.top()->find(_key);
179         if (it != varStack.top()->end())
180         {
181             if (it->second->empty() == false)
182             {
183                 pIT = it->second->get();
184             }
185         }
186     }
187
188     if (pIT == NULL)
189     {
190         pIT = variables.get(_key, _iLevel);
191         if (pIT == NULL)
192         {
193             //find in libraries
194             pIT = libraries.get(_key, _iLevel);
195         }
196     }
197     return pIT;
198 }
199
200 types::InternalType* Context::getCurrentLevel(const Symbol& _key)
201 {
202     return variables.get(_key, m_iLevel);
203 }
204
205 types::InternalType* Context::getCurrentLevel(Variable* _var)
206 {
207     return variables.get(_var, m_iLevel);
208 }
209
210 types::InternalType* Context::getAllButCurrentLevel(const Symbol& _key)
211 {
212     return variables.getAllButCurrentLevel(_key, m_iLevel);
213 }
214
215 types::InternalType* Context::getFunction(const Symbol& _key)
216 {
217     return get(_key);
218 }
219
220 int Context::getFunctionList(std::list<Symbol>& lst, std::wstring _stModuleName)
221 {
222     return variables.getFunctionList(lst, _stModuleName, m_iLevel);
223 }
224
225 int Context::getVarsName(std::list<std::wstring>& lst)
226 {
227     variables.getVarsName(lst);
228     libraries.getVarsName(lst);
229
230     return static_cast<int>(lst.size());
231 }
232
233 int Context::getMacrosName(std::list<std::wstring>& lst)
234 {
235     variables.getMacrosName(lst);
236     libraries.getMacrosName(lst);
237     return static_cast<int>(lst.size());
238 }
239
240 int Context::getFunctionsName(std::list<std::wstring>& lst)
241 {
242     return variables.getFunctionsName(lst);
243 }
244
245 int Context::getVarsNameForWho(std::list<std::wstring>& lst, bool bSorted)
246 {
247     int iZero = 0;
248     variables.getVarsNameForWho(lst, &iZero, bSorted);
249     return static_cast<int>(lst.size());
250 }
251
252 int Context::getGlobalNameForWho(std::list<std::wstring>& lst, bool bSorted)
253 {
254     int iZero = 0;
255     variables.getGlobalNameForWho(lst, &iZero, bSorted);
256     return static_cast<int>(lst.size());
257 }
258
259 int Context::getWhereIs(std::list<std::wstring>& lst, const std::wstring& _str)
260 {
261     return libraries.whereis(lst, Symbol(_str));
262 }
263
264 int Context::getLibrariesList(std::list<std::wstring>& lst)
265 {
266     return libraries.librarieslist(lst);
267 }
268
269 void Context::put(Variable* _var, types::InternalType* _pIT)
270 {
271     if (_pIT->isLibrary())
272     {
273         Library* lib = libraries.getOrCreate(_var->getSymbol());
274         lib->put((types::Library*)_pIT, m_iLevel);
275     }
276
277     _var->put(_pIT, m_iLevel);
278     if (varStack.empty() == false)
279     {
280         (*varStack.top())[_var->getSymbol()] = _var;
281     }
282 }
283
284 void Context::put(const Symbol& _key, types::InternalType* _pIT)
285 {
286     Variable* var = variables.getOrCreate(_key);
287     put(var, _pIT);
288 }
289
290 bool Context::remove(const Symbol& _key)
291 {
292     bool ret = variables.remove(_key, m_iLevel);
293
294     if (ret)
295     {
296         varStack.top()->erase(_key);
297     }
298
299     ret = ret | libraries.remove(_key, m_iLevel);
300     return ret;
301 }
302
303 bool Context::removeAll()
304 {
305     return clearCurrentScope(false);
306 }
307
308 bool Context::putInPreviousScope(Variable* _var, types::InternalType* _pIT)
309 {
310     //add variable in previous scope
311     variables.putInPreviousScope(_var, _pIT, m_iLevel - 1);
312
313     //add variable in stack of using variables
314     if (varStack.empty() == false)
315     {
316         VarList * list = varStack.top();
317         varStack.pop();
318         if (varStack.empty() == false)
319         {
320             (*varStack.top())[_var->getSymbol()] = _var;
321         }
322         varStack.push(list);
323     }
324
325     if (_pIT->isLibrary())
326     {
327         libraries.putInPreviousScope(_var->getSymbol(), _pIT->getAs<types::Library>(), m_iLevel - 1);
328     }
329     return true;
330 }
331
332 bool Context::addFunction(types::Function *_info)
333 {
334     Variable* var = variables.getOrCreate(Symbol(_info->getName()));
335     variables.putInPreviousScope(var, _info, 0);
336     return true;
337 }
338
339 bool Context::addMacro(types::Macro *_info)
340 {
341     put(Symbol(_info->getName()), _info);
342     return true;
343 }
344
345 bool Context::addMacroFile(types::MacroFile *_info)
346 {
347     put(Symbol(_info->getName()), _info);
348     return true;
349 }
350
351 bool Context::isGlobalVisible(const Symbol& _key)
352 {
353     return variables.isGlobalVisible(_key, m_iLevel);
354 }
355
356 /*return global variable existance status*/
357 bool Context::isGlobal(const Symbol& _key)
358 {
359     return variables.isGlobal(_key, m_iLevel);
360 }
361
362 types::InternalType* Context::getGlobalValue(const Symbol& _key)
363 {
364     return variables.getGlobalValue(_key);
365 }
366
367 void Context::setGlobalVisible(const Symbol& _key, bool bVisible)
368 {
369     variables.setGlobalVisible(_key, bVisible, m_iLevel);
370 }
371
372 void Context::setGlobal(const Symbol& _key)
373 {
374     variables.setGlobal(_key);
375     globals->push_back(_key);
376 }
377
378 void Context::removeGlobal(const Symbol& _key)
379 {
380     variables.removeGlobal(_key, m_iLevel);
381     globals->remove(_key);
382 }
383
384 void Context::removeGlobalAll()
385 {
386     std::list<Symbol>::iterator it = globals->begin();
387     while (it != globals->end())
388     {
389         removeGlobal(*it);
390         it = globals->begin();
391     }
392
393     globals->clear();
394 }
395
396 void Context::print(std::wostream& ostr, bool sorted) const
397 {
398     std::list<std::wstring> lstVar;
399     std::list<std::wstring> lstGlobal;
400     int iVarLenMax = 10; // initialise to the minimal value of padding
401     int iGlobalLenMax = 10; // initialise to the minimal value of padding
402     variables.getVarsNameForWho(lstVar, &iVarLenMax);
403     variables.getGlobalNameForWho(lstGlobal, &iGlobalLenMax);
404     libraries.getVarsNameForWho(&lstVar, &iVarLenMax);
405
406     if (sorted)
407     {
408         lstVar.sort();
409         lstGlobal.sort();
410     }
411
412 #define strSize 64
413     wchar_t wcsVarElem[strSize];
414     wchar_t wcsVarVariable[strSize];
415     wchar_t wcsGlobalElem[strSize];
416     wchar_t wcsGlobalVariable[strSize];
417
418     int iMemTotal = 0;
419     int iMemUsed = 0;
420     int nbMaxVar = 0;
421
422 #ifdef _MSC_VER
423     MEMORYSTATUSEX statex;
424     statex.dwLength = sizeof(statex);
425     GlobalMemoryStatusEx(&statex);
426     iMemTotal = (int)(statex.ullTotalPhys / (1024 * 1024));
427 #else
428     iMemTotal = getmemorysize();
429 #endif
430
431     ostr << _W("Your variables are:") << std::endl << std::endl;
432     std::list<std::wstring>::const_iterator it = lstVar.begin();
433     int iWidth = ConfigVariable::getConsoleWidth();
434     int iCurrentWidth = 0;
435     for (int i = 1; it != lstVar.end(); ++it, i++)
436     {
437         if (iCurrentWidth + iVarLenMax + 1 > iWidth)
438         {
439             ostr << std::endl;
440             iCurrentWidth = 0;
441         }
442         ostr << std::setw(iVarLenMax + 1) << *it;
443         iCurrentWidth += iVarLenMax + 1;
444     }
445
446     os_swprintf(wcsVarElem, strSize, _W(" using %10d elements out of  %10d.\n").c_str(), iMemUsed, iMemTotal);
447     ostr << std::endl << wcsVarElem;
448
449     os_swprintf(wcsVarVariable, strSize, _W(" and   %10d variables out of %10d.\n").c_str(), lstVar.size(), nbMaxVar);
450     ostr << wcsVarVariable << std::endl;
451
452     ostr << std::endl << _W("Your global variables are:") << std::endl << std::endl;
453     it = lstGlobal.begin();
454     for (int i = 1; it != lstGlobal.end(); ++it, i++)
455     {
456         ostr << std::setw(iGlobalLenMax + 1) << *it;
457         if (i % 4 == 0)
458         {
459             ostr << std::endl;
460         }
461     }
462
463     ostr << std::endl;
464
465     os_swprintf(wcsGlobalElem, strSize, _W(" using %10d elements out of  %10d.\n").c_str(), iMemUsed, iMemTotal);
466     ostr << std::endl << wcsGlobalElem;
467
468     os_swprintf(wcsGlobalVariable, strSize, _W(" and   %10d variables out of %10d.\n").c_str(), lstGlobal.size(), nbMaxVar);
469     ostr << wcsGlobalVariable;
470 }
471
472 int Context::getScopeLevel()
473 {
474     return m_iLevel;
475 }
476
477 bool Context::isValidVariableName(const wchar_t* wcsVarName)
478 {
479     static const wchar_t FORBIDDEN_CHARS[] = L" */\\.,;:^@><=+-&|()~\n\t'\"";
480     if (wcslen(wcsVarName) == 0 || std::wcspbrk(wcsVarName, FORBIDDEN_CHARS) || isdigit(wcsVarName[0]))
481     {
482         return false;
483     }
484     return true;
485 }
486
487 bool Context::isValidVariableName(const char* name)
488 {
489     bool isValid = false;
490     wchar_t* wcsname = to_wide_string(name);
491     if (wcsname)
492     {
493         isValid = isValidVariableName(wcsname);
494         FREE(wcsname);
495     }
496
497     return isValid;
498 }
499
500 int Context::getLibsToVariableBrowser(std::list<Library*>& lst)
501 {
502     libraries.getVarsToVariableBrowser(lst);
503
504     std::list<Library*> toremove;
505     //list lib that have a variable with the same name
506     for (auto lib : lst)
507     {
508         Variable* var = getOrCreate(lib->getSymbol());
509         if (var->empty() == false)
510         {
511             toremove.push_back(lib);
512         }
513     }
514
515     //remove
516     for (auto lib : toremove)
517     {
518         lst.remove(lib);
519     }
520
521     return static_cast<int>(lst.size());
522 }
523
524 int Context::getVarsToVariableBrowser(std::list<Variable*>& lst)
525 {
526     variables.getVarsToVariableBrowser(lst);
527     return static_cast<int>(lst.size());
528 }
529
530 void Context::updateProtection(bool protect)
531 {
532     if (varStack.empty() == false)
533     {
534         VarList* lst = varStack.top();
535         for (auto var : *lst)
536         {
537             if (var.second->empty() == false)
538             {
539                 ScopedVariable* pSV = var.second->top();
540                 //only for current scope but normally vars in VarStack are in the current scope
541                 if (pSV->m_iLevel == m_iLevel)
542                 {
543                     pSV->protect = protect;
544                 }
545                 else
546                 {
547                     std::wcerr << L"heu ... " << var.first.getName() << std::endl;
548                 }
549             }
550         }
551     }
552 }
553
554 void Context::protect()
555 {
556     updateProtection(true);
557 }
558
559 void Context::unprotect()
560 {
561     updateProtection(false);
562 }
563
564 bool Context::isprotected(const Symbol& key)
565 {
566     return isprotected(getOrCreate(key));
567 }
568
569 bool Context::isprotected(Variable* _var)
570 {
571     //don't check protection on "ans"
572     if (_var->getSymbol().getName() == L"ans")
573     {
574         return false;
575     }
576
577     if (_var->empty() == false)
578     {
579         ScopedVariable* pSV = _var->top();
580         if (pSV->m_iLevel == m_iLevel && pSV->protect)
581         {
582             return true;
583         }
584     }
585     return false;
586 }
587
588 int Context::protectedVars(std::list<std::wstring>& vars)
589 {
590     return variables.getProtectedVarsName(vars);
591 }
592 }