including assertions in net assemblies (dot net developers journal)
TRANSCRIPT
-
7/28/2019 Including Assertions in NET Assemblies (Dot Net Developers Journal)
1/738September 2003 dotnetdevelopersjournal.co
ET assemblies are superi-or to previous compo-
nent technologies
because they seamlessly
provide multilingual support; intro-
duce an excellent synergy between
code and metadata; and because of
the confidence they inspire through
a strong type system and the .NET
security policy. Nevertheless, .NET
lacks a formal mechanism for speci-
fying the semantics of the types
offered by an assembly.
This article shows how, using
.NET attributes, it is feasible to
introduce Boolean assertion clauses
in .NET assemblies. Using reflec-
tion, these assertion attributes can
be extracted, checked, and moni-
tored at runtime. We offer an asser-
tion browser tool that extracts the
assertion attributes from the assem-
blies for documentation purposes
and generates a trusted proxy
assembly to monitor these asser-
tions at runtime.
Introduction
Bertrand Meyer included the
assertion formalism in Eiffel underhis Design by Contract metaphor.
Works for other languages such as
Java and C++ recognize the signifi-
cance of assertions. This article
introduces assertions in .NET code
through attributes. This approach
changes neither .NET languages nor
.NET compilers.
The resulting asserted assembly
will have two advantages:
1. To serve as a documentation
vehicle: Because assertions are
inserted as attributes, they areformal metadata integrated into
the assembly.
2. To increase the developer's trust
in the assembly: Because asser-
tions can be checked and moni-
tored at runtime by means of
reflection, the developer can veri-
fy that the assembly really does
what the assertions say it does.
Components in Previous
Technologies
Technologies like CORBA and
COM use IDLs (interface definition
languages???), but these IDLs do not
include more semantic information
than that which can be deduced
from types, methods, and parame-
ter names.
Java technology alleviates this
drawback, but has the following
weaknesses:
The monolinguistic approach. A
Java component can only be used
by another Java component.
There is no seamless and com-
fortable way to connect an EJB
component to a component
developed in another languageand vice versa.
The documentation extracted
from a component consists only
of type signatures and comments.
The Java reflection mechanism is
unable to recover the informa-
tion embedded as comments.
Developers must work directly
with the internals of the Java
bytecode files.
Strengths of .NET Assemblies
.NET component are supportedby assemblies.
A .NET assembly is language neu
tral. It can be used transparently
from any other .NET assembly,
independently of the source lan-
guages that produced each.
A .NET assembly contains code,
data, and metadata. Metadata ca
be fully extracted using .NET
reflection capabilities.
.NET introduces a new metadata
feature: attributes. Through
attributes we can include our ow
custom metadata in assemblies.
The Convenience of More
Trusted .NET Assemblies
The .NET signing policy is based
on confidence in the human (enter
prise) author of the assembly, but
this is not enough for reliability.
Applications are not forced to use
the types of an assembly under the
contract that these types request
(in the BCL these contracts appea
in a verbose fashion). We need a
more organized documentation to
verify if an assembly does what it
says it does.How can an assembly exporting
a type Stack, with a Push method
and a property Top, prohibit its
client assemblies from applying Top
to a stacksifsis empty? How can
the same assembly guarantee that
after pushing an objectxin s, the
property Top will returnx?
Including Assertions by
Means of .NET Attributes
The proposed assertions are rep
BY MIGUEL KATRIB,
ERICH LEDESMA, &
LEONARDO PANEQUE
C#AL
Including Assertionin .NET Assemblies A step toward a trustedcomponent scenario
HOME
CLIENT
SERVER
EVERYWHERE
n
-
7/28/2019 Including Assertions in NET Assemblies (Dot Net Developers Journal)
2/739dotnetdevelopersjournal.com September 2003
resented by the interface:
interface IAssertion{
string BoolExpression {get;}
string Label {get; set;}
bool Inherited {get;}
}
The class AssertionAttribute
implements IAssertion and extends
the .NET SystemAttribute. This class
allows the inclusion of the assertion
attributes in the source code during
compilation.
The target of an assertion attrib-
ute will be a type or a method
(including properties and construc-
tors). The assembly resulting from
the compilation of a project with
those assertion attributes will be
called the asserted assembly.The assertions are really the raw
string parameters of these attrib-
utes, which are returned by the
property BoolExpression. They must
denote logical expressions under a
notation named C#AL (C# -like
Assertion Language).
For example, the string "!Empty"
used in the assertion attribute of the
method Pop (see Listing 1) helps to
specify that a stack can not be
popped if it is empty.
The property Label = "To pop an
element the stack cannot be empty"
defines a textual message about the
assertions semantics.
The Inherited property states
whether the assertion will also apply
to a derived type or method.
The AssertionAttribute class has
three derived types. The
InvariantAttribute derives from
AssertionAttribute. The
AttributeUsage attribute is used to
define the possible targets of this
InvariantAttribute, which in this
case are classes, interfaces, or
structs (see Listing 2).
For an InvariantAttribute theBoolExpression should be true
before and after applying any public
method of the type attached to the
attribute.
Because the property
AllowMultiple is set to true, several
invariant attributes can be targeted
to a single type. A logical conjunc-
tion (&&) will be applied to all
Boolean expressions.
When assertions are monitored
during runtime and && evaluates to
false, then an InvariantException
exception will be thrown. The Label
property will be part of the excep-
tion message.
The following excerpt shows an
invariant attribute attached to a
Rational type:
[Invariant("Denominator != 0",
Label="Denominator cannot be
zero")]
struct Rational{
...
public int Numerator{
...
}
public int Denominator{
...
}
}
PreconditionAttribute and
PostconditionAttribute also derive
from AssertionAttribute. Under the
Design by Contract guidelines, a
PreconditionAttribute denotes a
condition required by the assembly
before applying the public method
targeted by the attribute, and a
PostconditionAttribute denotes a
condition that will be ensured by
the assembly after applying the
method targeted by the attribute.
Note: The Push method of the
Stack type (see Listing 1) has three
postconditions (the property
AllowMultiple of the
PostconditionAttribute is true).
interface Stack {
...
[Postcondition("!Empty")]
[Postcondition("Total ==
Total.Old + 1")]
[Postcondition("Top == x",
Label="The Stack applies a LIFO
policy")]
public void Push(object x);
...
}
The attribute
[Precondition("!Full", Label="Stack
Overflow")] states that to push the
stack, it cannot be full; the attribute
[Postcondition("!Empty")] states
that after pushing, the stack will not
be empty; and the
[Postcondition("Total == Total.Old +
1")] states that the total of elements
in the stack will be one plus the old
value of Total before executing the
push. The attribute
[Postcondition("Top == x",
Label="The Stack applies a LIFO
policy")] means that the pushed ele-
ment will be at the top of the stack.
The InvariantException,
PreconditionException, andPostconditionException types,
derived from the base class
AssertionException, represent the
exceptions to be thrown when the
corresponding assertion is not ful-
filled at runtime.
Assertion Attributes and
Inheritance
An invariant defined in a base
type must be fulfilled by the
instances of a derived a class.
Preconditions and postconditions
targeting a virtual base methodmust be fulfilled by the correspon-
ding overriding method.
The above approach fits well
with classes implementing an inter-
face base type, but it should not
necessarily be applied when a class
extends another base class. If the
Inheritable property is set to false, a
developer of a derived method is
not forced to guarantee the asser-
tions of the corresponding base
method. For example, the following
class Account does not require a
derived class CreditAccount to con-
form to the precondition "Balance >
amount" when overriding the
method Withdraw, because a
CreditAccount allows a negative
Balance.
class Account{
..
[Precondition("Balance >
amount")]
[Postcondition("Balance ==
Balance.Old - amount")]
public virtual void Withdraw(int
amount){
...
}
}
The C# Assertion Language
(C#AL)
Each BoolExpression parameter
in the assertion attributes must
describe a C#-like Boolean expres-
sion (an analogous approach could
be applied to other .NET lan-
guages). An entity appearing in the
expression must observe the follow-
-
7/28/2019 Including Assertions in NET Assemblies (Dot Net Developers Journal)
3/740September 2003 dotnetdevelopersjournal.co
C#AL
HOME
CLIENT
SERVER
EVERYWHERE
ing rules:
1. It must be public in order to be
used and tested by client code in
other assemblies. If the Full proper-
ty of the Stack type (see Listing 1)
were not public, then it would makeno sense for the method Push to
have the precondition "!Full".
2. Pre- and postconditions of an
instance method can use both
instance and static features of the
type defined by the method.
3. Pre- and postconditions of a
static method can use only the
types static features.
C#AL includes a special property,
Old, which can be applied to any
term used in a method's postcondi-
tion. This property returns the cor-
responding value of the term before
a method's execution. The attribute
[Postcondition("Total == Total.Old
+ 1",
Label="The stack
must increase by one element"]
attached to the Push method,
expresses that after pushing an ele-
ment, the Total property must
return a value equal to the value of
Total before the execution of Push
(denoted by Total.Old) increased by
1.Whenxis of reference typex.Old
returns a copy of the reference. But
if the dynamic type of the object
attached toximplements the inter-
face ICloneable, the returned value
is a clone ofx. So by implementing
the method Clone and overriding
the method Equals, developers can
decide how deeply to apply the Old
policy.
A special "variable" result can be
used in the postcondition of a non-
void method; it will refer to the
returning value of the method. For
example, the property Tomorrow
(see Listing 3) has the postcondi-
tion:
[Postcondition("result-this == 1",
Label = "Tomorrow is the day after
today")]
Others operators are => (implies)
and (if only if ). These operators
facilitate design and improve read-
ability, as shown in the invariant
assertion attribute of the Date type
(see Listing 3).
Quantifiers in C#AL
Several previous papers have
recognized the relevance of quanti-
fiers in assertions. Fortunately .NETstandardizes the notion of collec-
tion based on the IEnumerable
interface. Furthermore, C# includes
the foreach operator. So, based on
these features, C#AL includes the
universal quantifier forall and the
existential quantifier exists:
forall (T x in C : E) and exists
(T x in C : E)
where Tis a type,xis a variable, Cis
an expression denoting a collection
of returning objects of some type
that can be cast to T, and Eis a
Boolean expression in which the
variablexshould appear.
The forall operator evaluates
true if for allxin Cthe expression E
evaluates to true; otherwise it evalu-
ates to false. The operator exists
evaluates to true if at least onexin
Cexists such that the expression E
evaluates to true; otherwise it evalu-
ates to false.
Both operators will throw an
InvalidCast exception if the object
returned by the iteration cannot be
cast to T. If you want to apply anassertion to only a subset ofC, you
can use the following pattern:
forall (object x in C : (x is T)
=> E')
where E'is the expression resulting
from changing all occurrences ofx
in Eby the cast (T)x.
An Employee type having a
property MyDepartment and a
Department type having an
Employees property could have the
following invariants:
[Invariant("forall (Employee e in
Employees : e.MyDepartment ==
this)",
Label = "Legal
Department")]
class Department {
...
}
[Invariant("exists (Employee e in
MyDepartment.Employees : e ==
this)",
Label = "Legal employ
ee")]
class Employee {
...
}
Quantifiers are also helpful inpostconditions and preconditions.
For example, a Fire method of the
Department type might be:
[Postcondition("! exists (e in
Employees: x == e)"]
public void Fire(Employee e){...}
A method, AnnounceMeeting,
announces a meeting date but
requires that the date will not be a
holiday:
[Precondition("forall (Date d in
Date.Holidays : d !=
meetingDate")]
public void AnnounceMeeting(Date
meetingDate){...}
The forthcoming iterator con-
struct announced for C# will offer
better support for these C#AL quan
tifiers.
About Implementation
The assembly
AssertionEngine.dll contains all
attribute types presented in the pre
vious sections.An application can use an asser
ed assembly as any ordinary .NET
assembly, ignoring the assertion
attributes embedded in it.
Nevertheless, an application can
also retrieve and study these asser-
tions for documentation purposes
and can also evaluate them to mon
itor the behavior of the features tar
geted by the assertion attributes.
For these purposes the
AssertionEngine.dll includes the
f1 Figure 1: The C#AL Browser Tool
-
7/28/2019 Including Assertions in NET Assemblies (Dot Net Developers Journal)
4/7
ad
?
41dotnetdevelopersjournal.com September 2003
-
7/28/2019 Including Assertions in NET Assemblies (Dot Net Developers Journal)
5/742September 2003 dotnetdevelopersjournal.co
C#AL
HOME
CLIENT
SERVER
EVERYWHERE
AssertionManager type. Using
reflection, AssertionManager
retrieves Assertion objects from the
asserted assembly at runtime (the
Assertion class implements
IAssertion).
But developers dont need to use
the above capabilities programmati-
cally. A browser tool (see Figure 1)
allows selection of an assembly, and
listing of the types, methods, and
assertions in the assembly.
The Documentation Panel gen-
erates an HTML file (Figure 2 illus-
trates the displayed HTML for the
Stack type).
The following is a typical sce-
nario: you are developing an appli-
cation using an asserted A.dll. You
can use the C#AL Browser to visual-
ize the assertion attributes embed-
ded in A.dll and check if they are
correct. If you want to monitor the
assertions, then you can use the
Build button (see Figure 1) to gen-
erate a trusted assembly,
Trusted_A.dll, which works as aproxy to the original A, intercepting
calls to the asserted A methods and
evaluating the code of the corre-
sponding assertions. Finally, you
must rebuild your project, removing
the reference to A.dll and including
references to the new Trusted_A.dll
and to the AssertionEngine.dll. The
resulting .exe file will monitor the
assertion's fulfillment.
Conclusion and Further Work
Unfortunately the .NET
Framework did not include an
assertion mechanism from the out-
set. There is an Assert method in the
Debug class, but it is not enough to
achieve the specification goal of
assertions.
Assertions are considered as first
runtime entities including them
into the Rotor runtime.???
eXtensible C#
(www.resolvecorp.com) includes
declarative assertions. At present
this approach is less expressive than
C#AL.
The C#AL Browser Tool allows
you to insert assertions in existing
assemblies. Developers are also
working on an implementation of
assertions using CodeDom. In this
case the Trusted_A.dll will not act as
a proxy, but will embed its own A
code.
Assertions are not enough to
completely guarantee a compo-
nent's quality and behavior, accord-
ing to Beugnard et al. in their book,Making Components Contract
Aware. Nevertheless, we hope that
the proposed inclusion of assertion
attributes in .NET will be a humble
but important step toward the goal
of a trusted component scenario.
This article illustrates the signifi-
cance of .NET attributes for
metaprogramming. Combining
attributes with assertions opens
other research areas such as the
specification of temporal con-
straints, component coordination,
and synchronization of multithread
ing applications. We are exploring
those areas. More contributions
would be very welcome.
References Beugnard, A., Jzquel, J.M.,
Plozeau, N., Watkins, D. (1999).
Making Components Contract
Aware. Computer. July.
Coira, J., Katrib, M. (1997).
Improving Eiffel Assertions
Using Quantified Iterators.
Journal of Object Oriented
Programming. Nov./Dec.
eXtensibleC# (XC#):
www.resolvecorp.com
Design by Contract for Java Using
JMSAssert:
www.mmsindia.com/DBCForJav.html
Fernandez, D., Katrib, M.,
Pimentel, E. (1999).
Synchronizing Java Threads
Using Assertions. Proceedings of
TOOLS 31th. IEEE Computer
Society.
Katrib, M., Martnez, I. (1993).
Collections and Iterators in
Eiffel.Journal of Object Oriented
Programming. Nov./Dec.
iContract: The Java Design by
Contract Tool:
http://people.cs.uchicago.edu/~s
ongyanf/research/contract_pape
/other/icontract-tools98usa.pdf
Meyer, B. (1992) Applying Design
by Contract. Computer. IEEE.
October.
Meyer, B. (1997). Object-Oriented
Software Construction. 2nd
Edition. Prentice Hall.
Meyer, B. (1992). Eiffel: The
Language. Prentice Hall.
Java with Assertions: http://theo
retica.informatik.uni-
oldenburg.de/~jawa
Support for Assertions in the Roto
Runtime:www.csse.monash.edu.au/~nam
/rotor/MonashRotorProject.html
f2Figure 2: HTML documentation for the Stack type
-
7/28/2019 Including Assertions in NET Assemblies (Dot Net Developers Journal)
6/7
-
7/28/2019 Including Assertions in NET Assemblies (Dot Net Developers Journal)
7/744September 2003 dotnetdevelopersjournal.co
C#AL
HOME
CLIENT
SERVER
EVERYWHERE
l 1[Invariant("Total>=0", Label = "The total itemsin a stack can not be negative")]public interface IStack{
int Total{get;}[Postcondition("Result Total == 0", Label =
"Emptiness of a stack")]bool Empty{get;}bool Full{get;}[Precondition("!Empty", Label = "Stack
Underflow")]object Top {
get;[Postcondition("Top == value", Label = "The
Top changes")]set;
}[Precondition("!Full", Label = "Stack
Overflow")][Postcondition("Top == x", Label = "Applying a
LIFO policy")][Postcondition("Total == Total.Old + 1", Label
= "Increasing the stack size")][Postcondition("!Empty", Label = "Non
Emptiness")]
void Push(object x);[Precondition("!Empty", Label = "StackUnderflow")]
[Postcondition("Total == Total.Old - 1", Label= "Decreasing the stack size")]
[Postcondition("!Full", Label = "NonFullness")]
void Pop();}
interface IAssertion{string BoolExpression {get;}string Label {get; set;}bool Inherited {get;}
}
abstract class AssertionAttribute:System.Attribute, IAssertion {
AssertionAttribute(string CSALExpression){boolExpression = CSALExpression;
}public string BoolExpression{
get {return boolExpression;
}}public string Label{
get {return label;
}set {
label = value;}
}public Type ExceptionRaised
get {return exceptionRaised;
}set {
exceptionRaised = value;}
}string boolExpression;string label = """;Type exceptionRaised;
}
[AttributeUsage(AttributeTargets.Class |AttributeTargets.Struct |AttributeTargets.Interface,AllowMultiple = true)]
public class InvariantAttribute :AssertionAttribute {
InvariantAttribute(string CSALExpression) :base(CSALExpression){}
}
[AttributeUsage(AttributeTargets.Method |AttributeTargets.Property |AttributeTargets.Constructor,AllowMultiple = true)]
public class PreconditionAttribute :AssertionAttribute {
PreconditionAttribute(string AALExpression) :base(AALExpression){}}
[AttributeUsage(AttributeTargets.Method |AttributeTargets.Property |AttributeTargets.Constructor,AllowMultiple = true)]
public class PostconditionAttribute :AssertionAttribute {
PostconditionAttribute(string CSAssertion) :base(CSExpression){}}
[Invariant(@"(Year > 0) && (Day >= 1) && (Month >= 1) &&
(Month (Day (Day (Day (Day