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