1/* -----------------------------------------------------------------------------
2 * See the LICENSE file for information on copyright, usage and redistribution
3 * of SWIG, and the README file for authors - http://www.swig.org/release.html.
4 *
5 * emit.cxx
6 *
7 * Useful functions for emitting various pieces of code.
8 * ----------------------------------------------------------------------------- */
9
10char cvsroot_emit_cxx[] = "$Id: emit.cxx 11471 2009-07-29 20:52:29Z wsfulton $";
11
12#include "swigmod.h"
13
14/* -----------------------------------------------------------------------------
15 * emit_return_variable()
16 *
17 * Emits a variable declaration for a function return value.
18 * The variable name is always called result.
19 * n => Node of the method being wrapped
20 * rt => the return type
21 * f => the wrapper to generate code into
22 * ----------------------------------------------------------------------------- */
23
24void emit_return_variable(Node *n, SwigType *rt, Wrapper *f) {
25
26  if (!GetFlag(n, "tmap:out:optimal")) {
27    if (rt && (SwigType_type(rt) != T_VOID)) {
28      SwigType *vt = cplus_value_type(rt);
29      SwigType *tt = vt ? vt : rt;
30      SwigType *lt = SwigType_ltype(tt);
31      String *lstr = SwigType_str(lt, "result");
32      if (SwigType_ispointer(lt)) {
33        Wrapper_add_localv(f, "result", lstr, "= 0", NULL);
34      } else {
35        Wrapper_add_local(f, "result", lstr);
36      }
37      if (vt) {
38        Delete(vt);
39      }
40      Delete(lt);
41      Delete(lstr);
42    }
43  }
44}
45
46/* -----------------------------------------------------------------------------
47 * emit_parameter_variables()
48 *
49 * Emits a list of variable declarations for function parameters.
50 * The variable names are always called arg1, arg2, etc...
51 * l => the parameter list
52 * f => the wrapper to generate code into
53 * ----------------------------------------------------------------------------- */
54
55void emit_parameter_variables(ParmList *l, Wrapper *f) {
56
57  Parm *p;
58  String *tm;
59
60  /* Emit function arguments */
61  Swig_cargs(f, l);
62
63  /* Attach typemaps to parameters */
64  /*  Swig_typemap_attach_parms("ignore",l,f); */
65
66  Swig_typemap_attach_parms("default", l, f);
67  Swig_typemap_attach_parms("arginit", l, f);
68
69  /* Apply the arginit and default */
70  p = l;
71  while (p) {
72    tm = Getattr(p, "tmap:arginit");
73    if (tm) {
74      Replace(tm, "$target", Getattr(p, "lname"), DOH_REPLACE_ANY);
75      Printv(f->code, tm, "\n", NIL);
76      p = Getattr(p, "tmap:arginit:next");
77    } else {
78      p = nextSibling(p);
79    }
80  }
81
82  /* Apply the default typemap */
83  p = l;
84  while (p) {
85    tm = Getattr(p, "tmap:default");
86    if (tm) {
87      Replace(tm, "$target", Getattr(p, "lname"), DOH_REPLACE_ANY);
88      Printv(f->code, tm, "\n", NIL);
89      p = Getattr(p, "tmap:default:next");
90    } else {
91      p = nextSibling(p);
92    }
93  }
94}
95
96/* -----------------------------------------------------------------------------
97 * emit_attach_parmmaps()
98 *
99 * Attach the standard parameter related typemaps.
100 * ----------------------------------------------------------------------------- */
101
102void emit_attach_parmmaps(ParmList *l, Wrapper *f) {
103  Swig_typemap_attach_parms("in", l, f);
104  Swig_typemap_attach_parms("typecheck", l, 0);
105  Swig_typemap_attach_parms("argout", l, f);
106  Swig_typemap_attach_parms("check", l, f);
107  Swig_typemap_attach_parms("freearg", l, f);
108
109  {
110    /* This is compatibility code to deal with the deprecated "ignore" typemap */
111    Parm *p = l;
112    Parm *np;
113    String *tm;
114    while (p) {
115      tm = Getattr(p, "tmap:in");
116      if (tm && checkAttribute(p, "tmap:in:numinputs", "0")) {
117	Replaceall(tm, "$target", Getattr(p, "lname"));
118	Printv(f->code, tm, "\n", NIL);
119	np = Getattr(p, "tmap:in:next");
120	while (p && (p != np)) {
121	  /*	  Setattr(p,"ignore","1");    Deprecate */
122	  p = nextSibling(p);
123	}
124      } else if (tm) {
125	p = Getattr(p, "tmap:in:next");
126      } else {
127	p = nextSibling(p);
128      }
129    }
130  }
131
132  /* Perform a sanity check on "in" and "freearg" typemaps.  These
133     must exactly match to avoid chaos.  If a mismatch occurs, we
134     nuke the freearg typemap */
135
136  {
137    Parm *p = l;
138    Parm *npin, *npfreearg;
139    while (p) {
140      npin = Getattr(p, "tmap:in:next");
141
142      /*
143         if (Getattr(p,"tmap:ignore")) {
144         npin = Getattr(p,"tmap:ignore:next");
145         } else if (Getattr(p,"tmap:in")) {
146         npin = Getattr(p,"tmap:in:next");
147         }
148       */
149
150      if (Getattr(p, "tmap:freearg")) {
151	npfreearg = Getattr(p, "tmap:freearg:next");
152	if (npin != npfreearg) {
153	  while (p != npin) {
154	    Delattr(p, "tmap:freearg");
155	    Delattr(p, "tmap:freearg:next");
156	    p = nextSibling(p);
157	  }
158	}
159      }
160      p = npin;
161    }
162  }
163
164  /* Check for variable length arguments with no input typemap.
165     If no input is defined, we set this to ignore and print a
166     message.
167   */
168  {
169    Parm *p = l;
170    Parm *lp = 0;
171    while (p) {
172      if (!checkAttribute(p, "tmap:in:numinputs", "0")) {
173	lp = p;
174	p = Getattr(p, "tmap:in:next");
175	continue;
176      }
177      if (SwigType_isvarargs(Getattr(p, "type"))) {
178	Swig_warning(WARN_LANG_VARARGS, input_file, line_number, "Variable length arguments discarded.\n");
179	Setattr(p, "tmap:in", "");
180      }
181      lp = 0;
182      p = nextSibling(p);
183    }
184
185    /* Check if last input argument is variable length argument */
186    if (lp) {
187      p = lp;
188      while (p) {
189	if (SwigType_isvarargs(Getattr(p, "type"))) {
190	  Setattr(l, "emit:varargs", lp);
191	  break;
192	}
193	p = nextSibling(p);
194      }
195    }
196  }
197}
198
199/* -----------------------------------------------------------------------------
200 * emit_num_arguments()
201 *
202 * Calculate the total number of arguments.   This function is safe for use
203 * with multi-argument typemaps which may change the number of arguments in
204 * strange ways.
205 * ----------------------------------------------------------------------------- */
206
207int emit_num_arguments(ParmList *parms) {
208  Parm *p = parms;
209  int nargs = 0;
210
211  while (p) {
212    if (Getattr(p, "tmap:in")) {
213      nargs += GetInt(p, "tmap:in:numinputs");
214      p = Getattr(p, "tmap:in:next");
215    } else {
216      p = nextSibling(p);
217    }
218  }
219
220  /* DB 04/02/2003: Not sure this is necessary with tmap:in:numinputs */
221  /*
222     if (parms && (p = Getattr(parms,"emit:varargs"))) {
223     if (!nextSibling(p)) {
224     nargs--;
225     }
226     }
227   */
228  return nargs;
229}
230
231/* -----------------------------------------------------------------------------
232 * emit_num_required()
233 *
234 * Computes the number of required arguments.  This function is safe for
235 * use with multi-argument typemaps and knows how to skip over everything
236 * properly. Note that parameters with default values are counted unless
237 * the compact default args option is on.
238 * ----------------------------------------------------------------------------- */
239
240int emit_num_required(ParmList *parms) {
241  Parm *p = parms;
242  int nargs = 0;
243  Parm *first_default_arg = 0;
244  int compactdefargs = ParmList_is_compactdefargs(p);
245
246  while (p) {
247    if (Getattr(p, "tmap:in") && checkAttribute(p, "tmap:in:numinputs", "0")) {
248      p = Getattr(p, "tmap:in:next");
249    } else {
250      if (Getattr(p, "tmap:default"))
251	break;
252      if (Getattr(p, "value")) {
253	if (!first_default_arg)
254	  first_default_arg = p;
255	if (compactdefargs)
256	  break;
257      }
258      nargs += GetInt(p, "tmap:in:numinputs");
259      if (Getattr(p, "tmap:in")) {
260	p = Getattr(p, "tmap:in:next");
261      } else {
262	p = nextSibling(p);
263      }
264    }
265  }
266
267  /* Print error message for non-default arguments following default arguments */
268  /* The error message is printed more than once with most language modules, this ought to be fixed */
269  if (first_default_arg) {
270    p = first_default_arg;
271    while (p) {
272      if (Getattr(p, "tmap:in") && checkAttribute(p, "tmap:in:numinputs", "0")) {
273	p = Getattr(p, "tmap:in:next");
274      } else {
275	if (!Getattr(p, "value") && (!Getattr(p, "tmap:default"))) {
276	  Swig_error(Getfile(p), Getline(p), "Non-optional argument '%s' follows an optional argument.\n", Getattr(p, "name"));
277	}
278	if (Getattr(p, "tmap:in")) {
279	  p = Getattr(p, "tmap:in:next");
280	} else {
281	  p = nextSibling(p);
282	}
283      }
284    }
285  }
286
287  /* DB 04/02/2003: Not sure this is necessary with tmap:in:numinputs */
288  /*
289     if (parms && (p = Getattr(parms,"emit:varargs"))) {
290     if (!nextSibling(p)) {
291     nargs--;
292     }
293     }
294   */
295  return nargs;
296}
297
298/* -----------------------------------------------------------------------------
299 * emit_isvarargs()
300 *
301 * Checks if a function is a varargs function
302 * ----------------------------------------------------------------------------- */
303
304int emit_isvarargs(ParmList *p) {
305  if (!p)
306    return 0;
307  if (Getattr(p, "emit:varargs"))
308    return 1;
309  return 0;
310}
311
312/* -----------------------------------------------------------------------------
313 * void emit_mark_vararg_parms()
314 *
315 * Marks the vararg parameters which are to be ignored.
316 * Vararg parameters are marked as ignored if there is no 'in' varargs (...)
317 * typemap.
318 * ----------------------------------------------------------------------------- */
319
320void emit_mark_varargs(ParmList *l) {
321  Parm *p = l;
322  while (p) {
323    if (SwigType_isvarargs(Getattr(p, "type")))
324      if (!Getattr(p, "tmap:in"))
325	Setattr(p, "varargs:ignore", "1");
326    p = nextSibling(p);
327  }
328}
329
330#if 0
331/* replace_contract_args.  This function replaces argument names in contract
332   specifications.   Used in conjunction with the %contract directive. */
333
334static void replace_contract_args(Parm *cp, Parm *rp, String *s) {
335  while (cp && rp) {
336    String *n = Getattr(cp, "name");
337    if (n) {
338      Replace(s, n, Getattr(rp, "lname"), DOH_REPLACE_ID);
339    }
340    cp = nextSibling(cp);
341    rp = nextSibling(rp);
342  }
343}
344#endif
345
346/* -----------------------------------------------------------------------------
347 * int emit_action_code()
348 *
349 * Emits action code for a wrapper. Adds in exception handling code (%exception).
350 * eaction -> the action code to emit
351 * wrappercode -> the emitted code (output)
352 * ----------------------------------------------------------------------------- */
353int emit_action_code(Node *n, String *wrappercode, String *eaction) {
354  assert(Getattr(n, "wrap:name"));
355
356  /* Look for except feature (%exception) */
357  String *tm = GetFlagAttr(n, "feature:except");
358  if (tm)
359    tm = Copy(tm);
360  if ((tm) && Len(tm) && (Strcmp(tm, "1") != 0)) {
361    if (Strstr(tm, "$")) {
362      Replaceall(tm, "$name", Getattr(n, "name"));
363      Replaceall(tm, "$symname", Getattr(n, "sym:name"));
364      Replaceall(tm, "$function", eaction); // deprecated
365      Replaceall(tm, "$action", eaction);
366      Replaceall(tm, "$wrapname", Getattr(n, "wrap:name"));
367      String *overloaded = Getattr(n, "sym:overloaded");
368      Replaceall(tm, "$overname", overloaded ? Char(Getattr(n, "sym:overname")) : "");
369
370      if (Strstr(tm, "$decl")) {
371        String *decl = Swig_name_decl(n);
372        Replaceall(tm, "$decl", decl);
373        Delete(decl);
374      }
375      if (Strstr(tm, "$fulldecl")) {
376        String *fulldecl = Swig_name_fulldecl(n);
377        Replaceall(tm, "$fulldecl", fulldecl);
378        Delete(fulldecl);
379      }
380    }
381    Printv(wrappercode, tm, "\n", NIL);
382    Delete(tm);
383    return 1;
384  } else {
385    Printv(wrappercode, eaction, "\n", NIL);
386    return 0;
387  }
388}
389
390/* -----------------------------------------------------------------------------
391 * int emit_action()
392 *
393 * Emits the call to the wrapped function.
394 * Adds in exception specification exception handling and %exception code.
395 * ----------------------------------------------------------------------------- */
396String *emit_action(Node *n) {
397  String *actioncode = NewStringEmpty();
398  String *tm;
399  String *action;
400  String *wrap;
401  SwigType *rt;
402  ParmList *catchlist = Getattr(n, "catchlist");
403
404  /* Look for fragments */
405  {
406    String *fragment = Getattr(n, "feature:fragment");
407    if (fragment) {
408      char *c, *tok;
409      String *t = Copy(fragment);
410      c = Char(t);
411      tok = strtok(c, ",");
412      while (tok) {
413	String *fname = NewString(tok);
414	Setfile(fname, Getfile(n));
415	Setline(fname, Getline(n));
416	Swig_fragment_emit(fname);
417	Delete(fname);
418	tok = strtok(NULL, ",");
419      }
420      Delete(t);
421    }
422  }
423
424  /* Emit wrapper code (if any) */
425  wrap = Getattr(n, "wrap:code");
426  if (wrap && Swig_filebyname("header") != Getattr(n, "wrap:code:done")) {
427    File *f_code = Swig_filebyname("header");
428    if (f_code) {
429      Printv(f_code, wrap, NIL);
430    }
431    Setattr(n, "wrap:code:done", f_code);
432  }
433
434  action = Getattr(n, "feature:action");
435  if (!action)
436    action = Getattr(n, "wrap:action");
437  assert(action != 0);
438
439  /* Get the return type */
440  rt = Getattr(n, "type");
441
442  /* Emit contract code (if any) */
443  if (Swig_contract_mode_get()) {
444    /* Preassertion */
445    tm = Getattr(n, "contract:preassert");
446    if (Len(tm)) {
447      Printv(actioncode, tm, "\n", NIL);
448    }
449  }
450  /* Exception handling code */
451
452  /* saves action -> eaction for postcatching exception */
453  String *eaction = NewString("");
454
455  /* If we are in C++ mode and there is an exception specification. We're going to
456     enclose the block in a try block */
457  if (catchlist) {
458    Printf(eaction, "try {\n");
459  }
460
461  Printv(eaction, action, NIL);
462
463  if (catchlist) {
464    int unknown_catch = 0;
465    Printf(eaction, "}\n");
466    for (Parm *ep = catchlist; ep; ep = nextSibling(ep)) {
467      String *em = Swig_typemap_lookup("throws", ep, "_e", 0);
468      if (em) {
469	SwigType *et = Getattr(ep, "type");
470	SwigType *etr = SwigType_typedef_resolve_all(et);
471	if (SwigType_isreference(etr) || SwigType_ispointer(etr) || SwigType_isarray(etr)) {
472	  Printf(eaction, "catch(%s) {", SwigType_str(et, "_e"));
473	} else if (SwigType_isvarargs(etr)) {
474	  Printf(eaction, "catch(...) {");
475	} else {
476	  Printf(eaction, "catch(%s) {", SwigType_str(et, "&_e"));
477	}
478	Printv(eaction, em, "\n", NIL);
479	Printf(eaction, "}\n");
480      } else {
481	Swig_warning(WARN_TYPEMAP_THROW, Getfile(n), Getline(n), "No 'throws' typemap defined for exception type '%s'\n", SwigType_str(Getattr(ep, "type"), 0));
482	unknown_catch = 1;
483      }
484    }
485    if (unknown_catch) {
486      Printf(eaction, "catch(...) { throw; }\n");
487    }
488  }
489
490  /* Look for except typemap (Deprecated) */
491  tm = Swig_typemap_lookup("except", n, "result", 0);
492  if (tm) {
493    Setattr(n, "feature:except", tm);
494    tm = 0;
495  }
496
497  /* emit the except feature code */
498  emit_action_code(n, actioncode, eaction);
499
500  Delete(eaction);
501
502  /* Emit contract code (if any) */
503  if (Swig_contract_mode_get()) {
504    /* Postassertion */
505    tm = Getattr(n, "contract:postassert");
506    if (Len(tm)) {
507      Printv(actioncode, tm, "\n", NIL);
508    }
509  }
510
511  return actioncode;
512}
513