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 | |
21 | Symbol 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 | */ |
56 | Symbol 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 | |
70 | int 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 | |
85 | void 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 | */ |
253 | int 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 | |
271 | static 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". */ |
331 | static void |
332 | insertSymbolBefore(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 | */ |
372 | Symbol |
373 | cloneFunction(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 | */ |
465 | void |
466 | debugFunction(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 | |
502 | void |
503 | listFunction(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 | |
537 | void 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 | |
555 | void 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 */ |
575 | void |
576 | setVariableScope(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 | |
652 | int |
653 | isLoopBarrier(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 | } |
669 | int |
670 | getBlockBegin(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 | } |
691 | int |
692 | getBlockExit(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 | */ |
725 | void 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 | |
734 | void 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 | |