PageRenderTime 53ms CodeModel.GetById 34ms app.highlight 17ms RepoModel.GetById 0ms app.codeStats 0ms

/tags/rel-1-3-24/SWIG/Source/Modules/contract.cxx

#
C++ | 341 lines | 243 code | 48 blank | 50 comment | 41 complexity | 86307c392efa04101e8d85114272b144 MD5 | raw file
Possible License(s): LGPL-2.1, Cube, GPL-3.0, 0BSD, GPL-2.0
  1/* ----------------------------------------------------------------------------- 
  2 * contract.cxx
  3 *
  4 *     Support for Wrap by Contract in SWIG
  5 * 
  6 * Author(s) : Songyan Feng (Tiger) (songyanf@cs.uchicago.edu)
  7 *             David Beazley (beazley@cs.uchicago.edu)
  8 *
  9 * Department of Computer Science
 10 * University of Chicago
 11 *
 12 * Copyright (C) 1999-2003.  The University of Chicago
 13 * See the file LICENSE for information on usage and redistribution.	
 14 * ----------------------------------------------------------------------------- */
 15
 16char cvsroot_contract_cxx[] = "$Header$";
 17
 18#include "swigmod.h"
 19
 20/* Contract structure.  This holds rules about the different kinds of contract sections
 21   and their combination rules */
 22
 23struct contract {
 24  const char *section;
 25  const char *combiner;
 26};
 27/* Contract rules.  This table defines what contract sections are recognized as well as
 28   how contracts are to combined via inheritance */
 29
 30static contract Rules[] = {
 31  {"require:", "&&"},
 32  {"ensure:",  "||"},
 33  { NULL, NULL}
 34};
 35
 36/************************************************************************
 37 * class Contracts:
 38 *
 39 * This class defines the functions that need to be used in 
 40 *         "wrap by contract" module.
 41 *************************************************************************/
 42
 43class Contracts : public Dispatcher {
 44  String *make_expression(String *s, Node *n);
 45  void    substitute_parms(String *s, ParmList *p, int method);
 46public:
 47  Hash *ContractSplit(Node *n);
 48  int emit_contract(Node *n, int method);
 49  int cDeclaration(Node *n);
 50  int constructorDeclaration(Node  *n);
 51  int externDeclaration(Node *n);
 52  int extendDirective(Node *n);
 53  int importDirective(Node *n);
 54  int includeDirective(Node *n);
 55  int classDeclaration(Node *n);
 56  virtual int top(Node *n);
 57};
 58
 59static int      Contract_Mode = 0;    /* contract option */
 60static int      InClass       = 0;    /* Parsing C++ or not */
 61static int      InConstructor = 0;
 62static Node    *CurrentClass  = 0;
 63
 64/* Set the contract mode, default is 0 (not open) */
 65/* Normally set in main.cxx, when get the "-contracts" option */
 66void Swig_contract_mode_set(int flag) {
 67  Contract_Mode = flag;
 68}
 69/* Get the contract mode */
 70int Swig_contract_mode_get() {
 71  return Contract_Mode;
 72}
 73
 74/* Apply contracts */
 75void Swig_contracts(Node *n) {
 76
 77  Contracts *a = new Contracts;
 78  a->top(n);
 79  delete a;
 80}
 81
 82/* Split the whole contract into preassertion, postassertion and others */
 83Hash *Contracts::ContractSplit(Node *n) {
 84
 85  String *contract   = Getattr(n, "feature:contract");  
 86  Hash   *result;
 87  if (!contract)
 88    return NULL;
 89
 90  result = NewHash();
 91  String *current_section = NewString("");
 92  const char   *current_section_name = Rules[0].section;
 93  List *l = SplitLines(contract);
 94
 95  Iterator i;
 96  for (i = First(l); i.item; i = Next(i)) {
 97    int found = 0;
 98    if (Strstr(i.item,"{")) continue;
 99    if (Strstr(i.item,"}")) continue;
100    for (int j = 0; Rules[j].section; j++) {
101      if (Strstr(i.item,Rules[j].section)) {
102	if (Len(current_section)) {
103	  Setattr(result,current_section_name,current_section);
104	  current_section = Getattr(result,Rules[j].section);
105	  if (!current_section) current_section = NewString("");
106	} 
107	current_section_name = Rules[j].section;
108	found = 1;
109	break;
110      }
111    }
112    if (!found) Append(current_section, i.item);
113  }
114  if (Len(current_section)) Setattr(result, current_section_name, current_section);
115  return result;
116}
117
118/* This function looks in base classes and collects contracts found */
119void inherit_contracts(Node *c, Node *n, Hash *contracts, Hash *messages) {
120  
121  Node *b, *temp;
122  String *name, *type, *local_decl, *base_decl;
123  List   *bases;
124  int     found = 0;
125
126  bases = Getattr(c,"bases");
127  if (!bases) return;
128    
129  name = Getattr(n, "name");
130  type = Getattr(n, "type");
131  local_decl = Getattr(n, "decl");
132  if (local_decl) {
133    local_decl = SwigType_typedef_resolve_all(local_decl);
134  } else {
135    return;
136  }
137  /* Width first search */
138  for (int i = 0; i < Len(bases); i++) {
139    b = Getitem(bases,i);
140    temp = firstChild (b);
141    while (temp) {
142      base_decl = Getattr(temp, "decl");
143      if (base_decl) {
144	base_decl = SwigType_typedef_resolve_all(base_decl);
145	if ( (checkAttribute(temp, "storage", "virtual")) &&
146	     (checkAttribute(temp, "name", name)) &&
147	     (checkAttribute(temp, "type", type)) &&
148	     (!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--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","arg0");
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) return SWIG_ERROR;
261
262  /* This messages hash is used to hold the error messages that will be displayed on
263     failed contract. */
264
265  messages = NewHash();
266
267  /* Take the different contract expressions and clean them up a bit */
268  Iterator i;
269  for (i = First(contracts); i.item; i = Next(i)) {
270    String *e = make_expression(i.item, n);
271    substitute_parms(e, cparms, method);
272    Setattr(contracts,i.key,e);
273
274    /* Make a string containing error messages */
275    Setattr(messages,i.key, NewString(e));
276  }
277   
278  /* If we're in a class. We need to inherit other assertions. */
279  if (InClass) {
280    inherit_contracts(CurrentClass, n, contracts, messages);
281  }
282
283  /* Save information */
284  Setattr(n,"contract:rules", contracts);
285  Setattr(n,"contract:messages", messages);
286
287  /* Okay.  Generate the contract runtime code. */
288  
289  if ((c = Getattr(contracts,"require:"))) {
290    Setattr(n,"contract:preassert",
291	    NewStringf("SWIG_contract_assert(%s, \"Contract violation: require: %s\");\n",
292		       c, Getattr(messages,"require:")));
293  }
294  if ((c = Getattr(contracts,"ensure:"))) {
295    Setattr(n,"contract:postassert",
296	    NewStringf("SWIG_contract_assert(%s, \"Contract violation: ensure: %s\");\n",
297		       c, Getattr(messages,"ensure:")));
298  }
299  return SWIG_OK;
300}
301
302int Contracts::cDeclaration(Node *n) {
303  int ret = SWIG_OK;
304  String *decl = Getattr(n,"decl");
305
306  /* Not a function.  Don't even bother with it (for now) */
307  if (!SwigType_isfunction(decl)) return SWIG_OK;
308
309  if (Getattr(n, "feature:contract"))
310    ret = emit_contract(n, (InClass && !checkAttribute(n,"storage","static")));
311  return ret;
312}
313
314int Contracts::constructorDeclaration(Node  *n){
315  int ret = SWIG_OK;
316  InConstructor = 1;
317  if (Getattr(n, "feature:contract"))
318    ret = emit_contract(n,0);
319  InConstructor = 0;
320  return ret;
321}
322
323int Contracts::externDeclaration(Node *n) { return emit_children(n); }
324int Contracts::extendDirective(Node *n)   { return emit_children(n); }
325int Contracts::importDirective(Node *n)   { return emit_children(n); }
326int Contracts::includeDirective(Node *n)  { return emit_children(n); }
327
328int Contracts::classDeclaration(Node *n) {
329  int ret = SWIG_OK;
330  InClass = 1;
331  CurrentClass = n; 
332  emit_children(n);
333  InClass = 0;
334  CurrentClass = 0;
335  return ret;
336}
337
338int Contracts::top(Node *n) {
339  emit_children(n);
340  return SWIG_OK;
341}