| /* ----------------------------------------------------------------------------- |
| * This file is part of SWIG, which is licensed as a whole under version 3 |
| * (or any later version) of the GNU General Public License. Some additional |
| * terms also apply to certain portions of SWIG. The full details of the SWIG |
| * license and copyrights can be found in the LICENSE and COPYRIGHT files |
| * included with the SWIG source code as distributed by the SWIG developers |
| * and at http://www.swig.org/legal.html. |
| * |
| * contract.cxx |
| * |
| * Support for Wrap by Contract in SWIG. |
| * ----------------------------------------------------------------------------- */ |
| |
| #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"), Swig_cresult_name()); |
| 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 && !Swig_storage_isstatic(n)); |
| 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; |
| int oldInClass = InClass; |
| Node *oldClass = CurrentClass; |
| InClass = 1; |
| CurrentClass = n; |
| emit_children(n); |
| InClass = oldInClass; |
| CurrentClass = oldClass; |
| return ret; |
| } |
| |
| int Contracts::top(Node *n) { |
| emit_children(n); |
| return SWIG_OK; |
| } |