including assertions in net assemblies (dot net developers journal)

Upload: mauricio-jorge-cordeiro-garrido

Post on 03-Apr-2018

212 views

Category:

Documents


0 download

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