- More work on scripting interface
[supertux.git] / src / squirrel / squirrel / sqfuncstate.cpp
1 /*\r
2         see copyright notice in squirrel.h\r
3 */\r
4 #include "sqpcheader.h"\r
5 #include "sqcompiler.h"\r
6 #include "sqfuncproto.h"\r
7 #include "sqstring.h"\r
8 #include "sqtable.h"\r
9 #include "sqopcodes.h"\r
10 #include "sqfuncstate.h"\r
11 \r
12 #ifdef _DEBUG_DUMP\r
13 SQInstructionDesc g_InstrDesc[]={\r
14         {_SC("_OP_LINE")},\r
15         {_SC("_OP_LOAD")},\r
16         {_SC("_OP_TAILCALL")},\r
17         {_SC("_OP_CALL")},\r
18         {_SC("_OP_PREPCALL")},\r
19         {_SC("_OP_PREPCALLK")},\r
20         {_SC("_OP_GETK")},\r
21         {_SC("_OP_MOVE")},\r
22         {_SC("_OP_NEWSLOT")},\r
23         {_SC("_OP_DELETE")},\r
24         {_SC("_OP_SET")},\r
25         {_SC("_OP_GET")},\r
26         {_SC("_OP_EQ")},\r
27         {_SC("_OP_NE")},\r
28         {_SC("_OP_ARITH")},\r
29         {_SC("_OP_BITW")},\r
30         {_SC("_OP_RETURN")},\r
31         {_SC("_OP_LOADNULLS")},\r
32         {_SC("_OP_LOADROOTTABLE")},\r
33         {_SC("_OP_DMOVE")},\r
34         {_SC("_OP_JMP")},\r
35         {_SC("_OP_JNZ")},\r
36         {_SC("_OP_JZ")},\r
37         {_SC("_OP_LOADFREEVAR")},\r
38         {_SC("_OP_VARGC")},\r
39         {_SC("_OP_GETVARGV")},\r
40         {_SC("_OP_NEWTABLE")},\r
41         {_SC("_OP_NEWARRAY")},\r
42         {_SC("_OP_APPENDARRAY")},\r
43         {_SC("_OP_GETPARENT")},\r
44         {_SC("_OP_COMPARITH")},\r
45         {_SC("_OP_COMPARITHL")},\r
46         {_SC("_OP_INC")},\r
47         {_SC("_OP_INCL")},\r
48         {_SC("_OP_PINC")},\r
49         {_SC("_OP_PINCL")},\r
50         {_SC("_OP_CMP")},\r
51         {_SC("_OP_EXISTS")},\r
52         {_SC("_OP_INSTANCEOF")},\r
53         {_SC("_OP_AND")},\r
54         {_SC("_OP_OR")},\r
55         {_SC("_OP_NEG")},\r
56         {_SC("_OP_NOT")},\r
57         {_SC("_OP_BWNOT")},\r
58         {_SC("_OP_CLOSURE")},\r
59         {_SC("_OP_YIELD")},\r
60         {_SC("_OP_RESUME")},\r
61         {_SC("_OP_FOREACH")},\r
62         {_SC("_OP_DELEGATE")},\r
63         {_SC("_OP_CLONE")},\r
64         {_SC("_OP_TYPEOF")},\r
65         {_SC("_OP_PUSHTRAP")},\r
66         {_SC("_OP_POPTRAP")},\r
67         {_SC("_OP_THROW")},\r
68         {_SC("_OP_CLASS")},\r
69         {_SC("_OP_NEWSLOTA")},\r
70         {_SC("_OP_LOADBOOL")}\r
71 };\r
72 #endif\r
73 void DumpLiteral(SQObjectPtr &o)\r
74 {\r
75         switch(type(o)){\r
76                 case OT_STRING: scprintf(_SC("\"%s\""),_stringval(o));break;\r
77                 case OT_FLOAT: scprintf(_SC("{%f}"),_float(o));break;\r
78                 case OT_INTEGER: scprintf(_SC("{%d}"),_integer(o));break;\r
79         }\r
80 }\r
81 \r
82 SQFuncState::SQFuncState(SQSharedState *ss,SQFunctionProto *func,SQFuncState *parent)\r
83 {\r
84                 _nliterals = 0;\r
85                 _literals = SQTable::Create(ss,0);\r
86                 _sharedstate = ss;\r
87                 _lastline = 0;\r
88                 _optimization = true;\r
89                 _func = func;\r
90                 _parent = parent;\r
91                 _stacksize = 0;\r
92                 _traps = 0;\r
93                 _returnexp = 0;\r
94                 _varparams = false;\r
95 }\r
96 \r
97 #ifdef _DEBUG_DUMP\r
98 void SQFuncState::Dump()\r
99 {\r
100         unsigned int n=0,i;\r
101         SQFunctionProto *func=_funcproto(_func);\r
102         scprintf(_SC("SQInstruction sizeof %d\n"),sizeof(SQInstruction));\r
103         scprintf(_SC("SQObject sizeof %d\n"),sizeof(SQObject));\r
104         scprintf(_SC("--------------------------------------------------------------------\n"));\r
105         scprintf(_SC("*****FUNCTION [%s]\n"),type(func->_name)==OT_STRING?_stringval(func->_name):_SC("unknown"));\r
106         scprintf(_SC("-----LITERALS\n"));\r
107         SQObjectPtr refidx,key,val;\r
108         SQInteger idx;\r
109         SQObjectPtrVec templiterals;\r
110         templiterals.resize(_nliterals);\r
111         while((idx=_table(_literals)->Next(refidx,key,val))!=-1) {\r
112                 refidx=idx;\r
113                 templiterals[_integer(val)]=key;\r
114         }\r
115         for(i=0;i<templiterals.size();i++){\r
116                 scprintf(_SC("[%d] "),n);\r
117                 DumpLiteral(templiterals[i]);\r
118                 scprintf(_SC("\n"));\r
119                 n++;\r
120         }\r
121         scprintf(_SC("-----PARAMS\n"));\r
122         if(_varparams)\r
123                 scprintf(_SC("<<VARPARAMS>>\n"));\r
124         n=0;\r
125         for(i=0;i<_parameters.size();i++){\r
126                 scprintf(_SC("[%d] "),n);\r
127                 DumpLiteral(_parameters[i]);\r
128                 scprintf(_SC("\n"));\r
129                 n++;\r
130         }\r
131         scprintf(_SC("-----LOCALS\n"));\r
132         for(i=0;i<func->_localvarinfos.size();i++){\r
133                 SQLocalVarInfo lvi=func->_localvarinfos[i];\r
134                 scprintf(_SC("[%d] %s \t%d %d\n"),lvi._pos,_stringval(lvi._name),lvi._start_op,lvi._end_op);\r
135                 n++;\r
136         }\r
137         scprintf(_SC("-----LINE INFO\n"));\r
138         for(i=0;i<_lineinfos.size();i++){\r
139                 SQLineInfo li=_lineinfos[i];\r
140                 scprintf(_SC("op [%d] line [%d] \n"),li._op,li._line);\r
141                 n++;\r
142         }\r
143         scprintf(_SC("-----dump\n"));\r
144         n=0;\r
145         for(i=0;i<_instructions.size();i++){\r
146                 SQInstruction &inst=_instructions[i];\r
147                 if(inst.op==_OP_LOAD || inst.op==_OP_PREPCALLK || inst.op==_OP_GETK ){\r
148                         \r
149                         int lidx = inst._arg1;\r
150                         scprintf(_SC("[%03d] %15s %d "),n,g_InstrDesc[inst.op].name,inst._arg0);\r
151                         if(lidx >= 0xFFFFFFFF)\r
152                                 scprintf(_SC("null"));\r
153                         else {\r
154                                 int refidx;\r
155                                 SQObjectPtr val,key,refo;\r
156                                 while(((refidx=_table(_literals)->Next(refo,key,val))!= -1) && (_integer(val) != lidx)) {\r
157                                         refo = refidx;  \r
158                                 }\r
159                                 DumpLiteral(key);\r
160                         }\r
161                         scprintf(_SC(" %d %d \n"),inst._arg2,inst._arg3);\r
162                 }\r
163                 else if(inst.op==_OP_ARITH){\r
164                         scprintf(_SC("[%03d] %15s %d %d %d %c\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);\r
165                 }\r
166                 else \r
167                         scprintf(_SC("[%03d] %15s %d %d %d %d\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);\r
168                 n++;\r
169         }\r
170         scprintf(_SC("-----\n"));\r
171         scprintf(_SC("stack size[%d]\n"),func->_stacksize);\r
172         scprintf(_SC("--------------------------------------------------------------------\n\n"));\r
173 }\r
174 #endif\r
175 int SQFuncState::GetStringConstant(const SQChar *cons)\r
176 {\r
177         return GetConstant(SQString::Create(_sharedstate,cons));\r
178 }\r
179 \r
180 int SQFuncState::GetNumericConstant(const SQInteger cons)\r
181 {\r
182         return GetConstant(cons);\r
183 }\r
184 \r
185 int SQFuncState::GetNumericConstant(const SQFloat cons)\r
186 {\r
187         return GetConstant(cons);\r
188 }\r
189 \r
190 int SQFuncState::GetConstant(SQObjectPtr cons)\r
191 {\r
192         int n=0;\r
193         SQObjectPtr val;\r
194         if(!_table(_literals)->Get(cons,val))\r
195         {\r
196                 val = _nliterals;\r
197                 _table(_literals)->NewSlot(cons,val);\r
198                 _nliterals++;\r
199                 if(_nliterals > MAX_LITERALS) throw ParserException(_SC("internal compiler error: too many literals"));\r
200         }\r
201         return _integer(val);\r
202 }\r
203 \r
204 void SQFuncState::SetIntructionParams(int pos,int arg0,int arg1,int arg2,int arg3)\r
205 {\r
206         _instructions[pos]._arg0=*((unsigned int *)&arg0);\r
207         _instructions[pos]._arg1=*((unsigned int *)&arg1);\r
208         _instructions[pos]._arg2=*((unsigned int *)&arg2);\r
209         _instructions[pos]._arg3=*((unsigned int *)&arg3);\r
210 }\r
211 \r
212 void SQFuncState::SetIntructionParam(int pos,int arg,int val)\r
213 {\r
214         switch(arg){\r
215                 case 0:_instructions[pos]._arg0=*((unsigned int *)&val);break;\r
216                 case 1:_instructions[pos]._arg1=*((unsigned int *)&val);break;\r
217                 case 2:_instructions[pos]._arg2=*((unsigned int *)&val);break;\r
218                 case 3:_instructions[pos]._arg3=*((unsigned int *)&val);break;\r
219                 case 4:_instructions[pos]._arg1=*((unsigned int *)&val);break;\r
220         };\r
221 }\r
222 \r
223 int SQFuncState::AllocStackPos()\r
224 {\r
225         int npos=_vlocals.size();\r
226         _vlocals.push_back(SQLocalVarInfo());\r
227         if(_vlocals.size()>((unsigned int)_stacksize)) {\r
228                 if(_stacksize>MAX_FUNC_STACKSIZE) throw ParserException(_SC("internal compiler error: too many locals"));\r
229                 _stacksize=_vlocals.size();\r
230         }\r
231         return npos;\r
232 }\r
233 \r
234 int SQFuncState::PushTarget(int n)\r
235 {\r
236         if(n!=-1){\r
237                 _targetstack.push_back(n);\r
238                 return n;\r
239         }\r
240         n=AllocStackPos();\r
241         _targetstack.push_back(n);\r
242         return n;\r
243 }\r
244 \r
245 int SQFuncState::GetUpTarget(int n){\r
246         return _targetstack[((_targetstack.size()-1)-n)];\r
247 }\r
248 \r
249 int SQFuncState::TopTarget(){\r
250         return _targetstack.back();\r
251 }\r
252 int SQFuncState::PopTarget()\r
253 {\r
254         int npos=_targetstack.back();\r
255         SQLocalVarInfo t=_vlocals[_targetstack.back()];\r
256         if(type(t._name)==OT_NULL){\r
257                 _vlocals.pop_back();\r
258         }\r
259         _targetstack.pop_back();\r
260         return npos;\r
261 }\r
262 \r
263 int SQFuncState::GetStackSize()\r
264 {\r
265         return _vlocals.size();\r
266 }\r
267 \r
268 void SQFuncState::SetStackSize(int n)\r
269 {\r
270         int size=_vlocals.size();\r
271         while(size>n){\r
272                 size--;\r
273                 SQLocalVarInfo lvi=_vlocals.back();\r
274                 if(type(lvi._name)!=OT_NULL){\r
275                         lvi._end_op=GetCurrentPos();\r
276                         _localvarinfos.push_back(lvi);\r
277                 }\r
278                 _vlocals.pop_back();\r
279         }\r
280 }\r
281 \r
282 bool SQFuncState::IsLocal(unsigned int stkpos)\r
283 {\r
284         if(stkpos>=_vlocals.size())return false;\r
285         else if(type(_vlocals[stkpos]._name)!=OT_NULL)return true;\r
286         return false;\r
287 }\r
288 \r
289 int SQFuncState::PushLocalVariable(const SQObjectPtr &name)\r
290 {\r
291         int pos=_vlocals.size();\r
292         SQLocalVarInfo lvi;\r
293         lvi._name=name;\r
294         lvi._start_op=GetCurrentPos()+1;\r
295         lvi._pos=_vlocals.size();\r
296         _vlocals.push_back(lvi);\r
297         if(_vlocals.size()>((unsigned int)_stacksize))_stacksize=_vlocals.size();\r
298         \r
299         return pos;\r
300 }\r
301 \r
302 int SQFuncState::GetLocalVariable(const SQObjectPtr &name)\r
303 {\r
304         int locals=_vlocals.size();\r
305         while(locals>=1){\r
306                 if(type(_vlocals[locals-1]._name)==OT_STRING && _string(_vlocals[locals-1]._name)==_string(name)){\r
307                         return locals-1;\r
308                 }\r
309                 locals--;\r
310         }\r
311         return -1;\r
312 }\r
313 \r
314 int SQFuncState::GetOuterVariable(const SQObjectPtr &name)\r
315 {\r
316         int outers = _outervalues.size();\r
317         for(int i = 0; i<outers; i++) {\r
318                 if(_string(_outervalues[i]._name) == _string(name))\r
319                         return i;\r
320         }\r
321         return -1;\r
322 }\r
323 \r
324 void SQFuncState::AddOuterValue(const SQObjectPtr &name)\r
325 {\r
326         //AddParameter(name);\r
327         int pos=-1;\r
328         if(_parent) { \r
329                 pos = _parent->GetLocalVariable(name);\r
330                 if(pos == -1) {\r
331                         pos = _parent->GetOuterVariable(name);\r
332                         if(pos != -1) {\r
333                                 _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otOUTER)); //local\r
334                                 return;\r
335                         }\r
336                 }\r
337                 else {\r
338                         _outervalues.push_back(SQOuterVar(name,SQObjectPtr(SQInteger(pos)),otLOCAL)); //local\r
339                         return;\r
340                 }\r
341         }       \r
342         _outervalues.push_back(SQOuterVar(name,name,otSYMBOL)); //global\r
343 }\r
344 \r
345 void SQFuncState::AddParameter(const SQObjectPtr &name)\r
346 {\r
347         PushLocalVariable(name);\r
348         _parameters.push_back(name);\r
349 }\r
350 \r
351 void SQFuncState::AddLineInfos(int line,bool lineop,bool force)\r
352 {\r
353         if(_lastline!=line || force){\r
354                 SQLineInfo li;\r
355                 li._line=line;li._op=(GetCurrentPos()+1);\r
356                 if(lineop)AddInstruction(_OP_LINE,0,line);\r
357                 _lineinfos.push_back(li);\r
358                 _lastline=line;\r
359         }\r
360 }\r
361 \r
362 void SQFuncState::AddInstruction(SQInstruction &i)\r
363 {\r
364         int size = _instructions.size();\r
365         if(size > 0 && _optimization){ //simple optimizer\r
366                 SQInstruction &pi = _instructions[size-1];//previous instruction\r
367                 switch(i.op) {\r
368                 case _OP_RETURN:\r
369                         if( _parent && i._arg0 != MAX_FUNC_STACKSIZE && pi.op == _OP_CALL && _returnexp < size-1) {\r
370                                 pi.op = _OP_TAILCALL;\r
371                         }\r
372                 break;\r
373                 case _OP_GET:\r
374                         if( pi.op == _OP_LOAD && pi._arg0 == i._arg2 && (!IsLocal(pi._arg0))){\r
375                                 pi._arg1 = pi._arg1;\r
376                                 pi._arg2 = (unsigned char)i._arg1;\r
377                                 pi.op = _OP_GETK;\r
378                                 pi._arg0 = i._arg0;\r
379                                 \r
380                                 return;\r
381                         }\r
382                 break;\r
383                 case _OP_PREPCALL:\r
384                         if( pi.op == _OP_LOAD  && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){\r
385                                 pi.op = _OP_PREPCALLK;\r
386                                 pi._arg0 = i._arg0;\r
387                                 pi._arg1 = pi._arg1;\r
388                                 pi._arg2 = i._arg2;\r
389                                 pi._arg3 = i._arg3;\r
390                                 return;\r
391                         }\r
392                         break;\r
393                 case _OP_APPENDARRAY:\r
394                         if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){\r
395                                 pi.op = _OP_APPENDARRAY;\r
396                                 pi._arg0 = i._arg0;\r
397                                 pi._arg1 = pi._arg1;\r
398                                 pi._arg2 = MAX_FUNC_STACKSIZE;\r
399                                 pi._arg3 = MAX_FUNC_STACKSIZE;\r
400                                 return;\r
401                         }\r
402                         break;\r
403                 case _OP_MOVE: \r
404                         if((pi.op == _OP_GET || pi.op == _OP_ARITH || pi.op == _OP_BITW) && (pi._arg0 == i._arg1))\r
405                         {\r
406                                 pi._arg0 = i._arg0;\r
407                                 _optimization = false;\r
408                                 return;\r
409                         }\r
410 \r
411                         if(pi.op == _OP_MOVE)\r
412                         {\r
413                                 pi.op = _OP_DMOVE;\r
414                                 pi._arg2 = i._arg0;\r
415                                 pi._arg3 = (unsigned char)i._arg1;\r
416                                 return;\r
417                         }\r
418                         break;\r
419 \r
420                 case _OP_EQ:case _OP_NE:\r
421                         if(pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0) ))\r
422                         {\r
423                                 pi.op = i.op;\r
424                                 pi._arg0 = i._arg0;\r
425                                 pi._arg1 = pi._arg1;\r
426                                 pi._arg2 = i._arg2;\r
427                                 pi._arg3 = MAX_FUNC_STACKSIZE;\r
428                                 return;\r
429                         }\r
430                         break;\r
431                 case _OP_LOADNULLS:\r
432                 //case _OP_LOADNULL:\r
433                         if((pi.op == _OP_LOADNULLS && pi._arg0+pi._arg1 == i._arg0)) {\r
434                                 \r
435                                 pi._arg1 = pi._arg1 + 1;\r
436                                 pi.op = _OP_LOADNULLS;\r
437                                 return;\r
438                         }\r
439             break;\r
440                 case _OP_LINE:\r
441                         if(pi.op == _OP_LINE) {\r
442                                 _instructions.pop_back();\r
443                                 _lineinfos.pop_back();\r
444                         }\r
445                         break;\r
446                 }\r
447         }\r
448         _optimization = true;\r
449         _instructions.push_back(i);\r
450 }\r
451 \r
452 SQObject SQFuncState::CreateString(const SQChar *s)\r
453 {\r
454         SQObjectPtr ns(SQString::Create(_sharedstate,s));\r
455         _stringrefs.push_back(ns);\r
456         return ns;\r
457 }\r
458 \r
459 void SQFuncState::Finalize()\r
460 {\r
461         SQFunctionProto *f=_funcproto(_func);\r
462         f->_literals.resize(_nliterals);\r
463         SQObjectPtr refidx,key,val;\r
464         SQInteger idx;\r
465         while((idx=_table(_literals)->Next(refidx,key,val))!=-1) {\r
466                 f->_literals[_integer(val)]=key;\r
467                 refidx=idx;\r
468         }\r
469         f->_functions.resize(_functions.size());\r
470         f->_functions.copy(_functions);\r
471         f->_parameters.resize(_parameters.size());\r
472         f->_parameters.copy(_parameters);\r
473         f->_outervalues.resize(_outervalues.size());\r
474         f->_outervalues.copy(_outervalues);\r
475         f->_instructions.resize(_instructions.size());\r
476         f->_instructions.copy(_instructions);\r
477         f->_localvarinfos.resize(_localvarinfos.size());\r
478         f->_localvarinfos.copy(_localvarinfos);\r
479         f->_lineinfos.resize(_lineinfos.size());\r
480         f->_lineinfos.copy(_lineinfos);\r
481         f->_varparams = _varparams;\r
482 }\r