The SOUL/Aop manual

The following manual assumes basic knowledge of the SOUL LMP-language and basic Aspect Oriented Programming terminology.

Introduction

SOUL/Aop consists of a hard-coded aspect weaver mechanism in Smalltalk and building blocks (AspectWeaverMixins) to extend this weaver to a user-defined aspect weaver using logic meta-programming (LMP).This means that the user can define his own aspect-specific aspect language. The hard-coded Basic Aspectweaver offers a limited support for the insertion of crosscutting code (and instance variables with crosscutting scope) in other Smalltalk classes. User-defined AspectWeaverMixins can be composed with the Basic Aspectweaver to form a user-defined aspect weaver.Such an AspectWeaverMixin must transform an aspect written in the user-defined aspect-language into an aspect written in the basic aspect-language that can be woven by the basic aspect weaver. Both aspects and AspectWeaverMixins are logic programs. As a consequence, aspects themselves are implemented using AspectWeaverMixins, but generally contain much simpler logic declarations (since they do not program transformations).
We will first introduce the concept of an aspect language embedded in a logic language. We will then explain the aspect language of the basic aspect weaver. After that, we will describe the AspectWeaverMixins and explain how to build a user-defined aspect weaver with them. Finally, we elaborate on some example aspect-languages built in SOUL/Aop and explain how to use the tool.


Aspects as logic propositions

Because we embed our aspect language in a logic programming language, the aspect language is described by logic predicates. Trivially, an aspect will be a set of logic propositions that use these predicates. For example, if our aspect language consists of an adviceBefore/3 and an adviceAfter/3 predicate, a possible aspect in this language is the following:

adviceBefore(MyClass,mySelector,{ 'some Smalltalk code to execute before MyClass>>mySelector executes' }).
adviceBefore(MyClass,myOtherSelector,{ 'some Smalltalk code to execute before MyClass>>myOtherSelector executes' }).
adviceAfter(MyClass,mySelector,{ 'some Smalltalk code to execute before MyClass>>mySelector executes' }).

Some aspectweaver understands these propositions and will perform the actual weaving process that introduces the desired behaviour that is specified in these propositions. In other words, the aspect weaver performs queries to retrieve all propositions of every predicate in the aspect language. In doing so, it gathers all its required information to weave the aspect.

<elaborate on the use of logic rules>


The Basic Aspectweaver

The basic aspect weaver is not intended to be used by end-user aspect-programmers. It provides a basic interface on which other aspect weavers can be build. In this section, we explain what predicates are understood by the basic aspect weaver. Information on how to use this basic aspect weaver can be found in the section on AspectWeaverMixins.

Weaving crosscutting behaviour

wrap/3 wrap(?class,?selector,?code)

The primary role of the basic aspectweaver is to offer an invisible method-wrapping mechanism. This means that we can wrap methods and specify new behaviour for existing methods. The changes will not be visible in the smalltalk codebrowser, but the execution will always be deferred to the overriden behaviour. An aspect for the basic aspect weaver can specify which methods to wrap with what code through the definition of propositions using the wrap/3 predicate. The first two arguments of this predicate specify which method in what class, the third argument is a quotedcode term.For example, the following fact states that we wrap the method Foo>>bar with Transcript show: 'Hello aspectworld'.

wrap([Foo],bar,{ Transcript show: 'Hello aspectworld' }).

Hence, this basic aspect weaver provides only method-based joinpoints to weave on.Currently, the basic weaver does not support multiple declarations of the wrap/2 predicate for a single method. If you want this, it should be handled in a user-defined aspect weaver. Wrap declarations for different methods are no problem.

original

Of course, wrapping a method means that the method still exists afterwards. This is usefull because we might want to call the original method somewhere as part of the new behaviour. In fact, this is even very common. Therefore, the basic aspectweaver provides access to the wrapped method through the original expression that can be used in aspect-code. For example, the following wrap/3-declaration defines that the new behaviour of method Foo>>bar should execute Transcript show: 'Hello aspectworld' and afterwards execute the original method's behaviour.

wrap([Foo],bar,{ Transcript show: 'Hello aspectworld'. original perform}).

TODO: Explain how the arguments are passed on and we can change the arguments

Communication between Aspects and Objects

thisObject

All code that executes in an object-oriented system is executing in the context of some current receiver. In Smalltalk, the keyword to refer to this current receiver is 'self'. Aspect-code is also executing in the context of a current receiver, i.e. the current receiver at the location where the aspect-code is invoked. In SOUL/Aop, we can refer to this current receiver using the 'thisObject' keyword. Mind that this is fundamentally different from 'self' because when aspect-code executes, 'self' would be ambiguous: is it a reference to the aspect or to the object in which the aspect is currently executing?. Clearly, 'thisObject' refers to the latter and 'self' does not exist in aspect-code.

thisAspect

Does not exist yet.

Weaving state with a crosscutting scope

scopeOf/2 scopeOf(?listOfVariables,?scopeCode)

Aspects encapsulate crosscutting code. But aspects can also have instance variables, which can be seen as a kind of crosscutting state. In the same sense that objects encapsulate behaviour and state; aspects also encapsulate crosscutting behaviour and state. Therefore, using the scopeOf/2-predicate, we can declare a list of aspect-instance variables in the first argument of scopeOf/2. The major difference between objects and aspects is the scope of these variables. All aspect-instance variables are visible in the aspect-code (defined using the wrap/3 predicate) but each aspect-instance variable's value is unique for each aspect-instance. What aspect-instance should be used as context for executing the aspect-code, can be defined in the second argument of the scopeOf/2-predicate.

