b9867072311f749e8d5c496592d5d9701fe03b69
[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_LOADINT")},\r
17         {_SC("_OP_DLOAD")},\r
18         {_SC("_OP_TAILCALL")},\r
19         {_SC("_OP_CALL")},\r
20         {_SC("_OP_PREPCALL")},\r
21         {_SC("_OP_PREPCALLK")},\r
22         {_SC("_OP_GETK")},\r
23         {_SC("_OP_MOVE")},\r
24         {_SC("_OP_NEWSLOT")},\r
25         {_SC("_OP_DELETE")},\r
26         {_SC("_OP_SET")},\r
27         {_SC("_OP_GET")},\r
28         {_SC("_OP_EQ")},\r
29         {_SC("_OP_NE")},\r
30         {_SC("_OP_ARITH")},\r
31         {_SC("_OP_BITW")},\r
32         {_SC("_OP_RETURN")},\r
33         {_SC("_OP_LOADNULLS")},\r
34         {_SC("_OP_LOADROOTTABLE")},\r
35         {_SC("_OP_LOADBOOL")},\r
36         {_SC("_OP_DMOVE")},\r
37         {_SC("_OP_JMP")},\r
38         {_SC("_OP_JNZ")},\r
39         {_SC("_OP_JZ")},\r
40         {_SC("_OP_LOADFREEVAR")},\r
41         {_SC("_OP_VARGC")},\r
42         {_SC("_OP_GETVARGV")},\r
43         {_SC("_OP_NEWTABLE")},\r
44         {_SC("_OP_NEWARRAY")},\r
45         {_SC("_OP_APPENDARRAY")},\r
46         {_SC("_OP_GETPARENT")},\r
47         {_SC("_OP_COMPARITH")},\r
48         {_SC("_OP_COMPARITHL")},\r
49         {_SC("_OP_INC")},\r
50         {_SC("_OP_INCL")},\r
51         {_SC("_OP_PINC")},\r
52         {_SC("_OP_PINCL")},\r
53         {_SC("_OP_CMP")},\r
54         {_SC("_OP_EXISTS")},\r
55         {_SC("_OP_INSTANCEOF")},\r
56         {_SC("_OP_AND")},\r
57         {_SC("_OP_OR")},\r
58         {_SC("_OP_NEG")},\r
59         {_SC("_OP_NOT")},\r
60         {_SC("_OP_BWNOT")},\r
61         {_SC("_OP_CLOSURE")},\r
62         {_SC("_OP_YIELD")},\r
63         {_SC("_OP_RESUME")},\r
64         {_SC("_OP_FOREACH")},\r
65         {_SC("_OP_DELEGATE")},\r
66         {_SC("_OP_CLONE")},\r
67         {_SC("_OP_TYPEOF")},\r
68         {_SC("_OP_PUSHTRAP")},\r
69         {_SC("_OP_POPTRAP")},\r
70         {_SC("_OP_THROW")},\r
71         {_SC("_OP_CLASS")},\r
72         {_SC("_OP_NEWSLOTA")}\r
73 };\r
74 #endif\r
75 void DumpLiteral(SQObjectPtr &o)\r
76 {\r
77         switch(type(o)){\r
78                 case OT_STRING: scprintf(_SC("\"%s\""),_stringval(o));break;\r
79                 case OT_FLOAT: scprintf(_SC("{%f}"),_float(o));break;\r
80                 case OT_INTEGER: scprintf(_SC("{%d}"),_integer(o));break;\r
81                 default: assert(0); break; //shut up compiler\r
82         }\r
83 }\r
84 \r
85 SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent,CompilerErrorFunc efunc,void *ed)\r
86 {\r
87                 _nliterals = 0;\r
88                 _literals = SQTable::Create(ss,0);\r
89                 _strings =  SQTable::Create(ss,0);\r
90                 _sharedstate = ss;\r
91                 _lastline = 0;\r
92                 _optimization = true;\r
93                 _parent = parent;\r
94                 _stacksize = 0;\r
95                 _traps = 0;\r
96                 _returnexp = 0;\r
97                 _varparams = false;\r
98                 _errfunc = efunc;\r
99                 _errtarget = ed;\r
100                 _bgenerator = false;\r
101 \r
102 }\r
103 \r
104 void SQFuncState::Error(const SQChar *err)\r
105 {\r
106         _errfunc(_errtarget,err);\r
107 }\r
108 \r
109 #ifdef _DEBUG_DUMP\r
110 void SQFuncState::Dump(SQFunctionProto *func)\r
111 {\r
112         SQUnsignedInteger n=0,i;\r
113         scprintf(_SC("SQInstruction sizeof %d\n"),sizeof(SQInstruction));\r
114         scprintf(_SC("SQObject sizeof %d\n"),sizeof(SQObject));\r
115         scprintf(_SC("--------------------------------------------------------------------\n"));\r
116         scprintf(_SC("*****FUNCTION [%s]\n"),type(func->_name)==OT_STRING?_stringval(func->_name):_SC("unknown"));\r
117         scprintf(_SC("-----LITERALS\n"));\r
118         SQObjectPtr refidx,key,val;\r
119         SQInteger idx;\r
120         SQObjectPtrVec templiterals;\r
121         templiterals.resize(_nliterals);\r
122         while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) {\r
123                 refidx=idx;\r
124                 templiterals[_integer(val)]=key;\r
125         }\r
126         for(i=0;i<templiterals.size();i++){\r
127                 scprintf(_SC("[%d] "),n);\r
128                 DumpLiteral(templiterals[i]);\r
129                 scprintf(_SC("\n"));\r
130                 n++;\r
131         }\r
132         scprintf(_SC("-----PARAMS\n"));\r
133         if(_varparams)\r
134                 scprintf(_SC("<<VARPARAMS>>\n"));\r
135         n=0;\r
136         for(i=0;i<_parameters.size();i++){\r
137                 scprintf(_SC("[%d] "),n);\r
138                 DumpLiteral(_parameters[i]);\r
139                 scprintf(_SC("\n"));\r
140                 n++;\r
141         }\r
142         scprintf(_SC("-----LOCALS\n"));\r
143         for(i=0;i<func->_localvarinfos.size();i++){\r
144                 SQLocalVarInfo lvi=func->_localvarinfos[i];\r
145                 scprintf(_SC("[%d] %s \t%d %d\n"),lvi._pos,_stringval(lvi._name),lvi._start_op,lvi._end_op);\r
146                 n++;\r
147         }\r
148         scprintf(_SC("-----LINE INFO\n"));\r
149         for(i=0;i<_lineinfos.size();i++){\r
150                 SQLineInfo li=_lineinfos[i];\r
151                 scprintf(_SC("op [%d] line [%d] \n"),li._op,li._line);\r
152                 n++;\r
153         }\r
154         scprintf(_SC("-----dump\n"));\r
155         n=0;\r
156         for(i=0;i<_instructions.size();i++){\r
157                 SQInstruction &inst=_instructions[i];\r
158                 if(inst.op==_OP_LOAD || inst.op==_OP_DLOAD || inst.op==_OP_PREPCALLK || inst.op==_OP_GETK ){\r
159                         \r
160                         SQInteger lidx = inst._arg1;\r
161                         scprintf(_SC("[%03d] %15s %d "),n,g_InstrDesc[inst.op].name,inst._arg0);\r
162                         if(lidx >= 0xFFFFFFFF)\r
163                                 scprintf(_SC("null"));\r
164                         else {\r
165                                 SQInteger refidx;\r
166                                 SQObjectPtr val,key,refo;\r
167                                 while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) {\r
168                                         refo = refidx;  \r
169                                 }\r
170                                 DumpLiteral(key);\r
171                         }\r
172                         if(inst.op != _OP_DLOAD) {\r
173                                 scprintf(_SC(" %d %d \n"),inst._arg2,inst._arg3);\r
174                         }\r
175                         else {\r
176                                 scprintf(_SC(" %d "),inst._arg2);\r
177                                 lidx = inst._arg3;\r
178                                 if(lidx >= 0xFFFFFFFF)\r
179                                         scprintf(_SC("null"));\r
180                                 else {\r
181                                         SQInteger refidx;\r
182                                         SQObjectPtr val,key,refo;\r
183                                         while(((refidx=_table(_literals)->Next(false,refo,key,val))!= -1) && (_integer(val) != lidx)) {\r
184                                                 refo = refidx;  \r
185                                 }\r
186                                 DumpLiteral(key);\r
187                                 scprintf(_SC("\n"));\r
188                         }\r
189                         }\r
190                 }\r
191                 else if(inst.op==_OP_ARITH){\r
192                         scprintf(_SC("[%03d] %15s %d %d %d %c\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);\r
193                 }\r
194                 else \r
195                         scprintf(_SC("[%03d] %15s %d %d %d %d\n"),n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3);\r
196                 n++;\r
197         }\r
198         scprintf(_SC("-----\n"));\r
199         scprintf(_SC("stack size[%d]\n"),func->_stacksize);\r
200         scprintf(_SC("--------------------------------------------------------------------\n\n"));\r
201 }\r
202 #endif\r
203 \r
204 SQInteger SQFuncState::GetNumericConstant(const SQInteger cons)\r
205 {\r
206         return GetConstant(SQObjectPtr(cons));\r
207 }\r
208 \r
209 SQInteger SQFuncState::GetNumericConstant(const SQFloat cons)\r
210 {\r
211         return GetConstant(SQObjectPtr(cons));\r
212 }\r
213 \r
214 SQInteger SQFuncState::GetConstant(const SQObject &cons)\r
215 {\r
216         SQObjectPtr val;\r
217         if(!_table(_literals)->Get(cons,val))\r
218         {\r
219                 val = _nliterals;\r
220                 _table(_literals)->NewSlot(cons,val);\r
221                 _nliterals++;\r
222                 if(_nliterals > MAX_LITERALS) {\r
223                         val.Null();\r
224                         Error(_SC("internal compiler error: too many literals"));\r
225                 }\r
226         }\r
227         return _integer(val);\r
228 }\r
229 \r
230 void SQFuncState::SetIntructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3)\r
231 {\r
232         _instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0);\r
233         _instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1);\r
234         _instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&arg2);\r
235         _instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3);\r
236 }\r
237 \r
238 void SQFuncState::SetIntructionParam(SQInteger pos,SQInteger arg,SQInteger val)\r
239 {\r
240         switch(arg){\r
241                 case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break;\r
242                 case 1:case 4:_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&val);break;\r
243                 case 2:_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&val);break;\r
244                 case 3:_instructions[pos]._arg3=(unsigned char)*((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,(SQInteger)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