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