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