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 * contract.cxx
6 *
7 * Support for Wrap by Contract in SWIG.
8 * ----------------------------------------------------------------------------- */
9
10char cvsroot_contract_cxx[] = "$Id: contract.cxx 11049 2009-01-10 01:15:03Z wsfulton $";
11
12#include "swigmod.h"
13
14/* Contract structure.  This holds rules about the different kinds of contract sections
15   and their combination rules */
16
17struct contract {
18  const char *section;
19  const char *combiner;
20};
21/* Contract rules.  This table defines what contract sections are recognized as well as
22   how contracts are to combined via inheritance */
23
24static contract Rules[] = {
25  {"require:", "&&"},
26  {"ensure:", "||"},
27  {NULL, NULL}
28};
29
30/* ----------------------------------------------------------------------------
31 * class Contracts:
32 *
33 * This class defines the functions that need to be used in
34 *         "wrap by contract" module.
35 * ------------------------------------------------------------------------- */
36
37class Contracts:public Dispatcher {
38  String *make_expression(String *s, Node *n);
39  void substitute_parms(String *s, ParmList *p, int method);
40public:
41  Hash *ContractSplit(Node *n);
42  int emit_contract(Node *n, int method);
43  int cDeclaration(Node *n);
44  int constructorDeclaration(Node *n);
45  int externDeclaration(Node *n);
46  int extendDirective(Node *n);
47  int importDirective(Node *n);
48  int includeDirective(Node *n);
49  int namespaceDeclaration(Node *n);
50  int classDeclaration(Node *n);
51  virtual int top(Node *n);
52};
53
54static int Contract_Mode = 0;	/* contract option */
55static int InClass = 0;		/* Parsing C++ or not */
56static int InConstructor = 0;
57static Node *CurrentClass = 0;
58
59/* Set the contract mode, default is 0 (not open) */
60/* Normally set in main.cxx, when get the "-contracts" option */
61void Swig_contract_mode_set(int flag) {
62  Contract_Mode = flag;
63}
64
65/* Get the contract mode */
66int Swig_contract_mode_get() {
67  return Contract_Mode;
68}
69
70/* Apply contracts */
71void Swig_contracts(Node *n) {
72
73  Contracts *a = new Contracts;
74  a->top(n);
75  delete a;
76}
77
78/* Split the whole contract into preassertion, postassertion and others */
79Hash *Contracts::ContractSplit(Node *n) {
80
81  String *contract = Getattr(n, "feature:contract");
82  Hash *result;
83  if (!contract)
84    return NULL;
85
86  result = NewHash();
87  String *current_section = NewString("");
88  const char *current_section_name = Rules[0].section;
89  List *l = SplitLines(contract);
90
91  Iterator i;
92  for (i = First(l); i.item; i = Next(i)) {
93    int found = 0;
94    if (Strchr(i.item, '{'))
95      continue;
96    if (Strchr(i.item, '}'))
97      continue;
98    for (int j = 0; Rules[j].section; j++) {
99      if (Strstr(i.item, Rules[j].section)) {
100	if (Len(current_section)) {
101	  Setattr(result, current_section_name, current_section);
102	  current_section = Getattr(result, Rules[j].section);
103	  if (!current_section)
104	    current_section = NewString("");
105	}
106	current_section_name = Rules[j].section;
107	found = 1;
108	break;
109      }
110    }
111    if (!found)
112      Append(current_section, i.item);
113  }
114  if (Len(current_section))
115    Setattr(result, current_section_name, current_section);
116  return result;
117}
118
119/* This function looks in base classes and collects contracts found */
120void inherit_contracts(Node *c, Node *n, Hash *contracts, Hash *messages) {
121
122  Node *b, *temp;
123  String *name, *type, *local_decl, *base_decl;
124  List *bases;
125  int found = 0;
126
127  bases = Getattr(c, "bases");
128  if (!bases)
129    return;
130
131  name = Getattr(n, "name");
132  type = Getattr(n, "type");
133  local_decl = Getattr(n, "decl");
134  if (local_decl) {
135    local_decl = SwigType_typedef_resolve_all(local_decl);
136  } else {
137    return;
138  }
139  /* Width first search */
140  for (int i = 0; i < Len(bases); i++) {
141    b = Getitem(bases, i);
142    temp = firstChild(b);
143    while (temp) {
144      base_decl = Getattr(temp, "decl");
145      if (base_decl) {
146	base_decl = SwigType_typedef_resolve_all(base_decl);
147	if ((checkAttribute(temp, "storage", "virtual")) &&
148	    (checkAttribute(temp, "name", name)) && (checkAttribute(temp, "type", type)) && (!Strcmp(local_decl, base_decl))) {
149	  /* Yes, match found. */
150	  Hash *icontracts = Getattr(temp, "contract:rules");
151	  Hash *imessages = Getattr(temp, "contract:messages");
152	  found = 1;
153	  if (icontracts && imessages) {
154	    /* Add inherited contracts and messages to the contract rules above */
155	    int j = 0;
156	    for (j = 0; Rules[j].section; j++) {
157	      String *t = Getattr(contracts, Rules[j].section);
158	      String *s = Getattr(icontracts, Rules[j].section);
159	      if (s) {
160		if (t) {
161		  Insert(t, 0, "(");
162		  Printf(t, ") %s (%s)", Rules[j].combiner, s);
163		  String *m = Getattr(messages, Rules[j].section);
164		  Printf(m, " %s [%s from %s]", Rules[j].combiner, Getattr(imessages, Rules[j].section), Getattr(b, "name"));
165		} else {
166		  Setattr(contracts, Rules[j].section, NewString(s));
167		  Setattr(messages, Rules[j].section, NewStringf("[%s from %s]", Getattr(imessages, Rules[j].section), Getattr(b, "name")));
168		}
169	      }
170	    }
171	  }
172	}
173	Delete(base_decl);
174      }
175      temp = nextSibling(temp);
176    }
177  }
178  Delete(local_decl);
179  if (!found) {
180    for (int j = 0; j < Len(bases); j++) {
181      b = Getitem(bases, j);
182      inherit_contracts(b, n, contracts, messages);
183    }
184  }
185}
186
187/* This function cleans up the assertion string by removing some extraneous characters.
188   Splitting the assertion into pieces */
189
190String *Contracts::make_expression(String *s, Node *n) {
191  String *str_assert, *expr = 0;
192  List *list_assert;
193
194  str_assert = NewString(s);
195  /* Omit all useless characters and split by ; */
196  Replaceall(str_assert, "\n", "");
197  Replaceall(str_assert, "{", "");
198  Replaceall(str_assert, "}", "");
199  Replace(str_assert, " ", "", DOH_REPLACE_ANY | DOH_REPLACE_NOQUOTE);
200  Replace(str_assert, "\t", "", DOH_REPLACE_ANY | DOH_REPLACE_NOQUOTE);
201
202  list_assert = Split(str_assert, ';', -1);
203  Delete(str_assert);
204
205  /* build up new assertion */
206  str_assert = NewString("");
207  Iterator ei;
208
209  for (ei = First(list_assert); ei.item; ei = Next(ei)) {
210    expr = ei.item;
211    if (Len(expr)) {
212      Replaceid(expr, Getattr(n, "name"), "result");
213      if (Len(str_assert))
214	Append(str_assert, "&&");
215      Printf(str_assert, "(%s)", expr);
216    }
217  }
218  Delete(list_assert);
219  return str_assert;
220}
221
222/* This function substitutes parameter names for argument names in the
223   contract specification.  Note: it is assumed that the wrapper code
224   uses arg1 for self and arg2..argn for arguments. */
225
226void Contracts::substitute_parms(String *s, ParmList *p, int method) {
227  int argnum = 1;
228  char argname[32];
229
230  if (method) {
231    Replaceid(s, "$self", "arg1");
232    argnum++;
233  }
234  while (p) {
235    sprintf(argname, "arg%d", argnum);
236    String *name = Getattr(p, "name");
237    if (name) {
238      Replaceid(s, name, argname);
239    }
240    argnum++;
241    p = nextSibling(p);
242  }
243}
244
245int Contracts::emit_contract(Node *n, int method) {
246  Hash *contracts;
247  Hash *messages;
248  String *c;
249
250  ParmList *cparms;
251
252  if (!Getattr(n, "feature:contract"))
253    return SWIG_ERROR;
254
255  /* Get contract parameters */
256  cparms = Getmeta(Getattr(n, "feature:contract"), "parms");
257
258  /*  Split contract into preassert & postassert */
259  contracts = ContractSplit(n);
260  if (!contracts)
261    return SWIG_ERROR;
262
263  /* This messages hash is used to hold the error messages that will be displayed on
264     failed contract. */
265
266  messages = NewHash();
267
268  /* Take the different contract expressions and clean them up a bit */
269  Iterator i;
270  for (i = First(contracts); i.item; i = Next(i)) {
271    String *e = make_expression(i.item, n);
272    substitute_parms(e, cparms, method);
273    Setattr(contracts, i.key, e);
274
275    /* Make a string containing error messages */
276    Setattr(messages, i.key, NewString(e));
277  }
278
279  /* If we're in a class. We need to inherit other assertions. */
280  if (InClass) {
281    inherit_contracts(CurrentClass, n, contracts, messages);
282  }
283
284  /* Save information */
285  Setattr(n, "contract:rules", contracts);
286  Setattr(n, "contract:messages", messages);
287
288  /* Okay.  Generate the contract runtime code. */
289
290  if ((c = Getattr(contracts, "require:"))) {
291    Setattr(n, "contract:preassert", NewStringf("SWIG_contract_assert(%s, \"Contract violation: require: %s\");\n", c, Getattr(messages, "require:")));
292  }
293  if ((c = Getattr(contracts, "ensure:"))) {
294    Setattr(n, "contract:postassert", NewStringf("SWIG_contract_assert(%s, \"Contract violation: ensure: %s\");\n", c, Getattr(messages, "ensure:")));
295  }
296  return SWIG_OK;
297}
298
299int Contracts::cDeclaration(Node *n) {
300  int ret = SWIG_OK;
301  String *decl = Getattr(n, "decl");
302
303  /* Not a function.  Don't even bother with it (for now) */
304  if (!SwigType_isfunction(decl))
305    return SWIG_OK;
306
307  if (Getattr(n, "feature:contract"))
308    ret = emit_contract(n, (InClass && !checkAttribute(n, "storage", "static")));
309  return ret;
310}
311
312int Contracts::constructorDeclaration(Node *n) {
313  int ret = SWIG_OK;
314  InConstructor = 1;
315  if (Getattr(n, "feature:contract"))
316    ret = emit_contract(n, 0);
317  InConstructor = 0;
318  return ret;
319}
320
321int Contracts::externDeclaration(Node *n) {
322  return emit_children(n);
323}
324
325int Contracts::extendDirective(Node *n) {
326  return emit_children(n);
327}
328
329int Contracts::importDirective(Node *n) {
330  return emit_children(n);
331}
332
333int Contracts::includeDirective(Node *n) {
334  return emit_children(n);
335}
336
337int Contracts::namespaceDeclaration(Node *n) {
338  return emit_children(n);
339}
340
341int Contracts::classDeclaration(Node *n) {
342  int ret = SWIG_OK;
343  InClass = 1;
344  CurrentClass = n;
345  emit_children(n);
346  InClass = 0;
347  CurrentClass = 0;
348  return ret;
349}
350
351int Contracts::top(Node *n) {
352  emit_children(n);
353  return SWIG_OK;
354}
355