summaryrefslogtreecommitdiff
path: root/Source/Modules/contract.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Modules/contract.cxx')
-rw-r--r--Source/Modules/contract.cxx354
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;
+}