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