source: dev/BasicCompiler32/NumOpe.cpp@ 63

Last change on this file since 63 was 63, checked in by dai_9181, 17 years ago

CClass::GetSize、CClass::GetMemberOffsetを追加

File size: 17.7 KB
Line 
1#include "../BasicCompiler_Common/common.h"
2#include "Opcode.h"
3
4void PushReturnValue(int type){
5 //関数の戻り値をスタックへプッシュする
6 //※この処理内では、esi、ediは使用不可
7
8 if(type==DEF_OBJECT){
9 //push eax
10 op_push(REG_EAX);
11 }
12 else if(type==DEF_DOUBLE){
13 //sub esp,8
14 op_sub_esp(8);
15
16 //fstp qword ptr[esp]
17 OpBuffer[obp++]=(char)0xDD;
18 OpBuffer[obp++]=(char)0x1C;
19 OpBuffer[obp++]=(char)0x24;
20 }
21 else if(type==DEF_SINGLE){
22 //sub esp,4
23 op_sub_esp(4);
24
25 //fstp dword ptr[esp]
26 OpBuffer[obp++]=(char)0xD9;
27 OpBuffer[obp++]=(char)0x1C;
28 OpBuffer[obp++]=(char)0x24;
29 }
30 else if(type==DEF_INT64||type==DEF_QWORD){
31 //push edx
32 op_push(REG_EDX);
33
34 //push eax
35 op_push(REG_EAX);
36 }
37 else if(type==DEF_LONG){
38 //push eax
39 op_push(REG_EAX);
40 }
41 else if(type==DEF_INTEGER || (isUnicode&&type==DEF_CHAR)){
42 //movsx ebx,ax
43 OpBuffer[obp++]=(char)0x0F;
44 OpBuffer[obp++]=(char)0xBF;
45 OpBuffer[obp++]=(char)0xD8;
46
47 //push ebx
48 op_push(REG_EBX);
49 }
50 else if(type==DEF_SBYTE || (isUnicode==false&&type==DEF_CHAR)){
51 //movsx ebx,al
52 OpBuffer[obp++]=(char)0x0F;
53 OpBuffer[obp++]=(char)0xBE;
54 OpBuffer[obp++]=(char)0xD8;
55
56 //push ebx
57 op_push(REG_EBX);
58 }
59 else if(type==DEF_DWORD||type==DEF_WORD||type==DEF_BYTE||type==DEF_BOOLEAN||
60 IsPtrType(type)){
61 //push eax
62 op_push(REG_EAX);
63 }
64 else if(type==DEF_PTR_BYTE){
65 //push eax
66 op_push(REG_EAX);
67 }
68}
69
70void NewStringObject(LPSTR lpszText){
71 ///////////////////////////////////////////////////////
72 // lpszTextを元にStringオブジェクトを生成し、
73 // オブジェクトポインタをスタックに格納する
74 ///////////////////////////////////////////////////////
75
76 extern CClass *pobj_StringClass;
77 int object_size = pobj_StringClass->GetSize();
78
79 //push object_size
80 op_push_value(object_size);
81
82 //call calloc
83 extern SUBINFO *pSub_calloc;
84 op_call(pSub_calloc);
85
86 //push eax
87 op_push(REG_EAX);
88
89 //push eax
90 op_push(REG_EAX);
91
92 {
93 //push eax
94 op_push(REG_EAX);
95
96 //call constructor
97 op_call(pobj_StringClass->GetConstructorMethod()->psi);
98 }
99
100 // TODO: Ex表記による文字列長に対応する
101 int i2 = dataTable.AddString( lpszText );
102
103 //push lpszPtr
104 OpBuffer[obp++]=(char)0x68;
105 *((long *)(OpBuffer+obp))=i2;
106 pobj_DataTableSchedule->add();
107 obp+=sizeof(long);
108
109
110 SetObjectVariable((LONG_PTR)pobj_StringClass,DEF_PTR_BYTE,-1,0);
111}
112
113
114int NumOpe(const char *Command,int BaseType,LONG_PTR lpBaseIndex,LONG_PTR *plpIndex,BOOL *pbUseHeap){
115 extern HANDLE hHeap;
116 int i,i2,i3,i4;
117 char temporary[8192],temp2[1024],temp3[1024];
118
119 if(Command[0]=='\0'){
120 SetError(1,NULL,cp);
121 return -1;
122 }
123
124 if(Command[0]==1&&Command[1]==ESC_NEW){
125 //New演算子(オブジェクト生成)
126 return Operator_New(Command+2,plpIndex);
127 }
128
129
130 /////////////////////////////////
131 // 式要素を逆ポーランド式で取得
132 /////////////////////////////////
133
134 char *values[255];
135 long calc[255];
136 long stack[255];
137 int pnum;
138 if(!GetNumOpeElements(Command,&pnum,values,calc,stack)){
139 for(i=0;i<pnum;i++){
140 if(values[i]) HeapDefaultFree(values[i]);
141 }
142 return 0;
143 }
144
145
146 BOOL bError;
147 bError=0;
148
149 //リテラル値のみの計算かどうかを判別するためのフラグ
150 BOOL bLiteralCalculation=1;
151
152 //リテラル演算の場合を考慮した演算前のバッファ位置
153 int BeforeObp;
154 BeforeObp=obp;
155
156 //リテラル演算の場合を考慮した演算前のプロシージャスケジュール位置
157 //※64ビットの掛け算、除算などで特殊関数が呼ばれるため
158 int Before_ProcAddrScheduleNum;
159 Before_ProcAddrScheduleNum=pobj_SubAddrSchedule->num;
160
161 //リテラル演算の場合を考慮した演算前のデータテーブルスケジュール位置
162 int Before_DataTableScheduleNum;
163 Before_DataTableScheduleNum=pobj_DataTableSchedule->num;
164
165 //リテラル演算の場合を考慮した演算前の再配置スケジュール
166 CReloc *pobj_BackReloc;
167 pobj_BackReloc=new CReloc();
168 pobj_BackReloc->copy(pobj_Reloc);
169
170 double dbl;
171 int sp;
172 int type[255];
173 LONG_PTR index_stack[255];
174 BOOL bUseHeap[255];
175 _int64 i64data;
176 for(i=0,sp=0;i<pnum;i++){
177 int idCalc;
178 idCalc=calc[i]%100;
179
180 if(idCalc){
181 if(type[sp-2]==DEF_OBJECT){
182 //オーバーロードされたオペレータを呼び出す
183 TYPEINFO BaseTypeInfo={BaseType,lpBaseIndex};
184 i2=CallOperatorProc(idCalc,&BaseTypeInfo,type,index_stack,bUseHeap,sp);
185 if(i2==0){
186 if(idCalc==CALC_EQUAL) lstrcpy(temp2,"==");
187 else GetCalcName(idCalc,temp2);
188 sprintf(temporary,"Operator %s",temp2);
189 SetError(27,temporary,cp);
190 goto error;
191 }
192 else if(i2==-1) goto error;
193
194 continue;
195 }
196
197 if(!CheckCalcType(idCalc,type,sp)) goto error;
198 }
199
200 switch(idCalc){
201 //数値
202 case 0:
203 index_stack[sp]=-1;
204 bUseHeap[sp]=0;
205
206 char *term;
207 term=values[i];
208
209 if(term[0]=='\"'){
210 //リテラル文字列
211 if(!RemoveStringQuotes(term)){
212 SetError(43,NULL,cp);
213 goto error;
214 }
215 i3=lstrlen(term);
216StrLiteral:
217
218 if(BaseType==DEF_OBJECT){
219 CClass *pobj_Class;
220 pobj_Class=(CClass *)lpBaseIndex;
221 TYPEINFO BaseTypeInfo = {BaseType,lpBaseIndex};
222 if(IsStringSubsituation(pobj_Class)
223 || IsStringObjectType(&BaseTypeInfo)){
224 //要求タイプがオブジェクトであり、Stringの受け入れが可能な場合
225
226 //String型オブジェクトを生成
227 NewStringObject(term);
228
229 extern CClass *pobj_StringClass;
230 type[sp]=DEF_OBJECT;
231 index_stack[sp]=(LONG_PTR)pobj_StringClass;
232 bUseHeap[sp]=1;
233 bLiteralCalculation=0;
234
235 sp++;
236 break;
237 }
238 }
239
240
241 type[sp]=DEF_PTR_BYTE;
242 index_stack[sp]=LITERAL_STRING;
243 bLiteralCalculation=0;
244
245 i2=dataTable.AddString(term,i3);
246
247 //push DataSize
248 OpBuffer[obp++]=(char)0x68;
249 *((long *)(OpBuffer+obp))=i2;
250 pobj_DataTableSchedule->add();
251 obp+=sizeof(long);
252 }
253 else if((term[0]=='e'||term[0]=='E')&&
254 (term[1]=='x'||term[1]=='X')&&
255 term[2]=='\"'){
256 //拡張版リテラル文字列(エスケープシーケンス可能)
257 if(!RemoveStringQuotes(term+2)){
258 SetError(43,NULL,cp);
259 goto error;
260 }
261 i3=FormatString_EscapeSequence(term+2);
262 term+=2;
263
264 goto StrLiteral;
265 }
266 else if(IsVariableTopChar(term[0])||
267 term[0]=='*'||
268 (term[0]=='.'&&IsVariableTopChar(term[1]))){
269 //////////////////
270 // 何らかの識別子
271
272 //////////////////////////////////////
273 // 関数(DLL、ユーザー定義、組み込み)
274 //////////////////////////////////////
275
276 i2=GetCallProcName(term,temporary);
277 if(term[i2]=='('){
278 i4=GetStringInPare_RemovePare(temp2,term+i2+1);
279
280 int idProc;
281 void *pInfo;
282 idProc=GetProc(temporary,&pInfo);
283
284 if(idProc){
285 //閉じカッコ")"に続く文字がNULLでないとき
286 if(term[i2+1+i4+1]!='\0'){
287 if( term[i2+1+i4+1] == '.'
288 || term[i2+1+i4+1] == 1 && term[i2+1+i4+2] == ESC_PSMEM ){
289 goto NonProc;
290 }
291 else{
292 SetError(42,NULL,cp);
293 }
294 }
295
296 ////////////////
297 // 呼び出し
298 ////////////////
299
300 i2=CallProc(idProc,pInfo,temporary,temp2,&index_stack[sp]);
301 if(i2==-1){
302 //戻り値が存在しないとき
303 for(i2=2;;i2++){
304 if(term[i2]=='('||term[i2]=='\0'){
305 term[i2]=0;
306 break;
307 }
308 }
309 SetError(38,term,cp);
310
311 goto error;
312 }
313
314
315 /////////////////////
316 // 戻り値の処理
317 /////////////////////
318
319 //大きな型への暗黙の変換
320 type[sp]=AutoBigCast(BaseType,i2);
321 bLiteralCalculation=0;
322
323 //スタックへプッシュ
324 PushReturnValue(i2);
325
326 if(Is64Type(type[sp])&&IsWholeNumberType(i2)&&GetTypeSize(i2,-1)<=sizeof(long)){
327 //必要に応じて64ビット拡張
328 ExtendStackTo64(i2);
329 }
330
331 if(i2==DEF_OBJECT){
332 //Object型が戻ったときはヒープ領域にインスタンスが格納されている
333 //※後にfreeする必要あり
334 bUseHeap[sp]=1;
335 }
336
337 sp++;
338 break;
339 }
340 else if(GetConstCalcBuffer(temporary,temp2,temp3)){
341 /////////////////////////
342 // マクロ関数
343 /////////////////////////
344
345 //閉じカッコ")"に続く文字がNULLでないときはエラーにする
346 if(term[i2+1+i4+1]!='\0') SetError(42,NULL,cp);
347
348 //マクロ関数の場合
349 type[sp]=NumOpe(temp3,0,0,&index_stack[sp]);
350
351 if(!IS_LITERAL(index_stack[sp])){
352 //リテラル値ではなかったとき
353 bLiteralCalculation=0;
354 }
355
356 sp++;
357 break;
358 }
359 }
360NonProc:
361
362
363
364 char variable[VN_SIZE],array_element[VN_SIZE];
365 CClass *pobj_c;
366 GetArrayElement(term,variable,array_element);
367 if(array_element[0]){
368 i2=GetVarType(variable,(LONG_PTR *)&pobj_c,0);
369 if(i2==DEF_OBJECT){
370 TYPEINFO RetTypeInfo;
371 CallIndexerGetterProc(pobj_c,variable,array_element,RetTypeInfo);
372 type[sp]=RetTypeInfo.type;
373 index_stack[sp]=RetTypeInfo.u.lpIndex;
374 bLiteralCalculation=0;
375
376 //push eax
377 op_push(REG_EAX);
378
379 sp++;
380 break;
381 }
382 }
383
384
385
386 RELATIVE_VAR RelativeVar;
387 if(GetVarOffset(
388 false, //エラー表示あり
389 false, //読み込み専用
390 term,&i2,&RelativeVar,&index_stack[sp])){
391 //////////
392 // 変数
393 //////////
394
395 //大きな型への暗黙の変換
396 type[sp]=AutoBigCast(BaseType,i2);
397 bLiteralCalculation=0;
398
399 if(i2&FLAG_PTR){
400 //配列ポインタ
401 type[sp]=GetPtrType(i2^FLAG_PTR,index_stack[sp]);
402
403 SetVarPtrToEax(&RelativeVar);
404
405 //push eax
406 op_push(REG_EAX);
407 }
408 else if(i2==DEF_DOUBLE||
409 i2==DEF_INT64||
410 i2==DEF_QWORD){
411 //64ビット型
412 PushDoubleVariable(&RelativeVar);
413 }
414 else if(i2==DEF_LONG||i2==DEF_DWORD||i2==DEF_SINGLE||
415 IsPtrType(i2)){
416 //32ビット型
417 PushLongVariable(&RelativeVar);
418 }
419 else if(i2==DEF_INTEGER || (isUnicode&&i2==DEF_CHAR)){
420 PushIntegerVariable(&RelativeVar);
421 }
422 else if(i2==DEF_WORD){
423 PushWordVariable(&RelativeVar);
424 }
425 else if(i2==DEF_SBYTE || (isUnicode==false&&i2==DEF_CHAR)){
426 PushCharVariable(&RelativeVar);
427 }
428 else if(i2==DEF_BYTE||i2==DEF_BOOLEAN){
429 PushByteVariable(&RelativeVar);
430 }
431 else if(i2==DEF_OBJECT){
432 //オブジェクト ポインタをeaxへ格納
433 SetVarPtrToEax(&RelativeVar);
434
435 //push eax
436 op_push(REG_EAX);
437 }
438
439 if(Is64Type(type[sp])&&IsWholeNumberType(i2)&&GetTypeSize(i2,-1)<=sizeof(long)){
440 //必要に応じて64ビット拡張
441 ExtendStackTo64(i2);
442 }
443
444 sp++;
445 break;
446 }
447
448
449 //////////////
450 // 定数の場合
451 //////////////
452
453 i3 = CDBConst::obj.GetType(term);
454 if(i3){
455 type[sp]=i3;
456 if(IsRealNumberType(i3)){
457 //実数
458 double dbl = CDBConst::obj.GetDoubleData(term);
459 memcpy(&i64data,&dbl,sizeof(double));
460 goto Literal;
461 }
462 else if(IsWholeNumberType(i3)){
463 //整数
464 i64data = CDBConst::obj.GetWholeData(term);
465 goto Literal;
466 }
467 /*else if(i3==DEF_STRING){
468 //リテラル文字列
469
470 //バイト数
471 i3=(int)dbl;
472
473 memcpy(term,temporary,i3);
474 goto StrLiteral;
475 }*/
476 else{
477 SetError(300,NULL,cp);
478 goto error;
479 }
480 }
481
482
483 //////////////
484 // 型名の場合
485 //////////////
486
487 LONG_PTR lp;
488 i3=GetTypeFixed(term,&lp);
489 if(i3!=-1){
490 type[sp]=i3|FLAG_CAST;
491 index_stack[sp]=lp;
492 sp++;
493 break;
494 }
495
496
497
498 /////////////////////////////////
499 // プロパティ用のメソッド
500 /////////////////////////////////
501
502 //配列要素を排除
503 char VarName[VN_SIZE],ArrayElements[VN_SIZE];
504 GetArrayElement(term,VarName,ArrayElements);
505
506 if(GetSubHash(VarName,0)){
507 TYPEINFO RetTypeInfo;
508 CallPropertyMethod(term,NULL,&RetTypeInfo);
509
510 //大きな型への暗黙の変換
511 type[sp]=AutoBigCast(BaseType,RetTypeInfo.type);
512
513 index_stack[sp]=RetTypeInfo.u.lpIndex;
514 bLiteralCalculation=0;
515
516 //スタックへプッシュ
517 PushReturnValue(RetTypeInfo.type);
518
519 if(type[sp]==DEF_OBJECT){
520 //Object型が戻ったときはヒープ領域にインスタンスが格納されている
521 //※後にfreeする必要あり
522 bUseHeap[sp]=1;
523 }
524
525 sp++;
526 break;
527 }
528
529
530
531 //該当する識別子が見当たらないときはエラー扱いにする
532 bError=1;
533 SetError(3,term,cp);
534 type[sp]=DEF_DOUBLE;
535 }
536 else{
537 //リテラル値
538 type[sp]=GetLiteralValue(term,&i64data,BaseType);
539Literal:
540 if(type[sp]==DEF_INT64||
541 type[sp]==DEF_QWORD||
542 type[sp]==DEF_DOUBLE){
543 //64ビット(符号有り整数/実数)
544
545 //push HILONG(dbl)
546 op_push_value((long)*(long *)(((char *)(&i64data))+4));
547
548 //push LOLONG(dbl)
549 op_push_value(*(long *)(&i64data));
550 }
551 else if(type[sp]==DEF_SINGLE){
552 //single実数
553
554 float flt;
555 memcpy(&dbl,&i64data,sizeof(double));
556 flt=(float)dbl;
557 memcpy(&i3,&flt,sizeof(long));
558
559 //push term
560 op_push_value(i3);
561 }
562 else{
563 //その他
564
565 //push term
566 op_push_value((long)i64data);
567
568 if((long)i64data==0) index_stack[sp]=LITERAL_NULL;
569 }
570
571
572 //リテラル値の種類
573 if(Is64Type(type[sp])==0&&IsRealNumberType(type[sp])==0){
574 //整数(符号有り/無し)
575
576 index_stack[sp]=GetLiteralIndex(i64data);
577 }
578 }
579 sp++;
580 break;
581
582 //論理演算子
583 case CALC_XOR:
584 //value[sp-2] xor= value[sp-1]
585 //xor演算
586 if(!Calc_Xor(type,index_stack,&sp)) goto error;
587 break;
588 case CALC_OR:
589 //value[sp-2] or= value[sp-1]
590 //or演算
591 if(!Calc_Or(type,index_stack,&sp)) goto error;
592 break;
593 case CALC_AND:
594 //value[sp-2] and= value[sp-1]
595 //and演算
596 if(!Calc_And(type,index_stack,&sp)) goto error;
597 break;
598 case CALC_NOT:
599 //value[sp-1]=Not value[sp-1]
600 //NOT演算子
601 if(!Calc_Not(type,sp)) goto error;
602 break;
603
604 //比較演算子
605 case CALC_PE:
606 //value[sp-2]<=value[sp-1]
607 if(!Calc_Relation_PE(type,index_stack,&sp)) goto error;
608 break;
609 case CALC_QE:
610 //value[sp-2]>=value[sp-1]
611 if(!Calc_Relation_QE(type,index_stack,&sp)) goto error;
612 break;
613 case CALC_P:
614 //value[sp-2]<value[sp-1]
615 if(!Calc_Relation_P(type,index_stack,&sp)) goto error;
616 break;
617 case CALC_Q:
618 //value[sp-2]>value[sp-1]
619 if(!Calc_Relation_Q(type,index_stack,&sp)) goto error;
620 break;
621 case CALC_NOTEQUAL:
622 //value[sp-2]<>value[sp-1]
623 if(!Calc_Relation_NotEqual(type,&sp)) goto error;
624 break;
625 case CALC_EQUAL:
626 //value[sp-2]=value[sp-1]
627 if(!Calc_Relation_Equal(type,&sp)) goto error;
628 break;
629
630 //ビットシフト
631 case CALC_SHL:
632 //value[sp-2]=value[sp-2]<<value[sp-1]
633 if(!Calc_SHL(type,&sp)) goto error;
634 break;
635 case CALC_SHR:
636 //value[sp-2]=value[sp-2]>>value[sp-1]
637 if(!Calc_SHR(type,&sp)) goto error;
638 break;
639
640 //算術演算
641 case CALC_ADDITION:
642 case CALC_SUBTRACTION:
643 case CALC_PRODUCT:
644 if(!CalcTwoTerm_Arithmetic(idCalc,type,index_stack,&sp)) goto error;
645 break;
646
647 case CALC_MOD:
648 //value[sp-2]%=value[sp-1]
649 //剰余演算
650 if(!Calc_Mod(type,&sp)) goto error;
651 break;
652 case CALC_QUOTIENT:
653 //value[sp-2]/=value[sp-1];
654 //除算
655 if(!Calc_Divide(type,&sp,BaseType)) goto error;
656 break;
657 case CALC_INTQUOTIENT:
658 //value[sp-2]/=value[sp-1]
659 //整数除算
660 if(!Calc_IntDivide(type,index_stack,&sp)) goto error;
661 break;
662 case CALC_MINUSMARK:
663 //value[sp-1]=-value[sp-1]
664 //符号反転
665 if(!Calc_MinusMark(type,sp)) goto error;
666 index_stack[sp-1]=-1;
667 break;
668 case CALC_POWER:
669 //べき乗演算(浮動小数点演算のみ)
670 if(!Calc_Power(type,&sp)) goto error;
671 break;
672 case CALC_AS:
673 //キャスト
674 if(!Calc_Cast(type,index_stack,&sp)) goto error;
675 break;
676
677 case CALC_BYVAL:
678 //ポインタ型→参照型
679 if( PTR_LEVEL( type[sp-1] ) <= 0 ){
680 //ポインタ型ではないとき
681 SetError( 3, NULL, cp );
682 goto error;
683 }
684
685 type[sp-1] = PTR_LEVEL_DOWN( type[sp-1] );
686
687 break;
688
689 default:
690 SetError(300,NULL,cp);
691 goto error;
692 }
693 }
694
695 if(bError) goto error;
696
697 if(sp!=1){
698 SetError(1,NULL,cp);
699 goto error;
700 }
701
702 if(bLiteralCalculation){
703 //右辺値が数値の定数式の場合
704 LONG_PTR lpClassIndex;
705 i2=StaticCalculation(true, Command,BaseType,&i64data,&lpClassIndex);
706
707 obp=BeforeObp;
708 pobj_SubAddrSchedule->num=Before_ProcAddrScheduleNum;
709 pobj_DataTableSchedule->num=Before_DataTableScheduleNum;
710 pobj_Reloc->copy(pobj_BackReloc);
711
712 if(i2==DEF_INT64||
713 i2==DEF_QWORD||
714 i2==DEF_DOUBLE){
715 //64ビット(符号有り整数/実数)
716
717 //push HILONG(i64data)
718 op_push_value((long)*(long *)(((char *)(&i64data))+4));
719
720 //push LOLONG(i64data)
721 op_push_value(*(long *)(&i64data));
722 }
723 else if(i2==DEF_SINGLE){
724 //single実数
725
726 memcpy(&dbl,&i64data,sizeof(_int64));
727
728 float flt;
729 flt=(float)dbl;
730 memcpy(&i3,&flt,sizeof(long));
731
732 //push flt
733 op_push_value(i3);
734 }
735 else{
736 //整数(符号有り/無し)
737
738 i3=(long)i64data;
739
740 if(i2==DEF_SBYTE||i2==DEF_BYTE||i2==DEF_BOOLEAN || (isUnicode==false&&i2==DEF_CHAR)) i3=i3&0x000000FF;
741 if(i2==DEF_INTEGER||i2==DEF_WORD || (isUnicode&&i2==DEF_CHAR)) i3=i3&0x0000FFFF;
742
743 //push term
744 op_push_value(i3);
745 }
746
747 type[0]=i2;
748 index_stack[0]=lpClassIndex;
749 }
750 else{
751 //右辺値が数値の定数式ではないとき
752 if(IS_LITERAL(index_stack[0])) index_stack[0]=-1;
753 }
754
755 if(plpIndex) *plpIndex=index_stack[0];
756 if(pbUseHeap) *pbUseHeap=bUseHeap[0];
757
758 int RetType;
759 RetType=type[0];
760 goto finish;
761
762
763error:
764 RetType=-1;
765 goto finish;
766
767
768finish:
769
770 for(i=0;i<pnum;i++){
771 if(values[i]) HeapDefaultFree(values[i]);
772 }
773
774 //再配置スケジュールバックアップ情報を解放
775 delete pobj_BackReloc;
776
777 return RetType;
778}
Note: See TracBrowser for help on using the repository browser.