fix scilab crashs when varargin is used with named arguments
[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  * 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 <memory>
17 #include <sstream>
18 #include <cstdio>
19
20 #include "macro.hxx"
21 #include "list.hxx"
22 #include "context.hxx"
23 #include "symbol.hxx"
24 #include "scilabWrite.hxx"
25 #include "configvariable.hxx"
26 #include "serializervisitor.hxx"
27
28 extern "C"
29 {
30 #include "localization.h"
31 #include "Scierror.h"
32 #include "sciprint.h"
33 #include "sci_malloc.h"
34 #include "os_string.h"
35 }
36
37 namespace types
38 {
39 Macro::Macro(const std::wstring& _stName, std::list<symbol::Variable*>& _inputArgs, std::list<symbol::Variable*>& _outputArgs, ast::SeqExp &_body, const std::wstring& _stModule):
40     Callable(),
41     m_inputArgs(&_inputArgs), m_outputArgs(&_outputArgs), m_body(_body.clone()),
42     m_Nargin(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"nargin"))),
43     m_Nargout(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"nargout"))),
44     m_Varargin(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"varargin"))),
45     m_Varargout(symbol::Context::getInstance()->getOrCreate(symbol::Symbol(L"varargout")))
46 {
47     setName(_stName);
48     setModule(_stModule);
49     bAutoAlloc = false;
50     m_pDblArgIn = new Double(1);
51     m_pDblArgIn->IncreaseRef(); //never delete
52     m_pDblArgOut = new Double(1);
53     m_pDblArgOut->IncreaseRef(); //never delete
54
55     m_body->setReturnable();
56     m_stPath = L"";
57 }
58
59 Macro::~Macro()
60 {
61     delete m_body;
62     m_pDblArgIn->DecreaseRef();
63     m_pDblArgIn->killMe();
64     m_pDblArgOut->DecreaseRef();
65     m_pDblArgOut->killMe();
66
67     if (m_inputArgs)
68     {
69         delete m_inputArgs;
70     }
71
72     if (m_outputArgs)
73     {
74         delete m_outputArgs;
75     }
76
77     for (const auto & sub : m_submacro)
78     {
79         sub.second->DecreaseRef();
80         sub.second->killMe();
81     }
82
83     m_submacro.clear();
84 }
85
86 void Macro::cleanCall(symbol::Context * pContext, int oldPromptMode)
87 {
88     //restore previous prompt mode
89     ConfigVariable::setPromptMode(oldPromptMode);
90     //close the current scope
91     pContext->scope_end();
92     ConfigVariable::macroFirstLine_end();
93 }
94
95 Macro* Macro::clone()
96 {
97     IncreaseRef();
98     return this;
99 }
100
101 void Macro::whoAmI()
102 {
103     std::cout << "types::Macro";
104 }
105
106 ast::SeqExp* Macro::getBody(void)
107 {
108     return m_body;
109 }
110
111 bool Macro::toString(std::wostringstream& ostr)
112 {
113     // get macro name
114     wchar_t* wcsVarName = NULL;
115     if (ostr.str() == SPACES_LIST)
116     {
117         wcsVarName = os_wcsdup(getName().c_str());
118     }
119     else
120     {
121         wcsVarName = os_wcsdup(ostr.str().c_str());
122     }
123
124     ostr.str(L"");
125     ostr << L"[";
126
127     // output arguments [a,b,c] = ....
128     if (m_outputArgs->empty() == false)
129     {
130         std::list<symbol::Variable*>::iterator OutArg = m_outputArgs->begin();
131         std::list<symbol::Variable*>::iterator OutArgfter = OutArg;
132         OutArgfter++;
133
134         for (; OutArgfter != m_outputArgs->end(); OutArgfter++)
135         {
136             ostr << (*OutArg)->getSymbol().getName();
137             ostr << ",";
138             OutArg++;
139         }
140
141         ostr << (*OutArg)->getSymbol().getName();
142     }
143
144     ostr << L"]";
145
146     // function name
147     ostr << L"=" << wcsVarName << L"(";
148
149     // input arguments function(a,b,c)
150     if (m_inputArgs->empty() == false)
151     {
152         std::list<symbol::Variable*>::iterator inArg = m_inputArgs->begin();
153         std::list<symbol::Variable*>::iterator inRagAfter = inArg;
154         inRagAfter++;
155
156         for (; inRagAfter != m_inputArgs->end(); inRagAfter++)
157         {
158             ostr << (*inArg)->getSymbol().getName();
159             ostr << ",";
160             inArg++;
161         }
162
163         ostr << (*inArg)->getSymbol().getName();
164     }
165
166     ostr << L")" << std::endl;
167
168     FREE(wcsVarName);
169     return true;
170 }
171
172 Callable::ReturnValue Macro::call(typed_list &in, optional_list &opt, int _iRetCount, typed_list &out)
173 {
174     bool bVarargout = false;
175     ReturnValue RetVal = Callable::OK;
176     symbol::Context *pContext = symbol::Context::getInstance();
177
178     //open a new scope
179     pContext->scope_begin();
180     //store the line number where is stored this macro in file.
181     ConfigVariable::macroFirstLine_begin(getFirstLine());
182
183     //check excepted and input/output parameters numbers
184     // Scilab Macro can be called with less than prototyped arguments,
185     // but not more execpts with varargin
186
187     // varargin management
188     if (m_inputArgs->size() > 0 && m_inputArgs->back()->getSymbol().getName() == L"varargin")
189     {
190         int iVarPos = static_cast<int>(in.size());
191         if (iVarPos > static_cast<int>(m_inputArgs->size()) - 1)
192         {
193             iVarPos = static_cast<int>(m_inputArgs->size()) - 1;
194         }
195
196         int cur = iVarPos;
197         int opt_offset = 0;
198         //add all standard variable in function context but not varargin
199         std::list<symbol::Variable*>::iterator itName = m_inputArgs->begin();
200         typed_list::const_iterator itValue = in.begin();
201         while (cur > 0)
202         {
203             if (*itValue)
204             {
205                 pContext->put(*itName, *itValue);
206             }
207             else
208             {
209                 opt_offset++;
210             }
211
212             cur--;
213             ++itName;
214             ++itValue;
215         }
216
217         //create varargin only if previous variable are assigned
218         optional_list::const_iterator it = opt.begin();
219         std::advance(it, opt_offset);
220         if (in.size() >= m_inputArgs->size() - 1)
221         {
222             //create and fill varargin
223             List* pL = new List();
224             while (itValue != in.end())
225             {
226                 if (*itValue != NULL)
227                 {
228                     pL->append(*itValue);
229                 }
230                 else
231                 {
232                     pL->append(it->second);
233                     opt[it->first] = nullptr;
234                     it++;
235                 }
236
237                 itValue++;
238             }
239             pContext->put(m_Varargin, pL);
240         }
241     }
242     else if (in.size() > m_inputArgs->size())
243     {
244         if (m_inputArgs->size() == 0)
245         {
246             Scierror(999, _("Wrong number of input arguments: This function has no input argument.\n"));
247         }
248         else
249         {
250             Scierror(999, _("Wrong number of input arguments.\n"));
251         }
252
253         pContext->scope_end();
254         ConfigVariable::macroFirstLine_end();
255         return Callable::Error;
256     }
257     else
258     {
259         //assign value to variable in the new context
260         std::list<symbol::Variable*>::iterator i;
261         typed_list::const_iterator j;
262
263         for (i = m_inputArgs->begin(), j = in.begin(); j != in.end(); ++j, ++i)
264         {
265             if (*j)
266             {
267                 //prevent assignation of NULL value
268                 pContext->put(*i, *j);
269             }
270         }
271     }
272
273     //add optional paramter in current scope
274     for (const auto& it : opt)
275     {
276         if (it.second)
277         {
278             pContext->put(symbol::Symbol(it.first), it.second);
279         }
280     }
281
282     // varargout management
283     //rules :
284     // varargout is a list
285     // varargout can containt more items than caller need
286     // varargout must containt at leat caller needs
287     if (m_outputArgs->size() >= 1 && m_outputArgs->back()->getSymbol().getName() == L"varargout")
288     {
289         bVarargout = true;
290         List* pL = new List();
291         pContext->put(m_Varargout, pL);
292     }
293
294     //common part with or without varargin/varargout
295
296     // Declare nargin & nargout in function context.
297     if (m_pDblArgIn->getRef() > 1)
298     {
299         m_pDblArgIn->DecreaseRef();
300         m_pDblArgIn = (Double*)m_pDblArgIn->clone();
301         m_pDblArgIn->IncreaseRef();
302     }
303     m_pDblArgIn->set(0, static_cast<double>(in.size()));
304
305     if (m_pDblArgOut->getRef() > 1)
306     {
307         m_pDblArgOut->DecreaseRef();
308         m_pDblArgOut = (Double*)m_pDblArgOut->clone();
309         m_pDblArgOut->IncreaseRef();
310     }
311     m_pDblArgOut->set(0, _iRetCount);
312
313     pContext->put(m_Nargin, m_pDblArgIn);
314     pContext->put(m_Nargout, m_pDblArgOut);
315
316
317     //add sub macro in current context
318     for (const auto & sub : m_submacro)
319     {
320         pContext->put(sub.first, sub.second);
321     }
322
323     //save current prompt mode
324     int oldVal = ConfigVariable::getPromptMode();
325     std::unique_ptr<ast::ConstVisitor> exec (ConfigVariable::getDefaultVisitor());
326     try
327     {
328         ConfigVariable::setPromptMode(-1);
329         m_body->accept(*exec);
330         //restore previous prompt mode
331         ConfigVariable::setPromptMode(oldVal);
332     }
333     catch (const ast::InternalError& ie)
334     {
335         cleanCall(pContext, oldVal);
336         throw ie;
337     }
338     catch (const ast::InternalAbort& ia)
339     {
340         cleanCall(pContext, oldVal);
341         throw ia;
342     }
343
344     //nb excepted output without varargout
345     int iRet = std::min((int)m_outputArgs->size() - (bVarargout ? 1 : 0), _iRetCount);
346
347     //normal output management
348     //for (std::list<symbol::Variable*>::iterator i = m_outputArgs->begin(); i != m_outputArgs->end() && _iRetCount; ++i, --_iRetCount)
349     for (auto arg : *m_outputArgs)
350     {
351         iRet--;
352         if (iRet < 0)
353         {
354             break;
355         }
356
357         InternalType * pIT = pContext->get(arg);
358         if (pIT)
359         {
360             out.push_back(pIT);
361             pIT->IncreaseRef();
362         }
363         else
364         {
365             const int size = (const int)out.size();
366             for (int j = 0; j < size; ++j)
367             {
368                 out[j]->DecreaseRef();
369                 out[j]->killMe();
370             }
371             out.clear();
372             cleanCall(pContext, oldVal);
373
374             char* pstArgName = wide_string_to_UTF8(arg->getSymbol().getName().c_str());
375             char* pstMacroName = wide_string_to_UTF8(getName().c_str());
376             Scierror(999, _("Undefined variable '%s' in function '%s'.\n"), pstArgName, pstMacroName);
377             FREE(pstArgName);
378             FREE(pstMacroName);
379             return Callable::Error;
380         }
381     }
382
383     //varargout management
384     if (bVarargout)
385     {
386         InternalType* pOut = pContext->get(m_Varargout);
387         if (pOut == NULL)
388         {
389             cleanCall(pContext, oldVal);
390             Scierror(999, _("Invalid index.\n"));
391             return Callable::Error;
392         }
393
394         if (pOut->isList() == false || pOut->getAs<List>()->getSize() == 0)
395         {
396             cleanCall(pContext, oldVal);
397             Scierror(999, _("Invalid index.\n"));
398             return Callable::Error;
399         }
400
401         List* pVarOut = pOut->getAs<List>();
402         const int size = std::min(pVarOut->getSize(), _iRetCount);
403         for (int i = 0 ; i < size ; ++i)
404         {
405             InternalType* pIT = pVarOut->get(i);
406             if (pIT->isListUndefined())
407             {
408                 for (int j = 0; j < i; ++j)
409                 {
410                     out[j]->DecreaseRef();
411                     out[j]->killMe();
412                 }
413                 out.clear();
414                 cleanCall(pContext, oldVal);
415
416                 Scierror(999, _("List element number %d is Undefined.\n"), i + 1);
417                 return Callable::Error;
418             }
419
420             pIT->IncreaseRef();
421             out.push_back(pIT);
422         }
423     }
424
425     //close the current scope
426     cleanCall(pContext, oldVal);
427
428     for (typed_list::iterator i = out.begin(), end = out.end(); i != end; ++i)
429     {
430         (*i)->DecreaseRef();
431     }
432
433     return RetVal;
434 }
435
436 std::list<symbol::Variable*>* Macro::getInputs()
437 {
438     return m_inputArgs;
439 }
440
441 std::list<symbol::Variable*>* Macro::getOutputs()
442 {
443     return m_outputArgs;
444 }
445
446 int Macro::getNbInputArgument(void)
447 {
448     return (int)m_inputArgs->size();
449 }
450
451 int Macro::getNbOutputArgument(void)
452 {
453     if (m_outputArgs->size() >= 1 && m_outputArgs->back()->getSymbol().getName() == L"varargout")
454     {
455         return -1;
456     }
457
458     return (int)m_outputArgs->size();
459 }
460
461 bool Macro::operator==(const InternalType& it)
462 {
463     if (const_cast<InternalType &>(it).isMacro() == false)
464     {
465         return false;
466     }
467
468     std::list<symbol::Variable*>* pInput = NULL;
469     std::list<symbol::Variable*>* pOutput = NULL;
470     types::Macro* pRight = const_cast<InternalType &>(it).getAs<types::Macro>();
471
472     //check inputs
473     pInput = pRight->getInputs();
474     if (pInput->size() != m_inputArgs->size())
475     {
476         return false;
477     }
478
479     std::list<symbol::Variable*>::iterator itOld = pInput->begin();
480     std::list<symbol::Variable*>::iterator itEndOld = pInput->end();
481     std::list<symbol::Variable*>::iterator itMacro = m_inputArgs->begin();
482
483     for (; itOld != itEndOld ; ++itOld, ++itMacro)
484     {
485         if ((*itOld)->getSymbol() != (*itMacro)->getSymbol())
486         {
487             return false;
488         }
489     }
490
491     //check outputs
492     pOutput = pRight->getOutputs();
493     if (pOutput->size() != m_outputArgs->size())
494     {
495         return false;
496     }
497
498     itOld = pOutput->begin();
499     itEndOld = pOutput->end();
500     itMacro = m_outputArgs->begin();
501
502     for (; itOld != itEndOld ; ++itOld, ++itMacro)
503     {
504         if ((*itOld)->getSymbol() != (*itMacro)->getSymbol())
505         {
506             return false;
507         }
508     }
509
510     ast::Exp* pExp = pRight->getBody();
511     ast::SerializeVisitor serialOld(pExp);
512     unsigned char* oldSerial = serialOld.serialize(false, false);
513     ast::SerializeVisitor serialMacro(m_body);
514     unsigned char* macroSerial = serialMacro.serialize(false, false);
515
516     //check buffer length
517     unsigned int oldSize = *((unsigned int*)oldSerial);
518     unsigned int macroSize = *((unsigned int*)macroSerial);
519     if (oldSize != macroSize)
520     {
521         free(oldSerial);
522         free(macroSerial);
523         return false;
524     }
525
526     bool ret = (memcmp(oldSerial, macroSerial, oldSize) == 0);
527
528     free(oldSerial);
529     free(macroSerial);
530
531     return ret;
532 }
533
534 void Macro::add_submacro(const symbol::Symbol& s, Macro* macro)
535 {
536     macro->IncreaseRef();
537     symbol::Context* ctx = symbol::Context::getInstance();
538     symbol::Variable* var = ctx->getOrCreate(s);
539     m_submacro[var] = macro;
540 }
541 }