This second argument of scopeOf/2 is Smalltalk code that we will refer to as 'aspect-instance-scope' code in the remainder of this text. This 'aspect-instance-scope' code will be executed by the weaver every time some aspect code will be executed. Based on the result of the execution of the 'aspect-instance-scope' code, the weaver will select an aspect instance as context in which to execute the aspect code. The same aspect instance will be used by all aspect code in which this 'aspect-scope-code' results in the same value. The aspect instance itself is automatically created when it is needed. For example, consider the following aspect in the basic aspect weaver's aspect language:

scopeOf(<counter>,{thisObject}).
wrap([Foo],bar,{ counter ifNil: [counter := 0] ifNotNil:[counter := counter + 1]. original perform}).

The aspect above consists of aspect code that will execute when the method Foo>>bar is executed. Each time, it will augment a counter aspect-instance variable.What aspect instance that should be used is determined by the code in the scopeOf/2-predicate proposition, which in this case is 'thisObject'. This code is executed before every execution of the aspect code that is defined in the wrap/3 predicate and will result in different values for each different currently executing receiver object (see previous section on thisObject). Hence, the counter will count the number of times the method Foo>>bla is called for each instance of Foo. As a second example, consider the same aspect but with a different scope of the counter instance variable:

scopeOf(<counter>,{#Image}).
wrap([Foo],bar,{ counter ifNil: [counter := 0] ifNotNil:[counter := counter + 1]. original perform}).

Here, the 'aspect-instance-scope' code is '#Image', which is a symbol and will trivially always result in the same value. As a result, the counter aspect-instance variable is the same everywhere and will count all executions of the Foo>>bla method. Note that we could also have used scopeOf(<counter>,{1}) or scopeOf(<counter>,{#anyConstant}), since '1' and '#anyConstant' will also evaluate to the same value every time they are evaluated..

Using this construction we can define many different aspect-instance scope's. Here are some examples:

scopeOf(?vars,{#global}) if
    globalScope(?vars).
globalScope(?vars) defines that all aspect-instance variables ?vars have a global scope and are the same for each aspect-instance.

scopeOf(?vars,{thisObject}) if
    eachObjectScope(?vars).

eachObjectScope(?vars) defines that all aspect-instance variables ?vars have an object-scope and are the same for each thisObject in which aspect-code executes.

scopeOf(?vars,{ thisObject understands: #enumerate}) if
    allEnumeratorsScope(?vars).

allEnumeratorsScope(?vars) defines that all aspect-instance variables ?vars have a scope that is divided between all objects of classes that define #enumerate and those that don't. The instance-variables are the same in all objects of classes that understand #enumerate and they are also the same in all classes that don't understand #enumerate.

scopeOf(?vars,{thisObject class}) if
     eachClassScope(?vars)

eachClassScope(?vars) defines that all aspect-instance variables ?vars have a class-scope and are the same for each class in which aspect-code executes.

initialize/1

TODO: explain how this is used to initialize the aspect-instance variables


The AspectWeaverMixins

Clearly, the basic aspect weaver defines a very low-level aspect language containing methodpointcuts and methodwrapper definitions in the form of wrap/3-declarations. To use this aspect language, an aspect programmer needs to understand the workings of the basic aspect weaver very well, especially for the scopeOf/2-predicate declarations that define the aspect-instance variables' crosscutting scope. But the LMP-language interface of this basic weaver allows to write logic rules that define a higher-level aspect language. This is done using an AspectWeaverMixin. Ultimately, several of such mixins are composed together to integrate and combine several aspect-languages.

AspectWeaverMixin

Basically, an AspectWeaverMixin is a set of logic facts and rules that is not complete because it is parameterized by another AspectWeaverMixin. Said differently, an AspectWeaverMixin is a module of logic rules that contains some external references that should be filled in by other logic modules. AspectWeaverMixins are our basic building blocks to build a complete aspectweaver. to be completed

A simple AspectWeaverMixin: 'Hello Aspectworld'

Consider this example of logic rules in an AspectWeaveMixin A:

scopeOf(<>,{#Image}).
wrap(?class,?selector,{ Transcript show:'Hello Aspectworld'. original perform}) if
     ?aspect.sayHelloBefore(?class,?selector) .

In this simple example, the weave-proposition is a rule that contains a condition that should be looked up in the logic module bound to the ?aspect parameter. This means that another AspectWeaverMixin B should be bound to ?aspect before this AspectWeaverMixin A can work. Here is an example of a possible set of logic rules for AspectWeaverMixin B:

sayHelloBefore([Foo],bar).
sayHelloBefore([MyClass],mySelector).

If we bind AspectWeaverMixin B to the ?aspect parameter of AspectWeaverMixin A, we obtain a complete aspectweaver that will weave 'Hello Aspectworld' printouts before every invocation of Foo>>bar and MyClass>>mySelector.

to be completed

Composing AspectWeaverMixins

to be completed

Pointcuts

Basic user-defined pointcuts

to be completed

Describing pointcuts using Logic Meta Programming

to be completed


Examples

AdviceWeaverMixin (towards aspectJ)

to be completed

ErrorHandlerMixin

to be completed

SubjectWeaverMixin (towards HyperJ)

to be completed


Hands on the SOUL/Aop tool

to be completed

 


The QSOUL/Aop manual was last updated on Wednesday, 03-Apr-2002 21:04 by Johan Brichau