fix valgrind issues during ast serialization
[scilab.git] / scilab / modules / ast / src / cpp / types / macro.cpp
1 /*
2 *  Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 *  Copyright (C) 2009-2009 - DIGITEO - Bruno JOFRET
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
13 #include <sstream>
14 #include <cstdio>
15
16 #include "macro.hxx"
17 #include "list.hxx"
18 #include "context.hxx"
19 #include "symbol.hxx"
20 #include "scilabWrite.hxx"
21 #include "scilabexception.hxx"
22 #include "configvariable.hxx"
23 #include "mutevisitor.hxx"
24 #include "serializervisitor.hxx"
25
26 extern "C"
27 {
28 #include "localization.h"
29 #include "Scierror.h"
30 #include "sciprint.h"
31 #include "sci_malloc.h"
32 #include "os_swprintf.h"
33 }
34
35 namespace types
36 {
37 Macro::Macro(const std::wstring& _stName, std::list<symbol::Variable*>& _inputArgs, std::list<symbol::Variable*>& _outputArgs, ast::SeqExp &_body, const std::wstring& _stModule):
38     Callable(),
39     m_inputArgs(&_inputArgs), m_outputArgs(&_outputArgs), m_body(&_body),
40     m_Nargin(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"nargin"))),
41     m_Nargout(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"nargout"))),
42     m_Varargin(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"varargin"))),
43     m_Varargout(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"varargout")))
44 {
45     setName(_stName);
46     setModule(_stModule);
47     bAutoAlloc = false;
48     m_pDblArgIn = new Double(1);
49     m_pDblArgIn->IncreaseRef(); //never delete
50     m_pDblArgOut = new Double(1);
51     m_pDblArgOut->IncreaseRef(); //never delete
52
53 }
54
55 Macro::~Macro()
56 {
57     //delete m_body;
58     m_pDblArgIn->DecreaseRef();
59     m_pDblArgIn->killMe();
60     m_pDblArgOut->DecreaseRef();
61     m_pDblArgOut->killMe();
62
63     if (m_inputArgs)
64     {
65         delete m_inputArgs;
66     }
67
68     if (m_outputArgs)
69     {
70         delete m_outputArgs;
71     }
72 }
73
74 void Macro::cleanCall(symbol::Context * pContext, int oldPromptMode)
75 {
76     //restore previous prompt mode
77     ConfigVariable::setPromptMode(oldPromptMode);
78     //close the current scope
79     pContext->scope_end();
80     ConfigVariable::macroFirstLine_end();
81 }
82
83 InternalType* Macro::clone()
84 {
85     IncreaseRef();
86     return this;
87 }
88
89 void Macro::whoAmI()
90 {
91     std::cout << "types::Macro";
92 }
93
94 ast::SeqExp* Macro::getBody(void)
95 {
96     return m_body;
97 }
98
99 bool Macro::toString(std::wostringstream& ostr)
100 {
101     ostr << L"FIXME : Implement Macro::toString" << std::endl;
102     return true;
103 }
104
105 Callable::ReturnValue Macro::call(typed_list &in, optional_list &opt, int _iRetCount, typed_list &out, ast::ConstVisitor* execFunc)
106 {
107     bool bVarargout = false;
108     ReturnValue RetVal = Callable::OK;
109     symbol::Context *pContext = symbol::Context::getInstance();
110
111     //open a new scope
112     pContext->scope_begin();
113     //store the line number where is stored this macro in file.
114     ConfigVariable::macroFirstLine_begin(getFirstLine());
115
116     //check excepted and input/output parameters numbers
117     // Scilab Macro can be called with less than prototyped arguments,
118     // but not more execpts with varargin
119
120     // varargin management
121     if (m_inputArgs->size() > 0 && m_inputArgs->back()->name_get().name_get() == L"varargin")
122     {
123         int iVarPos = static_cast<int>(in.size());
124         if (iVarPos > static_cast<int>(m_inputArgs->size()) - 1)
125         {
126             iVarPos = static_cast<int>(m_inputArgs->size()) - 1;
127         }
128
129         //add all standard variable in function context but not varargin
130         std::list<symbol::Variable*>::iterator itName = m_inputArgs->begin();
131         typed_list::const_iterator itValue = in.begin();
132         while (iVarPos > 0)
133         {
134             pContext->put(*itName, *itValue);
135             iVarPos--;
136             ++itName;
137             ++itValue;
138         }
139
140         //create varargin only if previous variable are assigned
141         optional_list::const_iterator it = opt.begin();
142         if (in.size() >= m_inputArgs->size() - 1)
143         {
144             //create and fill varargin
145             List* pL = new List();
146             while (itValue != in.end())
147             {
148                 if (*itValue != NULL)
149                 {
150                     pL->append(*itValue);
151                 }
152                 else
153                 {
154                     pL->append(it->second);
155                     it++;
156                 }
157
158                 itValue++;
159             }
160             pContext->put(m_Varargin, pL);
161         }
162     }
163     else if (in.size() > m_inputArgs->size())
164     {
165         if (m_inputArgs->size() == 0)
166         {
167             Scierror(999, _("Wrong number of input arguments: This function has no input argument.\n"));
168         }
169         else
170         {
171             Scierror(999, _("Wrong number of input arguments."));
172         }
173
174         pContext->scope_end();
175         ConfigVariable::macroFirstLine_end();
176         return Callable::Error;
177     }
178     else
179     {
180         //add optional paramter in current scope
181         optional_list::const_iterator it;
182         for (it = opt.begin() ; it != opt.end() ; it++)
183         {
184             pContext->put(symbol::Symbol(it->first), it->second);
185         }
186
187         //assign value to variable in the new context
188         std::list<symbol::Variable*>::iterator i;
189         typed_list::const_iterator j;
190
191         for (i = m_inputArgs->begin(), j = in.begin(); j != in.end (); ++j, ++i)
192         {
193             if (*j)
194             {
195                 //prevent assignation of NULL value
196                 pContext->put(*i, *j);
197             }
198         }
199     }
200
201     // varargout management
202     //rules :
203     // varargout must be alone
204     // varargout is a list
205     // varargout can containt more items than caller need
206     // varargout must containt at leat caller needs
207     if (m_outputArgs->size() == 1 && m_outputArgs->back()->name_get().name_get() == L"varargout")
208     {
209         bVarargout = true;
210         List* pL = new List();
211         pContext->put(m_Varargout, pL);
212     }
213
214     //common part with or without varargin/varargout
215
216     // Declare nargin & nargout in function context.
217     if (m_pDblArgIn->getRef() > 1)
218     {
219         m_pDblArgIn->DecreaseRef();
220         m_pDblArgIn = (Double*)m_pDblArgIn->clone();
221         m_pDblArgIn->IncreaseRef();
222     }
223     m_pDblArgIn->set(0, static_cast<double>(in.size()));
224
225     if (m_pDblArgOut->getRef() > 1)
226     {
227         m_pDblArgOut->DecreaseRef();
228         m_pDblArgOut = (Double*)m_pDblArgOut->clone();
229         m_pDblArgOut->IncreaseRef();
230     }
231     m_pDblArgOut->set(0, _iRetCount);
232
233     pContext->put(m_Nargin, m_pDblArgIn);
234     pContext->put(m_Nargout, m_pDblArgOut);
235
236     //save current prompt mode
237     int oldVal = ConfigVariable::getPromptMode();
238     try
239     {
240         //m_body->mute();
241         //MuteVisitor mute;
242         //m_body->accept(mute);
243
244         ConfigVariable::setPromptMode(-1);
245         m_body->returnable_set();
246         m_body->accept(*execFunc);
247         //restore previous prompt mode
248         ConfigVariable::setPromptMode(oldVal);
249         if (m_body->is_return())
250         {
251             m_body->returnable_set();
252         }
253     }
254     catch (ast::ScilabMessage & sm)
255     {
256         cleanCall(pContext, oldVal);
257         throw sm;
258     }
259     catch (ast::InternalAbort & ia)
260     {
261         cleanCall(pContext, oldVal);
262         throw ia;
263     }
264     // Normally, seqexp throws only SM so no need to catch SErr
265
266     //varargout management
267     if (bVarargout)
268     {
269         InternalType* pOut = pContext->get(m_Varargout);
270         if (pOut == NULL)
271         {
272             cleanCall(pContext, oldVal);
273             Scierror(999, _("Invalid index.\n"));
274             return Callable::Error;
275         }
276
277         if (pOut->isList() == false || pOut->getAs<List>()->getSize() == 0)
278         {
279             cleanCall(pContext, oldVal);
280             Scierror(999, _("Invalid index.\n"));
281             return Callable::Error;
282         }
283
284         List* pVarOut = pOut->getAs<List>();
285         const int size = std::max(pVarOut->getSize(), _iRetCount);
286         for (int i = 0 ; i < size ; ++i)
287         {
288             InternalType* pIT = pVarOut->get(i);
289             if (pIT->isListUndefined())
290             {
291                 for (int j = 0; j < i; ++j)
292                 {
293                     out[j]->DecreaseRef();
294                     out[j]->killMe();
295                 }
296                 out.clear();
297                 cleanCall(pContext, oldVal);
298
299                 Scierror(999, _("List element number %d is Undefined.\n"), i + 1);
300                 return Callable::Error;
301             }
302
303             out.push_back(pIT->clone());
304         }
305     }
306     else
307     {
308         //normal output management
309         for (std::list<symbol::Variable*>::iterator i = m_outputArgs->begin(); i != m_outputArgs->end() && _iRetCount; ++i, --_iRetCount)
310         {
311             InternalType * pIT = pContext->get(*i);
312             if (pIT)
313             {
314                 out.push_back(pIT);
315                 pIT->IncreaseRef();
316             }
317             else
318             {
319                 const int size = (const int)out.size();
320                 for (int j = 0; j < size; ++j)
321                 {
322                     out[j]->DecreaseRef();
323                     out[j]->killMe();
324                 }
325                 out.clear();
326                 cleanCall(pContext, oldVal);
327
328                 char* pst = wide_string_to_UTF8((*i)->name_get().name_get().c_str());
329                 Scierror(999, _("Undefined variable %s.\n"), pst);
330                 FREE(pst);
331                 return Callable::Error;
332             }
333         }
334     }
335
336     //close the current scope
337     cleanCall(pContext, oldVal);
338
339     for (typed_list::iterator i = out.begin(), end = out.end(); i != end; ++i)
340     {
341         (*i)->DecreaseRef();
342     }
343
344     return RetVal;
345 }
346
347 std::list<symbol::Variable*>* Macro::inputs_get()
348 {
349     return m_inputArgs;
350 }
351
352 std::list<symbol::Variable*>* Macro::outputs_get()
353 {
354     return m_outputArgs;
355 }
356
357 int Macro::getNbInputArgument(void)
358 {
359     return (int)m_inputArgs->size();
360 }
361
362 int Macro::getNbOutputArgument(void)
363 {
364     if (m_outputArgs->size() == 1 && m_outputArgs->back()->name_get().name_get() == L"varargout")
365     {
366         return -1;
367     }
368
369     return (int)m_outputArgs->size();
370 }
371
372 bool Macro::operator==(const InternalType& it)
373 {
374     if (const_cast<InternalType &>(it).isMacro() == false)
375     {
376         return false;
377     }
378
379     std::list<symbol::Variable*>* pInput = NULL;
380     std::list<symbol::Variable*>* pOutput = NULL;
381     types::Macro* pRight = const_cast<InternalType &>(it).getAs<types::Macro>();
382
383     //check inputs
384     pInput = pRight->inputs_get();
385     if (pInput->size() != m_inputArgs->size())
386     {
387         return false;
388     }
389
390     std::list<symbol::Variable*>::iterator itOld = pInput->begin();
391     std::list<symbol::Variable*>::iterator itEndOld = pInput->end();
392     std::list<symbol::Variable*>::iterator itMacro = m_inputArgs->begin();
393
394     for (; itOld != itEndOld ; ++itOld, ++itMacro)
395     {
396         if ((*itOld)->name_get() != (*itMacro)->name_get())
397         {
398             return false;
399         }
400     }
401
402     //check outputs
403     pOutput = pRight->outputs_get();
404     if (pOutput->size() != m_outputArgs->size())
405     {
406         return false;
407     }
408
409     itOld = pOutput->begin();
410     itEndOld = pOutput->end();
411     itMacro = m_outputArgs->begin();
412
413     for (; itOld != itEndOld ; ++itOld, ++itMacro)
414     {
415         if ((*itOld)->name_get() != (*itMacro)->name_get())
416         {
417             return false;
418         }
419     }
420
421     ast::Exp* pExp = pRight->getBody();
422     ast::SerializeVisitor serialOld(pExp);
423     unsigned char* oldSerial = serialOld.serialize(false);
424     ast::SerializeVisitor serialMacro(m_body);
425     unsigned char* macroSerial = serialMacro.serialize(false);
426
427     //check buffer length
428     unsigned int oldSize = *((unsigned int*)oldSerial);
429     unsigned int macroSize = *((unsigned int*)macroSerial);
430     if (oldSize != macroSize)
431     {
432         free(oldSerial);
433         free(macroSerial);
434         return false;
435     }
436
437     bool ret = (memcmp(oldSerial, macroSerial, oldSize) == 0);
438
439     free(oldSerial);
440     free(macroSerial);
441
442     return ret;
443 }
444 }