diff options
Diffstat (limited to 'Source/Modules/contract.cxx')
-rw-r--r-- | Source/Modules/contract.cxx | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/Source/Modules/contract.cxx b/Source/Modules/contract.cxx new file mode 100644 index 0000000..89e3150 --- /dev/null +++ b/Source/Modules/contract.cxx @@ -0,0 +1,354 @@ +/* ----------------------------------------------------------------------------- + * See the LICENSE file for information on copyright, usage and redistribution + * of SWIG, and the README file for authors - http://www.swig.org/release.html. + * + * contract.cxx + * + * Support for Wrap by Contract in SWIG. + * ----------------------------------------------------------------------------- */ + +char cvsroot_contract_cxx[] = "$Id: contract.cxx 11049 2009-01-10 01:15:03Z wsfulton $"; + +#include "swigmod.h" + +/* Contract structure. This holds rules about the different kinds of contract sections + and their combination rules */ + +struct contract { + const char *section; + const char *combiner; +}; +/* Contract rules. This table defines what contract sections are recognized as well as + how contracts are to combined via inheritance */ + +static contract Rules[] = { + {"require:", "&&"}, + {"ensure:", "||"}, + {NULL, NULL} +}; + +/* ---------------------------------------------------------------------------- + * class Contracts: + * + * This class defines the functions that need to be used in + * "wrap by contract" module. + * ------------------------------------------------------------------------- */ + +class Contracts:public Dispatcher { + String *make_expression(String *s, Node *n); + void substitute_parms(String *s, ParmList *p, int method); +public: + Hash *ContractSplit(Node *n); + int emit_contract(Node *n, int method); + int cDeclaration(Node *n); + int constructorDeclaration(Node *n); + int externDeclaration(Node *n); + int extendDirective(Node *n); + int importDirective(Node *n); + int includeDirective(Node *n); + int namespaceDeclaration(Node *n); + int classDeclaration(Node *n); + virtual int top(Node *n); +}; + +static int Contract_Mode = 0; /* contract option */ +static int InClass = 0; /* Parsing C++ or not */ +static int InConstructor = 0; +static Node *CurrentClass = 0; + +/* Set the contract mode, default is 0 (not open) */ +/* Normally set in main.cxx, when get the "-contracts" option */ +void Swig_contract_mode_set(int flag) { + Contract_Mode = flag; +} + +/* Get the contract mode */ +int Swig_contract_mode_get() { + return Contract_Mode; +} + +/* Apply contracts */ +void Swig_contracts(Node *n) { + + Contracts *a = new Contracts; + a->top(n); + delete a; +} + +/* Split the whole contract into preassertion, postassertion and others */ +Hash *Contracts::ContractSplit(Node *n) { + + String *contract = Getattr(n, "feature:contract"); + Hash *result; + if (!contract) + return NULL; + + result = NewHash(); + String *current_section = NewString(""); + const char *current_section_name = Rules[0].section; + List *l = SplitLines(contract); + + Iterator i; + for (i = First(l); i.item; i = Next(i)) { + int found = 0; + if (Strchr(i.item, '{')) + continue; + if (Strchr(i.item, '}')) + continue; + for (int j = 0; Rules[j].section; j++) { + if (Strstr(i.item, Rules[j].section)) { + if (Len(current_section)) { + Setattr(result, current_section_name, current_section); + current_section = Getattr(result, Rules[j].section); + if (!current_section) + current_section = NewString(""); + } + current_section_name = Rules[j].section; + found = 1; + break; + } + } + if (!found) + Append(current_section, i.item); + } + if (Len(current_section)) + Setattr(result, current_section_name, current_section); + return result; +} + +/* This function looks in base classes and collects contracts found */ +void inherit_contracts(Node *c, Node *n, Hash *contracts, Hash *messages) { + + Node *b, *temp; + String *name, *type, *local_decl, *base_decl; + List *bases; + int found = 0; + + bases = Getattr(c, "bases"); + if (!bases) + return; + + name = Getattr(n, "name"); + type = Getattr(n, "type"); + local_decl = Getattr(n, "decl"); + if (local_decl) { + local_decl = SwigType_typedef_resolve_all(local_decl); + } else { + return; + } + /* Width first search */ + for (int i = 0; i < Len(bases); i++) { + b = Getitem(bases, i); + temp = firstChild(b); + while (temp) { + base_decl = Getattr(temp, "decl"); + if (base_decl) { + base_decl = SwigType_typedef_resolve_all(base_decl); + if ((checkAttribute(temp, "storage", "virtual")) && + (checkAttribute(temp, "name", name)) && (checkAttribute(temp, "type", type)) && (!Strcmp(local_decl, base_decl))) { + /* Yes, match found. */ + Hash *icontracts = Getattr(temp, "contract:rules"); + Hash *imessages = Getattr(temp, "contract:messages"); + found = 1; + if (icontracts && imessages) { + /* Add inherited contracts and messages to the contract rules above */ + int j = 0; + for (j = 0; Rules[j].section; j++) { + String *t = Getattr(contracts, Rules[j].section); + String *s = Getattr(icontracts, Rules[j].section); + if (s) { + if (t) { + Insert(t, 0, "("); + Printf(t, ") %s (%s)", Rules[j].combiner, s); + String *m = Getattr(messages, Rules[j].section); + Printf(m, " %s [%s from %s]", Rules[j].combiner, Getattr(imessages, Rules[j].section), Getattr(b, "name")); + } else { + Setattr(contracts, Rules[j].section, NewString(s)); + Setattr(messages, Rules[j].section, NewStringf("[%s from %s]", Getattr(imessages, Rules[j].section), Getattr(b, "name"))); + } + } + } + } + } + Delete(base_decl); + } + temp = nextSibling(temp); + } + } + Delete(local_decl); + if (!found) { + for (int j = 0; j < Len(bases); j++) { + b = Getitem(bases, j); + inherit_contracts(b, n, contracts, messages); + } + } +} + +/* This function cleans up the assertion string by removing some extraneous characters. + Splitting the assertion into pieces */ + +String *Contracts::make_expression(String *s, Node *n) { + String *str_assert, *expr = 0; + List *list_assert; + + str_assert = NewString(s); + /* Omit all useless characters and split by ; */ + Replaceall(str_assert, "\n", ""); + Replaceall(str_assert, "{", ""); + Replaceall(str_assert, "}", ""); + Replace(str_assert, " ", "", DOH_REPLACE_ANY | DOH_REPLACE_NOQUOTE); + Replace(str_assert, "\t", "", DOH_REPLACE_ANY | DOH_REPLACE_NOQUOTE); + + list_assert = Split(str_assert, ';', -1); + Delete(str_assert); + + /* build up new assertion */ + str_assert = NewString(""); + Iterator ei; + + for (ei = First(list_assert); ei.item; ei = Next(ei)) { + expr = ei.item; + if (Len(expr)) { + Replaceid(expr, Getattr(n, "name"), "result"); + if (Len(str_assert)) + Append(str_assert, "&&"); + Printf(str_assert, "(%s)", expr); + } + } + Delete(list_assert); + return str_assert; +} + +/* This function substitutes parameter names for argument names in the + contract specification. Note: it is assumed that the wrapper code + uses arg1 for self and arg2..argn for arguments. */ + +void Contracts::substitute_parms(String *s, ParmList *p, int method) { + int argnum = 1; + char argname[32]; + + if (method) { + Replaceid(s, "$self", "arg1"); + argnum++; + } + while (p) { + sprintf(argname, "arg%d", argnum); + String *name = Getattr(p, "name"); + if (name) { + Replaceid(s, name, argname); + } + argnum++; + p = nextSibling(p); + } +} + +int Contracts::emit_contract(Node *n, int method) { + Hash *contracts; + Hash *messages; + String *c; + + ParmList *cparms; + + if (!Getattr(n, "feature:contract")) + return SWIG_ERROR; + + /* Get contract parameters */ + cparms = Getmeta(Getattr(n, "feature:contract"), "parms"); + + /* Split contract into preassert & postassert */ + contracts = ContractSplit(n); + if (!contracts) + return SWIG_ERROR; + + /* This messages hash is used to hold the error messages that will be displayed on + failed contract. */ + + messages = NewHash(); + + /* Take the different contract expressions and clean them up a bit */ + Iterator i; + for (i = First(contracts); i.item; i = Next(i)) { + String *e = make_expression(i.item, n); + substitute_parms(e, cparms, method); + Setattr(contracts, i.key, e); + + /* Make a string containing error messages */ + Setattr(messages, i.key, NewString(e)); + } + + /* If we're in a class. We need to inherit other assertions. */ + if (InClass) { + inherit_contracts(CurrentClass, n, contracts, messages); + } + + /* Save information */ + Setattr(n, "contract:rules", contracts); + Setattr(n, "contract:messages", messages); + + /* Okay. Generate the contract runtime code. */ + + if ((c = Getattr(contracts, "require:"))) { + Setattr(n, "contract:preassert", NewStringf("SWIG_contract_assert(%s, \"Contract violation: require: %s\");\n", c, Getattr(messages, "require:"))); + } + if ((c = Getattr(contracts, "ensure:"))) { + Setattr(n, "contract:postassert", NewStringf("SWIG_contract_assert(%s, \"Contract violation: ensure: %s\");\n", c, Getattr(messages, "ensure:"))); + } + return SWIG_OK; +} + +int Contracts::cDeclaration(Node *n) { + int ret = SWIG_OK; + String *decl = Getattr(n, "decl"); + + /* Not a function. Don't even bother with it (for now) */ + if (!SwigType_isfunction(decl)) + return SWIG_OK; + + if (Getattr(n, "feature:contract")) + ret = emit_contract(n, (InClass && !checkAttribute(n, "storage", "static"))); + return ret; +} + +int Contracts::constructorDeclaration(Node *n) { + int ret = SWIG_OK; + InConstructor = 1; + if (Getattr(n, "feature:contract")) + ret = emit_contract(n, 0); + InConstructor = 0; + return ret; +} + +int Contracts::externDeclaration(Node *n) { + return emit_children(n); +} + +int Contracts::extendDirective(Node *n) { + return emit_children(n); +} + +int Contracts::importDirective(Node *n) { + return emit_children(n); +} + +int Contracts::includeDirective(Node *n) { + return emit_children(n); +} + +int Contracts::namespaceDeclaration(Node *n) { + return emit_children(n); +} + +int Contracts::classDeclaration(Node *n) { + int ret = SWIG_OK; + InClass = 1; + CurrentClass = n; + emit_children(n); + InClass = 0; + CurrentClass = 0; + return ret; +} + +int Contracts::top(Node *n) { + emit_children(n); + return SWIG_OK; +} |