1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * (author) M. Kersten
11 * For documentation see website
12 */
13#include "monetdb_config.h"
14#include "mal_function.h"
15#include "mal_resolve.h" /* for isPolymorphic() & chkProgram() */
16#include "mal_interpreter.h" /* for showErrors() */
17#include "mal_listing.h"
18#include "mal_namespace.h"
19#include "mal_private.h"
20
21Symbol newFunction(str mod, str nme,int kind){
22 Symbol s;
23 InstrPtr p;
24 int varid;
25
26 if(mod == NULL || nme == NULL)
27 return NULL;
28
29 s = newSymbol(nme,kind);
30 if (s == NULL)
31 return NULL;
32
33 varid = newVariable(s->def,nme,strlen(nme),TYPE_any);
34 if( varid < 0){
35 freeSymbol(s);
36 return NULL;
37 }
38
39 p = newInstruction(NULL, mod, nme);
40 if (p == NULL) {
41 freeSymbol(s);
42 return NULL;
43 }
44 p->token = kind;
45 p->barrier = 0;
46 setDestVar(p, varid);
47 pushInstruction(s->def,p);
48 return s;
49}
50/*
51 * Optimizers may be interested in the function definition
52 * for obtaining properties. Rather than polution of the
53 * instruction record with a scope reference, we use a lookup function until it
54 * becomes a performance hindrance.
55 */
56Symbol getFunctionSymbol(Module scope, InstrPtr p){
57 Module m;
58 Symbol s;
59
60 for(m= findModule(scope,getModuleId(p)); m; m= m->link)
61 if(idcmp(m->name, getModuleId(p))==0 ) {
62 s= m->space[getSymbolIndex(getFunctionId(p))];
63 for(; s; s= s->peer)
64 if( getSignature(s)->fcn == p->fcn)
65 return s;
66 }
67 return 0;
68}
69
70int getPC(MalBlkPtr mb, InstrPtr p)
71{ int i;
72 for( i=0;i<mb->stop; i++)
73 if( getInstrPtr(mb,i)==p) return i;
74 return -1;
75}
76/*
77 * Checking the control flow structure is done by a single pass over the
78 * MAL program after the program has been type-checked.
79 * It should inspect all BARRIER and CATCH blocks for proper structure.
80 * If the flow is correct and not dependent on an undefined typed instruction
81 * we avoid doing this check any further.
82 */
83#define DEPTH 128
84
85void chkFlow(MalBlkPtr mb)
86{ int i,j,k, v,lastInstruction;
87 int pc[DEPTH];
88 int var[DEPTH];
89 InstrPtr stmt[DEPTH];
90 int btop=0;
91 int endseen=0, retseen=0, yieldseen=0;
92 InstrPtr p;
93 str msg = MAL_SUCCEED;
94
95 if ( mb->errors != MAL_SUCCEED)
96 return ;
97 lastInstruction = mb->stop-1;
98 for(i= 0; i<mb->stop; i++){
99 p= getInstrPtr(mb,i);
100 /* we have to keep track on the maximal arguments/block
101 because it is needed by the interpreter */
102 switch( p->barrier){
103 case BARRIERsymbol:
104 case CATCHsymbol:
105 if(btop== DEPTH){
106 mb->errors = createMalException(mb,0,TYPE,"Too many nested MAL blocks");
107 return;
108 }
109 pc[btop]= i;
110 v= var[btop]= getDestVar(p);
111 stmt[btop]=p;
112
113 for(j=btop-1;j>=0;j--)
114 if( v==var[j]){
115 mb->errors = createMalException(mb,i,MAL,
116 "recursive %s[%d] shields %s[%d]",
117 getVarName(mb,v), pc[j],
118 getFcnName(mb),pc[i]);
119 return;
120 }
121
122 btop++;
123 break;
124 case EXITsymbol:
125 v= getDestVar(p);
126 if( btop>0 && var[btop-1] != v){
127 mb->errors = createMalException( mb,i,MAL,
128 "exit-label '%s' doesnot match '%s'",
129 getVarName(mb,v), getVarName(mb,var[btop-1]));
130 }
131 if(btop==0){
132 mb->errors = createMalException(mb,i,MAL,
133 "exit-label '%s' without begin-label",
134 getVarName(mb,v));
135 continue;
136 }
137 /* search the matching block */
138 for(j=btop-1;j>=0;j--)
139 if( var[j]==v) break;
140 if(j>=0) btop= j; else btop--;
141
142 /* retrofit LEAVE/REDO instructions */
143 stmt[btop]->jump= i;
144 for(k=pc[btop]; k<i; k++){
145 InstrPtr p1= getInstrPtr(mb,k);
146 if( getDestVar(p1)==v ) {
147 /* handle assignments with leave/redo option*/
148 if(p1->barrier== LEAVEsymbol )
149 p1->jump= i;
150 if( p1->barrier==REDOsymbol )
151 p1->jump= pc[btop]+1;
152 }
153 }
154 break;
155 case LEAVEsymbol:
156 case REDOsymbol:
157 v= getDestVar(p);
158 for(j=btop-1;j>=0;j--)
159 if( var[j]==v) break;
160 if(j<0){
161 str nme=getVarName(mb,v);
162 mb->errors = createMalException(mb,i,MAL,
163 "label '%s' not in guarded block", nme);
164 }
165 break;
166 case YIELDsymbol:
167 { InstrPtr ps= getInstrPtr(mb,0);
168 if( ps->token != FACTORYsymbol){
169 mb->errors = createMalException(mb,i,MAL, "yield misplaced!");
170 }
171 yieldseen= TRUE;
172 }
173 /* fall through */
174 case RETURNsymbol:
175 {
176 InstrPtr ps = getInstrPtr(mb, 0);
177 int e;
178 if (p->barrier == RETURNsymbol)
179 yieldseen = FALSE; /* always end with a return */
180 if (ps->retc != p->retc) {
181 mb->errors = createMalException( mb, i, MAL,
182 "invalid return target!");
183 } else
184 if (ps->typechk == TYPE_RESOLVED)
185 for (e = 0; e < p->retc; e++) {
186 if (resolveType(getArgType(mb, ps, e), getArgType(mb, p, e)) < 0) {
187 str tpname = getTypeName(getArgType(mb, p, e));
188 mb->errors = createMalException(mb, i, TYPE,
189 "%s type mismatch at type '%s'\n",
190 (p->barrier == RETURNsymbol ? "RETURN" : "YIELD"), tpname);
191 GDKfree(tpname);
192 }
193 }
194 }
195 //if (btop == 0)
196 retseen = 1;
197 break;
198 case RAISEsymbol:
199 endseen = 1;
200 break;
201 case ENDsymbol:
202 endseen =1;
203 break;
204 default:
205 if( isaSignature(p) ){
206 if( p->token == REMsymbol){
207 /* do nothing */
208 } else if( i) {
209 str msg=instruction2str(mb,0,p,TRUE);
210 mb->errors = createMalException( mb,i,MAL, "signature misplaced\n!%s",msg);
211 GDKfree(msg);
212 }
213 }
214 }
215 }
216
217 if(msg == MAL_SUCCEED && lastInstruction < mb->stop-1 ){
218 mb->errors = createMalException( mb,lastInstruction,SYNTAX,
219 "instructions after END");
220#ifdef DEBUG_MAL_FCN
221 fprintFunction(stderr, mb, 0, LIST_MAL_ALL);
222#endif
223 }
224 if( endseen)
225 for(btop--; btop>=0;btop--){
226 mb->errors = createMalException( mb,lastInstruction, SYNTAX,
227 "barrier '%s' without exit in %s[%d]",
228 getVarName(mb,var[btop]),getFcnName(mb),i);
229 }
230 p= getInstrPtr(mb,0);
231 if( !isaSignature(p)){
232 mb->errors = createMalException( mb,0,SYNTAX,"signature missing");
233 }
234 if( retseen == 0){
235 if( getArgType(mb,p,0)!= TYPE_void &&
236 (p->token==FUNCTIONsymbol || p->token==FACTORYsymbol)){
237 mb->errors = createMalException( mb,0,SYNTAX,"RETURN missing");
238 }
239 }
240 if ( msg== MAL_SUCCEED && yieldseen && getArgType(mb,p,0)!= TYPE_void){
241 mb->errors = createMalException( mb,0,SYNTAX,"RETURN missing");
242 }
243}
244
245/*
246 * A code may contain temporary names for marking barrier blocks.
247 * Since they are introduced by the compiler, the parser should locate
248 * them itself when encountering the LEAVE,EXIT,REDO.
249 * The starting position is mostly the last statement entered.
250 * Purposely, the nameless envelops searches the name of the last
251 * unclosed block. All others are ignored.
252 */
253int getBarrierEnvelop(MalBlkPtr mb){
254 int pc;
255 InstrPtr p;
256 for(pc= mb->stop-2 ; pc>=0; pc--){
257 p= getInstrPtr(mb,pc);
258 if( blockExit(p)){
259 int l= p->argv[0];
260 for(; pc>=0;pc--){
261 p= getInstrPtr(mb,pc);
262 if( blockStart(p) && p->argv[0]==l) break;
263 }
264 continue;
265 }
266 if( blockStart(p) ) return p->argv[0];
267 }
268 return newTmpVariable(mb,TYPE_any);
269}
270
271static void replaceTypeVar(MalBlkPtr mb, InstrPtr p, int v, malType t){
272 int j,i,x,y;
273#ifdef DEBUG_MAL_FCN
274 char *tpenme = getTypeName(t);
275 fprintf(stderr,"#replace type _%d by type %s\n",v, tpenme);
276 GDKfree(tpenme);
277#endif
278 for(j=0; j<mb->stop; j++){
279 p= getInstrPtr(mb,j);
280#ifdef DEBUG_MAL_FCN
281 fprintInstruction(stderr,mb,0,p,LIST_MAL_ALL);
282#endif
283 if( p->polymorphic)
284 for(i=0;i<p->argc; i++)
285 if( isPolymorphic(x= getArgType(mb,p,i))) {
286 if( isaBatType(x)){
287 int tail;
288 int tx;
289 tail = getBatType(x);
290 tx = getTypeIndex(x);
291 if(v && tx == v && tail == TYPE_any){
292 tx= 0;
293 tail = t;
294 }
295 y= newBatType(tail);
296 setTypeIndex(y,tx);
297 setArgType(mb,p,i,y);
298#ifdef DEBUG_MAL_FCN
299 {
300 char *xnme = getTypeName(x), *ynme = getTypeName(y);
301 fprintf(stderr," %d replaced %s->%s \n",i,xnme,ynme);
302 GDKfree(xnme);
303 GDKfree(ynme);
304 }
305#endif
306 } else
307 if(getTypeIndex(x) == v){
308#ifdef DEBUG_MAL_FCN
309 char *xnme = getTypeName(x);
310 fprintf(stderr," replace x= %s polymorphic\n",xnme);
311 GDKfree(xnme);
312#endif
313 setArgType(mb,p,i,t);
314 }
315#ifdef DEBUG_MAL_FCN
316 else {
317 char *xnme = getTypeName(x);
318 fprintf(stderr," non x= %s %d\n",xnme,getTypeIndex(x));
319 GDKfree(xnme);
320 }
321#endif
322 }
323#ifdef DEBUG_MAL_FCN
324 fprintInstruction(stderr,mb,0,p,LIST_MAL_ALL);
325#endif
326 }
327}
328
329/* insert a symbol into the symbol table just before the symbol
330 * "before". */
331static void
332insertSymbolBefore(Module scope, Symbol prg, Symbol before)
333{
334 InstrPtr sig;
335 int t;
336 Symbol s;
337
338 assert(strcmp(prg->name, before->name) == 0);
339 sig = getSignature(prg);
340 if (getModuleId(sig) && getModuleId(sig) != scope->name) {
341 Module c = findModule(scope, getModuleId(sig));
342 if (c)
343 scope = c;
344 }
345 t = getSymbolIndex(getFunctionId(sig));
346 assert(scope->space != NULL);
347 assert(scope->space[t] != NULL);
348 s = scope->space[t];
349 prg->skip = before->skip;
350 prg->peer = before;
351 if (s == before) {
352 scope->space[t] = prg;
353 } else {
354 for (;;) {
355 assert(s != NULL);
356 if (s->skip == before) {
357 s->skip = prg;
358 }
359 if (s->peer == before) {
360 s->peer = prg;
361 break;
362 }
363 s = s->peer;
364 }
365 }
366}
367
368/*
369 * Upon cloning a function we should remove all the polymorphic flags.
370 * Otherwise we may end up with a recursive clone.
371 */
372Symbol
373cloneFunction(Module scope, Symbol proc, MalBlkPtr mb, InstrPtr p)
374{
375 Symbol new;
376 int i,v;
377 InstrPtr pp;
378
379#ifdef DEBUG_CLONE
380 fprintf(stderr,"clone the function %s to scope %s\n",
381 proc->name,scope->name);
382 fprintInstruction(stderr,mb,0,p,LIST_MAL_ALL);
383#endif
384 new = newFunction(scope->name, proc->name, getSignature(proc)->token);
385 if( new == NULL){
386 fprintf(stderr,"cloneFunction() failed");
387 return NULL;
388 }
389 freeMalBlk(new->def);
390 if((new->def = copyMalBlk(proc->def)) == NULL) {
391 freeSymbol(new);
392 fprintf(stderr,"cloneFunction() failed");
393 return NULL;
394 }
395 /* now change the definition of the original proc */
396#ifdef DEBUG_CLONE
397 fprintf(stderr, "CLONED VERSION\n");
398 fprintFunction(stderr, new->def, 0, LIST_MAL_ALL);
399#endif
400 /* check for errors after fixation , TODO*/
401 pp = getSignature(new);
402 for (i = 0; i < pp->argc; i++)
403 if (isPolymorphic(v = getArgType(new->def,pp, i))) {
404 int t = getArgType(mb, p, i);
405
406 if (v == TYPE_any)
407 replaceTypeVar(new->def, pp, v, t);
408 if (isaBatType(v)) {
409 if (getTypeIndex(v))
410 replaceTypeVar(new->def, pp, getTypeIndex(v), getBatType(t));
411 } else
412 replaceTypeVar(new->def, pp, getTypeIndex(v), t);
413 }
414#ifdef DEBUG_MAL_FCN
415 else {
416 char *tpenme = getTypeName(v);
417 fprintf(stderr,"%d remains %s\n", i, tpenme);
418 GDKfree(tpenme);
419 }
420#endif
421 /* include the function at the proper place in the scope */
422 insertSymbolBefore(scope, new, proc);
423 /* clear polymorphic and type to force analysis*/
424 for (i = 0; i < new->def->stop; i++) {
425 pp = getInstrPtr(new->def, i);
426 pp->typechk = TYPE_UNKNOWN;
427 pp->polymorphic = 0;
428 }
429 /* clear type fixations */
430 for (i = 0; i < new->def->vtop; i++)
431 clrVarFixed(new->def, i);
432
433#ifdef DEBUG_MAL_FCN
434 fprintf(stderr, "FUNCTION TO BE CHECKED\n");
435 fprintFunction(stderr, new->def, 0, LIST_MAL_ALL);
436#endif
437
438 /* check for errors after fixation , TODO*/
439 /* beware, we should now ignore any cloning */
440 if (proc->def->errors == 0) {
441 chkProgram(scope,new->def);
442 if(new->def->errors){
443 assert(mb->errors == NULL);
444 mb->errors = new->def->errors;
445 mb->errors = createMalException(mb,0,TYPE,"Error in cloned function");
446 new->def->errors = 0;
447#ifdef DEBUG_MAL_FCN
448 fprintFunction(stderr, new->def, 0, LIST_MAL_ALL);
449#endif
450 }
451 }
452#ifdef DEBUG_CLONE
453 fprintf(stderr, "newly cloned function added to %s %d \n",
454 scope->name, i);
455 fprintFunction(stderr, new->def, 0, LIST_MAL_ALL);
456#endif
457 return new;
458}
459
460/*
461 * For commands we do not have to clone the routine. We merely have to
462 * assure that the type-constraints are obeyed. The resulting type
463 * is returned.
464 */
465void
466debugFunction(stream *fd, MalBlkPtr mb, MalStkPtr stk, int flg, int first, int step)
467{
468 int i,j;
469 str ps;
470 InstrPtr p;
471
472 if (mb == NULL) {
473 mnstr_printf(fd, "# function definition missing\n");
474 return;
475 }
476 if ( flg == 0 || step < 0 || first < 0 )
477 return;
478
479 for (i = first; i < first +step && i < mb->stop; i++){
480 ps = instruction2str(mb, stk, (p=getInstrPtr(mb, i)), flg);
481 if (ps) {
482 if (p->token == REMsymbol)
483 mnstr_printf(fd,"%-40s\n",ps);
484 else {
485 mnstr_printf(fd,"%-40s\t#[%d] ("BUNFMT") %s ",ps, i, getRowCnt(mb,getArg(p,0)), (p->blk? p->blk->binding:""));
486 for(j =0; j < p->retc; j++)
487 mnstr_printf(fd,"%d ",getArg(p,j));
488 if( p->argc - p->retc > 0)
489 mnstr_printf(fd,"<- ");
490 for(; j < p->argc; j++)
491 mnstr_printf(fd,"%d ",getArg(p,j));
492 // also show type check property
493 if( p->typechk == TYPE_UNKNOWN)
494 mnstr_printf(fd," type check needed ");
495 mnstr_printf(fd,"\n");
496 }
497 GDKfree(ps);
498 } else mnstr_printf(fd,"#failed instruction2str()\n");
499 }
500}
501
502void
503listFunction(stream *fd, MalBlkPtr mb, MalStkPtr stk, int flg, int first, int size)
504{
505 int i;
506 if (mb == NULL) {
507 mnstr_printf(fd, "# function definition missing\n");
508 return;
509 }
510 if ( flg == 0)
511 return;
512 assert(size>=0);
513 assert(first>=0 && first <mb->stop);
514 if (flg & LIST_MAL_MAPI) {
515 size_t len = 0;
516 str ps;
517 mnstr_printf(fd, "&1 0 %d 1 %d\n", /* type id rows columns tuples */
518 mb->stop, mb->stop);
519 mnstr_printf(fd, "%% .explain # table_name\n");
520 mnstr_printf(fd, "%% mal # name\n");
521 mnstr_printf(fd, "%% clob # type\n");
522 for (i = first; i < first +size && i < mb->stop; i++) {
523 ps = instruction2str(mb, stk, getInstrPtr(mb, i), flg);
524 if (ps) {
525 size_t l = strlen(ps);
526 if (l > len)
527 len = l;
528 GDKfree(ps);
529 } else mnstr_printf(fd,"#failed instruction2str()\n");
530 }
531 mnstr_printf(fd, "%% %zu # length\n", len);
532 }
533 for (i = first; i < first +size && i < mb->stop; i++)
534 printInstruction(fd, mb, stk, getInstrPtr(mb, i), flg);
535}
536
537void printFunction(stream *fd, MalBlkPtr mb, MalStkPtr stk, int flg)
538{
539 int i,j;
540 InstrPtr p;
541 // Set the used bits properly
542 for(i=0; i< mb->vtop; i++)
543 clrVarUsed(mb,i);
544 for(i=0; i< mb->stop; i++){
545 p= getInstrPtr(mb,i);
546 for(j= p->retc; j<p->argc; j++)
547 setVarUsed(mb, getArg(p,j));
548 if( p->barrier)
549 for(j= 0; j< p->retc; j++)
550 setVarUsed(mb, getArg(p,j));
551 }
552 listFunction(fd,mb,stk,flg,0,mb->stop);
553}
554
555void fprintFunction(FILE *fd, MalBlkPtr mb, MalStkPtr stk, int flg)
556{
557 int i,j;
558 InstrPtr p;
559 // Set the used bits properly
560 for(i=0; i< mb->vtop; i++)
561 clrVarUsed(mb,i);
562 for(i=0; i< mb->stop; i++){
563 p= getInstrPtr(mb,i);
564 for(j= p->retc; j<p->argc; j++)
565 setVarUsed(mb, getArg(p,j));
566 if( p->barrier)
567 for(j= 0; j< p->retc; j++)
568 setVarUsed(mb, getArg(p,j));
569 }
570 for (i = 0; i < mb->stop; i++)
571 fprintInstruction(fd, mb, stk, getInstrPtr(mb, i), flg);
572}
573
574/* initialize the static scope boundaries for all variables */
575void
576setVariableScope(MalBlkPtr mb)
577{
578 int pc, k, depth=0, dflow= -1;
579 InstrPtr p;
580
581 /* reset the scope admin */
582 for (k = 0; k < mb->vtop; k++)
583 if( isVarConstant(mb,k)){
584 setVarScope(mb,k,0);
585 setVarDeclared(mb,k,0);
586 setVarUpdated(mb,k,0);
587 setVarEolife(mb,k,mb->stop);
588 } else {
589 setVarScope(mb,k,0);
590 setVarDeclared(mb,k,0);
591 setVarUpdated(mb,k,0);
592 setVarEolife(mb,k,0);
593 }
594
595 for (pc = 0; pc < mb->stop; pc++) {
596 p = getInstrPtr(mb, pc);
597 if( p->token == NOOPsymbol)
598 continue;
599
600 if( blockStart(p)){
601 if (getModuleId(p) && getFunctionId(p) && strcmp(getModuleId(p),"language")==0 && strcmp(getFunctionId(p),"dataflow")==0){
602 if( dflow != -1)
603 addMalException(mb,"setLifeSpan nested dataflow blocks not allowed" );
604 dflow= depth;
605 } else
606 depth++;
607 }
608
609 for (k = 0; k < p->argc; k++) {
610 int v = getArg(p,k);
611 if( isVarConstant(mb,v) && getVarUpdated(mb,v) == 0)
612 setVarUpdated(mb,v, pc);
613
614 if ( getVarDeclared(mb,v) == 0 ){
615 setVarDeclared(mb,v, pc);
616 setVarScope(mb,v,depth);
617 }
618 if (k < p->retc )
619 setVarUpdated(mb,v, pc);
620 if ( getVarScope(mb,v) == depth )
621 setVarEolife(mb,v,pc);
622
623 if ( k >= p->retc && getVarScope(mb,v) < depth )
624 setVarEolife(mb,v,-1);
625 }
626 /*
627 * At a block exit we can finalize all variables defined within that block.
628 * This does not hold for dataflow blocks. They merely direct the execution
629 * thread, not the syntactic scope.
630 */
631 if( blockExit(p) ){
632 for (k = 0; k < mb->vtop; k++)
633 if ( getVarEolife(mb,k) == 0 && getVarScope(mb,k) ==depth )
634 setVarEolife(mb,k,pc);
635 else if ( getVarEolife(mb,k) == -1 )
636 setVarEolife(mb,k,pc);
637
638 if( dflow == depth)
639 dflow= -1;
640 else depth--;
641 }
642 if( blockReturn(p)){
643 for (k = 0; k < p->argc; k++)
644 setVarEolife(mb,getArg(p,k),pc);
645 }
646 }
647 for (k = 0; k < mb->vtop; k++)
648 if( getVarEolife(mb,k) == 0)
649 setVarEolife(mb,k, mb->stop-1);
650}
651
652int
653isLoopBarrier(MalBlkPtr mb, int pc){
654 InstrPtr p;
655 int varid;
656 p= getInstrPtr(mb,pc);
657 if( p->barrier != BARRIERsymbol)
658 return 0;
659 varid= getDestVar(p);
660 for(pc++; pc< mb->stop; pc++){
661 p= getInstrPtr(mb,pc);
662 if( p->barrier == REDOsymbol && getDestVar(p)== varid)
663 return 1;
664 if( p->barrier == EXITsymbol && getDestVar(p)== varid)
665 break;
666 }
667 return 0;
668}
669int
670getBlockBegin(MalBlkPtr mb,int pc){
671 InstrPtr p;
672 int varid=0,i;
673
674 for(i= pc; i< mb->stop; i++){
675 p= getInstrPtr(mb,i);
676 if( p->barrier == EXITsymbol ){
677 varid= getDestVar(p);
678 break;
679 }
680 }
681 if( i==mb->stop) return 0;
682
683 for(; pc> 0; pc--){
684 p= getInstrPtr(mb,pc);
685 if( (p->barrier == BARRIERsymbol || p->barrier == CATCHsymbol) &&
686 getDestVar(p)== varid)
687 return pc;
688 }
689 return 0;
690}
691int
692getBlockExit(MalBlkPtr mb,int pc){
693 InstrPtr p;
694 int varid;
695 p= getInstrPtr(mb,pc);
696 if( p->barrier != BARRIERsymbol && p->barrier != CATCHsymbol)
697 return 0;
698 varid= getDestVar(p);
699 for(pc++; pc< mb->stop; pc++){
700 p= getInstrPtr(mb,pc);
701 if( p->barrier == EXITsymbol && getDestVar(p)== varid)
702 return pc;
703 }
704 return 0;
705}
706/*
707 * Variable declaration
708 * Variables are implicitly declared upon first use.
709 * This feature may become a source of runtime errors and
710 * complicates the analyse during optimization.
711 * Therefore, in line with the flow of control check,
712 * we make sure that all variables are properly initialized
713 * before being used. Since barrier blocks may be skipped at
714 * runtime, they actually introduce a separate scope.
715 * Variables declared within a block may not be used outside it.
716 * Variables can only be declared once.
717 *
718 * In many situation chkFlow and chkDeclarations should be called
719 * together. Moreover, an erroneous chkFlow most likely implies
720 * errors in the declarations as well.
721 *
722 * Since in interactive mode each statement is handled separately,
723 * we have to remember the scope assigned to a variable.
724 */
725void clrDeclarations(MalBlkPtr mb){
726 int i;
727 for(i=0;i<mb->vtop; i++){
728 clrVarInit(mb,i);
729 clrVarUsed(mb,i);
730 clrVarDisabled(mb,i);
731 }
732}
733
734void chkDeclarations(MalBlkPtr mb){
735 int pc,i, k,l;
736 InstrPtr p;
737 short blks[MAXDEPTH], top= 0, blkId=1;
738 int dflow = -1;
739 str msg = MAL_SUCCEED;
740
741 if( mb->errors)
742 return;
743 blks[top] = blkId;
744
745 /* initialize the scope */
746 for(i=0; i< mb->vtop; i++)
747 setVarScope(mb,i,0);
748
749 /* all signature variables are declared at outer level */
750 p= getInstrPtr(mb,0);
751 for(k=0;k<p->argc; k++)
752 setVarScope(mb, getArg(p,k), blkId);
753
754 for(pc=1;pc<mb->stop; pc++){
755 p= getInstrPtr(mb,pc);
756 if ( p->token == REMsymbol || p->token == NOOPsymbol)
757 continue;
758 /* check correct use of the arguments*/
759 for(k=p->retc;k<p->argc; k++) {
760 l=getArg(p,k);
761 setVarUsed(mb,l);
762 if( getVarScope(mb,l) == 0){
763 /*
764 * The problem created here is that only variables are
765 * recognized that are declared through instructions.
766 * For interactive code, and code that is based on a global
767 * stack this is insufficient. In those cases, the variable
768 * can be defined in a previous execution.
769 * We have to recognize if the declaration takes place
770 * in the context of a global stack.
771 */
772 if( p->barrier == CATCHsymbol){
773 setVarScope(mb, l, blks[0]);
774 } else if( !( isVarConstant(mb, l) || isVarTypedef(mb,l)) &&
775 !isVarInit(mb,l) ) {
776 mb->errors = createMalException( mb,pc,TYPE,
777 "'%s' may not be used before being initialized",
778 getVarName(mb,l));
779 }
780 } else if( !isVarInit(mb,l) ){
781 /* is the block still active ? */
782 for( i=0; i<= top; i++)
783 if( blks[i] == getVarScope(mb,l) )
784 break;
785 if( i> top || blks[i]!= getVarScope(mb,l) ){
786 mb->errors = createMalException( mb,pc,TYPE,
787 "'%s' used outside scope",
788 getVarName(mb,l));
789 }
790 }
791 if( blockCntrl(p) || blockStart(p) )
792 setVarInit(mb, l);
793 }
794 /* define variables */
795 for(k=0; k<p->retc; k++){
796 l= getArg(p,k);
797 if (isVarInit(mb, l) && getVarScope(mb,l) == 0) {
798 /* first time we see this variable and it is already
799 * initialized: assume it exists globally */
800 setVarScope(mb, l, blks[0]);
801 }
802 setVarInit(mb,l);
803 if( getVarScope(mb,l) == 0){
804 /* variable has not been defined yet */
805 /* exceptions are always declared at level 1 */
806 if( p->barrier == CATCHsymbol)
807 setVarScope(mb, l, blks[0]);
808 else
809 setVarScope(mb, l, blks[top]);
810#ifdef DEBUG_MAL_FCN
811 fprintf(stderr,"#defined %s in block %d\n", getVarName(mb,l), getVarScope(mb,l));
812#endif
813 }
814 if( blockCntrl(p) || blockStart(p) )
815 setVarUsed(mb, l);
816 }
817 if( p->barrier && msg == MAL_SUCCEED){
818 if ( blockStart(p)){
819 if( top == MAXDEPTH-2){
820 mb->errors = createMalException(mb,pc,SYNTAX, "too deeply nested MAL program");
821 return;
822 }
823 blkId++;
824 if (getModuleId(p) && getFunctionId(p) && strcmp(getModuleId(p),"language")==0 && strcmp(getFunctionId(p),"dataflow")== 0){
825 if( dflow != -1){
826 mb->errors = createMalException(mb,0, TYPE,"setLifeSpan nested dataflow blocks not allowed" );
827 }
828 dflow= blkId;
829 }
830 blks[++top]= blkId;
831#ifdef DEBUG_MAL_FCN
832 fprintf(stderr,"#new block %d at top %d\n",blks[top], top);
833#endif
834 }
835 if( blockExit(p) && top > 0 ){
836#ifdef DEBUG_MAL_FCN
837 fprintf(stderr,"leave block %d at top %d\n",blks[top], top);
838#endif
839 if( dflow == blkId){
840 dflow = -1;
841 } else
842 /*
843 * At the end of the block we should reset the status of all variables
844 * defined within the block. For, the block could have been skipped
845 * leading to uninitialized variables.
846 */
847 for (l = 0; l < mb->vtop; l++)
848 if( getVarScope(mb,l) == blks[top]){
849 setVarScope(mb,l, 0);
850 clrVarInit(mb,l);
851 }
852 top--;
853 }
854 }
855 }
856}
857