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 2015
11*/
12
13#include "monetdb_config.h"
14#include "mal_instruction.h"
15#include "mal_function.h" /* for getPC() */
16#include "mal_utils.h"
17#include "mal_exception.h"
18#include "mal_listing.h"
19
20/*
21 * Since MAL programs can be created on the fly by linked-in query
22 * compilers, or transformed by optimizers, it becomes
23 * mandatory to be able to produce textual correct MAL programs
24 * from its internal representation for several purposes.
25 *
26 * Whenever there is an overriding property it is applied.
27 *
28 * The hiddenInstruction operator assumes a sufficiently large block
29 * to leave information on the signature behind.
30 *
31 * The protection against overflow is not tight.
32*/
33#define advance(X,B,L) while(*(X) && B+L>X)(X)++;
34
35/* Copy string in src to *dstp which has *lenp space available and
36 * terminate with a NULL byte. *dstp and *lenp are adjusted for the
37 * used space. If there is not enough space to copy all of src,
38 * return false, otherwise return true. The resulting string is
39 * always NULL-terminated. */
40static inline bool
41copystring(char **dstp, const char *src, size_t *lenp)
42{
43 size_t len = *lenp;
44 char *dst = *dstp;
45
46 if (src == NULL)
47 return true;
48 if (len > 0) {
49 while (*src && len > 1) {
50 *dst++ = *src++;
51 len--;
52 }
53 *dst = 0;
54 *dstp = dst;
55 *lenp = len;
56 }
57 return *src == 0;
58}
59
60static str
61renderTerm(MalBlkPtr mb, MalStkPtr stk, InstrPtr p, int idx, int flg)
62{
63 char *buf =0;
64 char *nme =0;
65 int nameused = 0;
66 size_t len = 0, maxlen = BUFSIZ;
67 ValRecord *val = 0;
68 char *cv =0;
69 str tpe;
70 int showtype = 0, closequote=0;
71 int varid = getArg(p,idx);
72
73 buf = GDKzalloc(maxlen);
74 if( buf == NULL) {
75 addMalException(mb, "renderTerm:Failed to allocate");
76 return NULL;
77 }
78 // show the name when required or is used
79 if ((flg & LIST_MAL_NAME) && !isVarConstant(mb,varid) && !isVarTypedef(mb,varid)) {
80 nme = getVarName(mb,varid);
81 len +=snprintf(buf, maxlen, "%s", nme);
82 nameused =1;
83 }
84 // show the value when required or being a constant
85 if( ((flg & LIST_MAL_VALUE) && stk != 0) || isVarConstant(mb,varid) ){
86 if (nameused){
87 strcat(buf + len,"=");
88 len++;
89 }
90
91 // locate value record
92 if (isVarConstant(mb,varid)){
93 val = &getVarConstant(mb, varid);
94 showtype= getVarType(mb,varid) != TYPE_str && getVarType(mb,varid) != TYPE_bit;
95 } else if( stk)
96 val = &stk->stk[varid];
97
98 if ((cv = VALformat(val)) == NULL) {
99 addMalException(mb, "renderTerm:Failed to allocate");
100 GDKfree(buf);
101 return NULL;
102 }
103 if (len + strlen(cv) >= maxlen) {
104 char *nbuf= GDKrealloc(buf, maxlen =len + strlen(cv) + BUFSIZ);
105
106 if( nbuf == 0){
107 GDKfree(buf);
108 GDKfree(cv);
109 addMalException(mb,"renderTerm:Failed to allocate");
110 return NULL;
111 }
112 buf = nbuf;
113 }
114
115 if( strcmp(cv,"nil") == 0){
116 strcat(buf+len,cv);
117 len += strlen(buf+len);
118 GDKfree(cv);
119 showtype = showtype || getBatType(getVarType(mb,varid)) > TYPE_str ||
120 ((isVarUDFtype(mb,varid) || isVarTypedef(mb,varid)) && isVarConstant(mb,varid)) || isaBatType(getVarType(mb,varid));
121 } else{
122 if ( !isaBatType(getVarType(mb,varid)) && getBatType(getVarType(mb,varid)) > TYPE_str ){
123 closequote = 1;
124 strcat(buf+len,"\"");
125 len++;
126 }
127 strcat(buf+len,cv);
128 len += strlen(buf+len);
129 GDKfree(cv);
130
131 if( closequote ){
132 strcat(buf+len,"\"");
133 len++;
134 }
135 showtype = showtype || closequote > TYPE_str || ((isVarUDFtype(mb,varid) || isVarTypedef(mb,varid) || (flg & (LIST_MAL_REMOTE | LIST_MAL_TYPE))) && isVarConstant(mb,varid)) ||
136 (isaBatType(getVarType(mb,varid)) && idx < p->retc);
137
138 if (stk && isaBatType(getVarType(mb,varid)) && stk->stk[varid].val.bval ){
139 BAT *d= BBPquickdesc(stk->stk[varid].val.bval, true);
140 if( d)
141 len += snprintf(buf+len,maxlen-len,"[" BUNFMT "]", BATcount(d));
142 }
143 }
144 }
145
146 // show the type when required or frozen by the user
147 // special care should be taken with constants, they may have been casted
148 if ((flg & LIST_MAL_TYPE) || (isVarUDFtype(mb, varid) && idx < p->retc) || isVarTypedef(mb,varid) || showtype){
149 strcat(buf + len,":");
150 len++;
151 tpe = getTypeName(getVarType(mb, varid));
152 len += snprintf(buf+len,maxlen-len,"%s",tpe);
153 GDKfree(tpe);
154 }
155
156 if( len >= maxlen)
157 addMalException(mb,"renderTerm:Value representation too large");
158 return buf;
159}
160
161/*
162It receives the space to store the definition
163The MAL profiler dumps some performance data at the
164beginning of each line.
165*/
166
167str
168fcnDefinition(MalBlkPtr mb, InstrPtr p, str t, int flg, str base, size_t len)
169{
170 int i;
171 str arg, tpe;
172
173 len -= t - base;
174 if (!flg && !copystring(&t, "#", &len))
175 return base;
176 if( mb->inlineProp && !copystring(&t, "inline ", &len))
177 return base;
178 if( mb->unsafeProp && !copystring(&t, "unsafe ", &len))
179 return base;
180 if( mb->sealedProp && !copystring(&t, "sealed ", &len))
181 return base;
182 if (!copystring(&t, operatorName(p->token), &len) ||
183 !copystring(&t, " ", &len) ||
184 !copystring(&t, getModuleId(p) ? getModuleId(p) : "user", &len) ||
185 !copystring(&t, ".", &len) ||
186 !copystring(&t, getFunctionId(p), &len) ||
187 !copystring(&t, "(", &len))
188 return base;
189
190 for (i = p->retc; i < p->argc; i++) {
191 arg = renderTerm(mb, 0, p, i, (LIST_MAL_NAME | LIST_MAL_TYPE | LIST_MAL_PROPS));
192 if (arg && !copystring(&t, arg, &len)) {
193 GDKfree(arg);
194 return base;
195 }
196 GDKfree(arg);
197 if( i<p->argc-1 && !copystring(&t, ", ", &len))
198 return base;
199 }
200
201 advance(t,base,len);
202 if (p->varargs & VARARGS && !copystring(&t, "...", &len))
203 return base;
204
205 if (p->retc == 1) {
206 if (!copystring(&t, "):", &len))
207 return base;
208 tpe = getTypeName(getVarType(mb, getArg(p,0)));
209 if (!copystring(&t, tpe, &len)) {
210 GDKfree(tpe);
211 return base;
212 }
213 GDKfree(tpe);
214 if (p->varargs & VARRETS && !copystring(&t, "...", &len))
215 return base;
216 } else {
217 if (!copystring(&t, ") (", &len))
218 return base;
219 for (i = 0; i < p->retc; i++) {
220 arg = renderTerm(mb, 0, p, i, (LIST_MAL_NAME | LIST_MAL_TYPE | LIST_MAL_PROPS));
221 if (arg && !copystring(&t, arg, &len)) {
222 GDKfree(arg);
223 return base;
224 }
225 GDKfree(arg);
226 if( i<p->retc-1 && !copystring(&t, ", ", &len))
227 return base;
228 }
229 if (p->varargs & VARRETS && !copystring(&t, "...", &len))
230 return base;
231 if (!copystring(&t, ")", &len))
232 return base;
233 }
234
235 if (mb->binding[0]) {
236 if (!copystring(&t, " address ", &len) ||
237 !copystring(&t, mb->binding, &len))
238 return base;
239 }
240 (void) copystring(&t, ";", &len);
241 return base;
242}
243
244str
245operatorName(int i)
246{
247 switch (i) {
248 case ASSIGNsymbol: return ":=";
249 case BARRIERsymbol: return "barrier";
250 case REDOsymbol: return "redo";
251 case LEAVEsymbol: return "leave";
252 case EXITsymbol: return "exit";
253 case RETURNsymbol: return "return";
254 case YIELDsymbol: return "yield";
255 case CATCHsymbol: return "catch";
256 case RAISEsymbol: return "raise";
257 case ENDsymbol: return "end";
258 case FUNCTIONsymbol: return "function";
259 case FACTORYsymbol: return "factory";
260 case COMMANDsymbol: return "command";
261 case PATTERNsymbol: return "pattern";
262 }
263 return "Undefined";
264}
265
266str
267instruction2str(MalBlkPtr mb, MalStkPtr stk, InstrPtr p, int flg)
268{
269 int i;
270 str base, t;
271 size_t len = 512 + (p->argc * 128); /* max realistic line length estimate */
272 str arg;
273
274 t = base = GDKmalloc(len);
275 if ( base == NULL)
276 return NULL;
277 if (!flg) {
278 *t++ = '#';
279 len--;
280 if (p->typechk == TYPE_UNKNOWN) {
281 *t++ = '!'; /* error */
282 len--;
283 }
284 }
285 *t = 0;
286 if (p->token == REMsymbol && !( getModuleId(p) && strcmp(getModuleId(p),"querylog") == 0 && getFunctionId(p) && strcmp(getFunctionId(p),"define") == 0)) {
287 /* do nothing */
288 } else if (p->barrier) {
289 if (p->barrier == LEAVEsymbol ||
290 p->barrier == REDOsymbol ||
291 p->barrier == RETURNsymbol ||
292 p->barrier == YIELDsymbol ||
293 p->barrier == RAISEsymbol) {
294 if (!copystring(&t, " ", &len))
295 return base;
296 }
297 arg = operatorName(p->barrier);
298 if (!copystring(&t, arg, &len) ||
299 !copystring(&t, " ", &len))
300 return base;
301 } else if( functionStart(p) && flg != LIST_MAL_CALL ){
302 return fcnDefinition(mb, p, t, flg, base, len + (t - base));
303 } else if (!functionExit(p) && flg!=LIST_MAL_CALL) {
304 // beautify with tabs
305 if (!copystring(&t, " ", &len))
306 return base;
307 }
308 switch (p->token<0?-p->token:p->token) {
309 case FCNcall:
310 case FACcall:
311 case PATcall:
312 case CMDcall:
313 case ASSIGNsymbol :
314 // is any variable explicit or used
315 for (i = 0; i < p->retc; i++)
316 if ( !isTmpVar(mb,getArg(p,i)) || isVarUsed(mb, getArg(p, i)) || isVarUDFtype(mb,getArg(p,i)))
317 break;
318
319 if (i == p->retc)
320 break;
321
322 /* display multi-assignment list */
323 if (p->retc > 1 && !copystring(&t, "(", &len))
324 return base;
325
326 for (i = 0; i < p->retc; i++) {
327 arg= renderTerm(mb, stk, p, i, flg);
328 if (arg) {
329 if (!copystring(&t, arg, &len)) {
330 GDKfree(arg);
331 return base;
332 }
333 GDKfree(arg);
334 }
335 if (i < p->retc - 1 && !copystring(&t, ", ", &len))
336 return base;
337 }
338 if (p->retc > 1 && !copystring(&t, ")", &len))
339 return base;
340
341 if (p->argc > p->retc || getFunctionId(p)) {
342 if (!copystring(&t, " := ", &len))
343 return base;
344 }
345 break;
346 case ENDsymbol:
347 if (!copystring(&t, "end ", &len) ||
348 !copystring(&t, getModuleId(getInstrPtr(mb,0)), &len) ||
349 !copystring(&t, ".", &len) ||
350 !copystring(&t, getFunctionId(getInstrPtr(mb, 0)), &len))
351 return base;
352 break;
353 case COMMANDsymbol:
354 case FUNCTIONsymbol:
355 case FACTORYsymbol:
356 case PATTERNsymbol:
357 if (flg & LIST_MAL_VALUE) {
358 if (!copystring(&t, operatorName(p->token), &len) ||
359 !copystring(&t, " ", &len))
360 return base;
361 }
362 return fcnDefinition(mb, p, t, flg, base, len + (t - base));
363 case REMsymbol:
364 case NOOPsymbol:
365 if (!copystring(&t, "#", &len))
366 return base;
367 if (getVar(mb, getArg(p, 0))->value.val.sval && getVar(mb, getArg(p, 0))->value.len > 0 &&
368 !copystring(&t, getVar(mb, getArg(p, 0))->value.val.sval, &len))
369 return base;
370 if (!copystring(&t, " ", &len))
371 return base;
372 break;
373 default:
374 i = snprintf(t, len, " unknown symbol ?%d? ", p->token);
375 if (i < 0 || (size_t) i >= len)
376 return base;
377 len -= (size_t) i;
378 t += i;
379 break;
380 }
381
382 if (getModuleId(p)) {
383 if (!copystring(&t, getModuleId(p), &len) ||
384 !copystring(&t, ".", &len))
385 return base;
386 }
387 if (getFunctionId(p)) {
388 if (!copystring(&t, getFunctionId(p), &len) ||
389 !copystring(&t, "(", &len))
390 return base;
391 } else if (p->argc > p->retc + 1) {
392 if (!copystring(&t, "(", &len))
393 return base;
394 }
395 for (i = p->retc; i < p->argc; i++) {
396 arg= renderTerm(mb, stk, p, i, flg);
397 if (arg) {
398 if (!copystring(&t, arg, &len)) {
399 GDKfree(arg);
400 return base;
401 }
402 GDKfree(arg);
403 }
404
405 if (i < p->argc -1 && !copystring(&t, ", ", &len))
406 return base;
407 }
408 if (getFunctionId(p) || p->argc > p->retc + 1) {
409 if (!copystring(&t, ")", &len))
410 return base;
411 }
412 if (p->token != REMsymbol){
413 if (!copystring(&t, ";", &len))
414 return base;
415 }
416 return base;
417}
418
419/* the MAL beautifier is meant to simplify correlation of MAL variables and
420 * the columns in the underlying database.
421 * If the status is set, then we consider the instruction DONE and the result variables
422 * should be shown as well.
423 */
424static str
425shortRenderingTerm(MalBlkPtr mb, MalStkPtr stk, InstrPtr p, int idx)
426{
427 str s, nme;
428 BAT *b;
429 ValRecord *val;
430 char *cv =0;
431 int varid = getArg(p,idx);
432 size_t len = BUFSIZ;
433
434 s= GDKmalloc(len);
435 if( s == NULL)
436 return NULL;
437 *s = 0;
438
439 if( isVarConstant(mb,varid) ){
440 val =&getVarConstant(mb, varid);
441 if ((cv = VALformat(val)) == NULL) {
442 GDKfree(s);
443 return NULL;
444 }
445 if (strlen(cv) >= len) {
446 char *nbuf;
447 len = strlen(cv);
448 nbuf = GDKrealloc(s, len + 1);
449 if (nbuf == NULL) {
450 GDKfree(s);
451 GDKfree(cv);
452 return NULL;
453 }
454 s = nbuf;
455 }
456 snprintf(s,len + 1,"%s",cv);
457 } else {
458 val = &stk->stk[varid];
459 if ((cv = VALformat(val)) == NULL) {
460 GDKfree(s);
461 return NULL;
462 }
463 nme = getVarName(mb, varid);
464 if ( isaBatType(getArgType(mb,p,idx))){
465 b = BBPquickdesc(stk->stk[varid].val.bval, true);
466 snprintf(s,BUFSIZ,"%s["BUNFMT"]" ,nme, b?BATcount(b):0);
467 } else
468 snprintf(s,BUFSIZ,"%s=%s ",nme,cv);
469 }
470 GDKfree(cv);
471 return s;
472}
473
474str
475shortStmtRendering(MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
476{
477 int i;
478 str base, s, t, nme;
479 size_t len= (mb->stop < 1000? 1000: mb->stop) * 128 /* max realistic line length estimate */;
480
481 base = s = GDKmalloc(len);
482 if ( s == NULL)
483 return s;
484 *s =0;
485 t=s;
486 if (p->token == REMsymbol && !( getModuleId(p) && strcmp(getModuleId(p),"querylog") == 0 && getFunctionId(p) && strcmp(getFunctionId(p),"define") == 0))
487 return base;
488 if (p->barrier == LEAVEsymbol ||
489 p->barrier == REDOsymbol ||
490 p->barrier == RETURNsymbol ||
491 p->barrier == YIELDsymbol ||
492 p->barrier == EXITsymbol ||
493 p->barrier == RAISEsymbol) {
494 snprintf(t,(len-(t-base)), "%s ", operatorName(p->barrier));
495 advance(t,base,len);
496 }
497 if( p->token == FUNCTIONsymbol) {
498 snprintf(t,(len-(t-base)), "function %s.", getModuleId(p));
499 advance(t,base,len);
500 }
501 if (p->token == ENDsymbol ){
502 snprintf(t,(len-(t-base)), "end %s.%s", getModuleId(getInstrPtr(mb,0)), getFunctionId(getInstrPtr(mb,0)));
503 return base;
504 }
505 // handle the result variables
506 for (i = 0; i < p->retc; i++)
507 if ( !isTmpVar(mb,getArg(p,i)) || isVarUsed(mb, getArg(p, i)) || isVarUDFtype(mb,getArg(p,i)))
508 break;
509
510 if (i == p->retc) // no result arguments
511 goto short_end;
512
513 /* display optional multi-assignment list */
514 if( getArgType(mb,p,0) != TYPE_void){
515 if (p->retc > 1 && t < base + len-1){
516 *t++ = '(';
517 *t=0;
518 }
519
520 for (i = 0; i < p->retc; i++) {
521 nme = shortRenderingTerm(mb, stk, p,i);
522 snprintf(t,(len-(t-base)), "%s%s", (i?", ":""), nme);
523 GDKfree(nme);
524 advance(t,base,len);
525 }
526 if (p->retc > 1 && t< base+len)
527 *t++ = ')';
528 if( t < base +len) *t++ = ':';
529 if( t < base +len) *t++ = '=';
530 if( t < base +len) *t++ = ' ';
531 }
532 *t =0;
533
534 short_end:
535 advance(t,base,len);
536
537 // handle the instruction mapping
538 snprintf(t, (len-(t-base)),"%s", (getFunctionId(p)?getFunctionId(p):""));
539 advance(t,base,len);
540
541 // handle the arguments, constants should be shown including their non-default type
542 /* display optional multi-assignment list */
543 if( t< base + len) *t++ = '(';
544 for (i = p->retc; i < p->argc; i++) {
545 nme = shortRenderingTerm(mb, stk, p,i);
546 snprintf(t,(len-(t-base)), "%s%s", (i!= p->retc? ", ":" "), nme);
547 GDKfree(nme);
548 advance(t,base,len);
549 if (i < p->retc - 1 && t < base+len){
550 *t++ = ',';
551 *t++ = ' ';
552 }
553 }
554 if( t < base + len) *t++ = ' ';
555 if( t < base + len) *t++ = ')';
556 *t=0;
557
558 if (t >= s + len)
559 throw(MAL,"instruction2str:","instruction too long");
560 return base;
561}
562
563/* Remote execution of MAL calls for more type/property information to be exchanged */
564str
565mal2str(MalBlkPtr mb, int first, int last)
566{
567 str ps = NULL, *txt;
568 int i, j;
569 size_t *len, totlen = 0;
570
571 txt = GDKmalloc(sizeof(str) * mb->stop);
572 len = GDKmalloc(sizeof(size_t) * mb->stop);
573
574 if( txt == NULL || len == NULL){
575 addMalException(mb,"mal2str: " MAL_MALLOC_FAIL);
576 GDKfree(txt);
577 GDKfree(len);
578 return NULL;
579 }
580 for (i = first; i < last; i++) {
581 if( i == 0)
582 txt[i] = instruction2str(mb, 0, getInstrPtr(mb, i), LIST_MAL_NAME | LIST_MAL_TYPE | LIST_MAL_PROPS);
583 else
584 txt[i] = instruction2str(mb, 0, getInstrPtr(mb, i), LIST_MAL_CALL | LIST_MAL_PROPS | LIST_MAL_REMOTE);
585#ifdef _DEBUG_LISTING_
586 fprintf(stderr,"%s\n",txt[i]);
587#endif
588
589 if ( txt[i])
590 totlen += len[i] = strlen(txt[i]);
591 else {
592 addMalException(mb,"mal2str: " MAL_MALLOC_FAIL);
593 GDKfree(len);
594 for (j = first; j < i; j++)
595 GDKfree(txt[j]);
596 GDKfree(txt);
597 return NULL;
598 }
599 }
600 ps = GDKmalloc(totlen + mb->stop + 1);
601 if( ps == NULL){
602 addMalException(mb,"mal2str: " MAL_MALLOC_FAIL);
603 GDKfree(len);
604 for (i = first; i < last; i++)
605 GDKfree(txt[i]);
606 GDKfree(txt);
607 return NULL;
608 }
609
610 totlen = 0;
611 for (i = first; i < last; i++) {
612 if( txt[i]){
613 strncpy(ps + totlen, txt[i], len[i]);
614 ps[totlen + len[i]] = '\n';
615 ps[totlen + len[i] + 1] = 0;
616 totlen += len[i] + 1;
617 GDKfree(txt[i]);
618 }
619 }
620 GDKfree(len);
621 GDKfree(txt);
622 return ps;
623}
624
625void
626printInstruction(stream *fd, MalBlkPtr mb, MalStkPtr stk, InstrPtr p, int flg)
627{
628 str ps;
629
630 if (fd == 0)
631 return;
632 ps = instruction2str(mb, stk, p, flg);
633 /* ps[strlen(ps)-1] = 0; remove '\n' */
634 if ( ps ){
635 mnstr_printf(fd, "%s%s", (flg & LIST_MAL_MAPI ? "=" : ""), ps);
636 GDKfree(ps);
637 } else {
638 mnstr_printf(fd,"#failed instruction2str()");
639 }
640 mnstr_printf(fd, "\n");
641}
642
643void
644fprintInstruction(FILE *fd, MalBlkPtr mb, MalStkPtr stk, InstrPtr p, int flg)
645{
646 str ps;
647
648 if (fd == 0)
649 return;
650 ps = instruction2str(mb, stk, p, flg);
651 /* ps[strlen(ps)-1] = 0; remove '\n' */
652 if ( ps ){
653 fprintf(fd, "%s%s", (flg & LIST_MAL_MAPI ? "=" : ""), ps);
654 GDKfree(ps);
655 } else {
656 fprintf(fd,"#failed instruction2str()");
657 }
658 fprintf(fd, "\n");
659}
660
661void
662printSignature(stream *fd, Symbol s, int flg)
663{
664 InstrPtr p;
665 str txt;
666
667 if ( s->def == 0 ){
668 mnstr_printf(fd, "missing definition of %s\n", s->name);
669 return;
670 }
671 txt = GDKzalloc(MAXLISTING); /* some slack for large blocks */
672 if( txt){
673 p = getSignature(s);
674 (void) fcnDefinition(s->def, p, txt, flg, txt, MAXLISTING);
675 mnstr_printf(fd, "%s\n", txt);
676 GDKfree(txt);
677 } else mnstr_printf(fd, "printSignature: " MAL_MALLOC_FAIL);
678}
679
680void showMalBlkHistory(stream *out, MalBlkPtr mb)
681{
682 MalBlkPtr m=mb;
683 InstrPtr p,sig;
684 int j=0;
685 str msg;
686
687 sig = getInstrPtr(mb,0);
688 m= m->history;
689 while(m){
690 p= getInstrPtr(m,m->stop-1);
691 if( p->token == REMsymbol){
692 msg= instruction2str(m, 0, p, FALSE);
693 if (msg ) {
694 mnstr_printf(out,"%s.%s[%2d] %s\n",
695 getModuleId(sig), getFunctionId(sig),j++,msg+3);
696 GDKfree(msg);
697 } else {
698 mnstr_printf(out,"#failed instruction2str()\n");
699 }
700 }
701 m= m->history;
702 }
703}
704