Upstream version 1.3.40
[profile/ivi/swig.git] / Source / Modules / contract.cxx
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
10 char 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
17 struct 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
24 static 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
37 class Contracts:public Dispatcher {
38   String *make_expression(String *s, Node *n);
39   void substitute_parms(String *s, ParmList *p, int method);
40 public:
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
54 static int Contract_Mode = 0;   /* contract option */
55 static int InClass = 0;         /* Parsing C++ or not */
56 static int InConstructor = 0;
57 static 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 */
61 void Swig_contract_mode_set(int flag) {
62   Contract_Mode = flag;
63 }
64
65 /* Get the contract mode */
66 int Swig_contract_mode_get() {
67   return Contract_Mode;
68 }
69
70 /* Apply contracts */
71 void 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 */
79 Hash *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 */
120 void 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
190 String *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
226 void 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
245 int 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
299 int 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
312 int 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
321 int Contracts::externDeclaration(Node *n) {
322   return emit_children(n);
323 }
324
325 int Contracts::extendDirective(Node *n) {
326   return emit_children(n);
327 }
328
329 int Contracts::importDirective(Node *n) {
330   return emit_children(n);
331 }
332
333 int Contracts::includeDirective(Node *n) {
334   return emit_children(n);
335 }
336
337 int Contracts::namespaceDeclaration(Node *n) {
338   return emit_children(n);
339 }
340
341 int 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
351 int Contracts::top(Node *n) {
352   emit_children(n);
353   return SWIG_OK;
354 }