mirror of https://github.com/swig/swig
342 lines
9.6 KiB
C++
342 lines
9.6 KiB
C++
/* -----------------------------------------------------------------------------
|
|
* contract.cxx
|
|
*
|
|
* Support for Wrap by Contract in SWIG
|
|
*
|
|
* Author(s) : Songyan Feng (Tiger) (songyanf@cs.uchicago.edu)
|
|
* David Beazley (beazley@cs.uchicago.edu)
|
|
*
|
|
* Department of Computer Science
|
|
* University of Chicago
|
|
*
|
|
* Copyright (C) 1999-2003. The University of Chicago
|
|
* See the file LICENSE for information on usage and redistribution.
|
|
* ----------------------------------------------------------------------------- */
|
|
|
|
char cvsroot_contract_cxx[] = "$Header$";
|
|
|
|
#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 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 (Strstr(i.item,"{")) continue;
|
|
if (Strstr(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--argn for arguments. */
|
|
|
|
void Contracts::substitute_parms(String *s, ParmList *p, int method) {
|
|
int argnum = 1;
|
|
char argname[32];
|
|
|
|
if (method) {
|
|
Replaceid(s,"self","arg0");
|
|
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::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;
|
|
}
|