/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}