This Series
- Getting Started with SheepAOP
- Pointcuts and SAQL Basics
- Aspects Lifecycles & Instantiations
- Integrating with IoC containers
- Aspects Inheritance & Polymorphism
- Attributive Aspects (ala PostSharp)
- Unit-testing your aspects
This post is based on SheepAop Preview 1.1.
This Post
This post will serve primarily more as a reference material than a blog post or a bed-time reading consumption. In fact, a substantial portion of this post will quickly find its way to the SheepAop project documentation/wiki pages any time soon.
Pointcuts
We have covered this one quite briefly in the previous post.
Pointcut is an aspect-oriented mechanism to pick out join-points (e.g. method calls, properties, field access, instantiations) from your program code using some sort of query language.
The following is an example of how we define a pointcut to pick out all methods in Sheep.EmailService class:
[Aspect]
public class MyAspect
{
[SelectMethods("Public & InType: 'Sheep.EmailService'")]
public void MyPointcut(){}
}
Member Pointcut vs Call Pointcut
There are several types of pointcut in SheepAop, but they all generally fall into 2 categories: member-pointcut and call-pointcut.
Member-pointcuts (aka “Execution” pointcuts in AspectJ) pick out the structural elements of your program. For example your actual classes, methods, properties, fields, etc.
In the other hand, call-pointcuts (likewise “Call” pointcuts in AspectJ) pick out specific lines of instruction within the body of your program code.
An easy way to understand the difference is to take method-pointcut (a member pointcut) and a call-method-pointcut (a method pointcut) as an example.
When you apply an advice to a method-pointcut, you are weaving around the body of the targetted metod itself.
Whereas with a call-method-pointcut, you will be weaving every specific line in your code body that is found to be making a call to the targeted method.
Member Pointcuts
The following are the types of Member Pointcuts available in SheepAop:
| Type | Attribute | Advices** |
| Types | [SelectTypes(“saql”)] | Mixin* |
| Methods | [SelectMethods(“saql”)] | Around |
| Properties (gets + sets) | [SelectProperties(“saql”)] | Around |
| Property-Gets | [SelectPropertyGets(“saql”)] | Around |
| Property-Sets | [SelectPropertySets(“saql”)] | Around |
| Fields | [SelectFields(“saql”)] | (none) |
| Constructors* | [SelectConstructors(“saql”)] | Around |
| Events* | [SelectEvents(“saql”)] | (none) |
*) Not supported yet in the current release version of SheepAop
**) advices that are currently supported. The number might grow in the future
Call Pointcuts
In contrast to Member Pointcuts, which pick out structural element of your program, Call Pointcuts pick out specific line of instruction from within your program code.
There are different kinds of Call Pointcut recognizable by SheepAOP:
| Type | Attribute | Advices** |
| Call Methods | [SelectCallMethods(“saql”)] | Around |
| Get Properties* | [SelectGetProperties(“saql”)] | Around |
| Set Properties* | [SelectSetProperties(“saql”)] | Around |
| Get Fields | [SelectGetFields(“saql”)] | Around |
| Set Fields | [SelectSetFields(“saql”)] | Around |
| Instantiations* | [SelectInstantiations(“saql”)] | Around |
| Add events* | [SelectAddEvents(“saql”)] | Around |
| Remove events* | [SelectRemoveEvents(“saql”)] | Around |
| Invoke events* | [SelectInvokeEvents(“saql”)] | Around |
*) Not supported yet in the current release version of SheepAop
**) Advices that are currently supported. There might be more introduced in the future
SAQL
All pointcuts in SheepAop are expressed using a language called SAQL. Generally, there are 2 different types of SAQL syntax: literal-expression, and criteria-expression.
- Literal Expression
This is the simpler form of SAQL syntax, which looks and feels a lot like the pointcut language in AspectJ. I.e., it uses mainly member signatures and wildcards.
Syntax: a single-quoted string'expression'
Here are few examples:
[SelectMethods("'System.Int32 System.String::IndexOf(System.Char, System.Int32)'")] [SelectMethods("'* System.*::???Of*(*, System.*)'")] [SelectTypes("'Sheep*.Domain.*.???Customer'")]See below for the literal format for each type of pointcut.
Note: further development is underway to enhance the current SAQL literal-expression to follow a smarter DSL syntax, rather than the current implementation using the raw power of wildcard string comparison. - Criteria ExpressionThis is a richer way to do complex queries that are not possible using simple AspectJ syntax. It allows us to describe conditions based on certain criteria-set supported by each type of pointcuts.
Syntax:criteria
… for unary criteria (taking no argument, e.g. Public, Static, HasGetter), or…
criteria: argument
… for binary criteria (taking an argument, e.g. Name:’*Repository’), or..
Some examples:[SelectMethods("'Public & Name:'???CustomersBy*' & InType:Implements:Name:'*Repository'")] [SelectFields("(!Static & 'System.Int32 Sheep.*::_count') | (Static & Name: '*Count')")] [SelectProperties("((Static & Public) || (!Public & Virtual) & InType: (Implements:'Sheep.IRepository' | Inherits: 'Sheep.DataContext')")]Each type of pointcut has different set of supported criteria, which are all covered below.
Literal and Criteria expressions are not mutually exclusive. In fact, it is very common to mix both syntices within the same SAQL statement. For instance, take a look at lines #2 and #3 of the last example on Criteria Expression above. There are several literal-expressions embedded within the criteria (which are: ‘System.Int32 Sheep.*::_count’, ’Sheep.IRepository’, and ’Sheep.DataContext’ respectively).
Type Pointcut
Type-Pointcut Literal Format
In general, all literal-formats in SheepAop simply follows the standard qualified naming format in .Net framework. It hardly takes an inspired guess to work out the literal-format for ShepAop Type pointcut, which simply follows the naming format of .Net types.
Examples:
- ‘System.Collections.Generics.IList`1′
- ‘Sheep.OuterClass+NestedType’
- ‘System.String[]‘
Likewise any other literal expression, you can use ‘*’ and ‘?’ wildcards to match any string and any character respectively.
Type Pointcut Criteria
| Criteria | Argument | Examples |
| Name | string |
|
| Namespace | string |
|
| ImplementsType | Type Pointcut |
|
| AssignableToType | Type Pointcut |
|
| HasMethod | Method Pointcut |
|
| HasProperty | Property Pointcut |
|
| HasField | Field Pointcut |
|
| ThisAspect | (none) |
|
| HasCustomAttributeType | Type Pointcut |
|
| InheritsType* | Type Pointcut |
|
| Interface* | (none) |
|
| Abstract* | (none) |
|
| ValueType* | (none) |
|
| Class* | (none) |
|
Tips: The names of SheepAop criteria follow a convention designed to describe the type of their arguments. E.g.: ImplementsType (takes a Type argument), HasMethod (takes a Method argument). This is to aid readability, and ultimately to prevent SAQL from becoming a write-only language like Regex.
Method Pointcut
Method-Pointcut Literal Format
Likewise Type-Pointcut, the literal-format for Method Pointcut simply follows the standard fully-qualified naming format of .net methods.
Examples:
- ‘System.Int32 System.String::IndexOf(System.Char, System.Int32)’
- ‘Sheep.OuterClass`1+NestedType::SomeMethod(System.String, System.Boolean[])’
- ‘System.Int32 System.Object[]::SetValue()’
And of course, you can use ‘*’ and ‘?’ wildcards.
Method Pointcut Criteria
| Criteria | Argument | Examples |
| Name | string |
|
| Args (Compare the first N arguments, not the entirety) |
TypePointcut[…] |
|
| InType | Type Pointcut |
|
| ReturnType | Type Pointcut |
|
| ReturnsVoid | (none) |
|
| Static | (none) |
|
| Virtual | (none) |
|
| Public | (none) |
|
| Private | (none) |
|
| Protected | (none) |
|
| Internal | (none) |
|
| Abstract* | (none) |
|
| Implements* | Method Pointcut |
|
| HasCustomAttributeType | Type Pointcut |
|
Property Pointcut
Property-Pointcut Literal Format
The literal-format of a Property Pointcut is exactly the same as that of a Method Pointcut.
Examples:
- ‘System.Int32 System.Collections.IList`1::Count()’
- ‘Sheep.OuterClass`1+NestedType::SomeProperty()’
- ‘System.Int32 System.Object[]::Length()’
Yep, you guessed it, you can still use your good old ‘*’ and ‘?’ wildcards.
Property Pointcut Criteria
| Criteria | Argument | Examples |
| Name | string |
|
| Type | Type Pointcut |
|
| InType | Type Pointcut |
|
| Static | (none) |
|
| Virtual | (none) |
|
| Public | (none) |
|
| Private | (none) |
|
| Protected | (none) |
|
| Internal | (none) |
|
| HasCustomAttributeType | Type Pointcut |
|
| Abstract* | (none) |
|
Property Gets and Property Sets Pointcuts
Exactly the same as Property Pointcut, except that these pointcuts will pick out only the property getters and property setters respectively.
Field Pointcut
Field-Pointcut Literal Format
The literal-format of a Field Pointcut is quite similar to that of a Property Pointcut, minus the brackets.
Examples:
- ‘System.Int32 System.Collections.IList`1::_items’
- ‘Sheep.Web.ModelBinder::Instance’
- ‘Sheep.Product::_price’
Same deal, ‘*’ and ‘?’ wildcards are your friends.
Field-Pointcut Criteria
| Criteria | Argument | Examples |
| Name | string | Static & Public & Name: ‘Instance’ |
| Type | Type Pointcut | Name:’_hasValues’ & Type:’System.Boolean’ |
| InType | Type Pointcut | Name: ‘_items’ & InType: ‘System.Collections.Dictionary’ |
| Static | (none) | !Static & Private |
| Virtual | (none) | Virtual | Public |
| Public | (none) | !Public & Type:(‘System.Boolean’|'System.Int16′) |
| Private | (none) | Private & InType:Implements:’System.Collections.IList’ |
| Protected | (none) | Protected & !Static |
| Internal | (none) | Internal | Protected |
| HasCustomAttributeType | Type Pointcut | HasCustomAttributeType:Name:’Required’ |
Constructor Pointcut
(Not implemented yet in the current version of SheepAop, but will be very similar to Method Pointcuts in almost every respect).
About Call Pointcuts
Ok, time for a good tip. All call-pointcuts basically look the same. Each call-pointcut has a set of criteria “FromMethod”/”FromProperty” etc that represents the method where the line of call-instruction is found in its body. The other criteria represents what’s being called (e.g. a method, a field, a constructor, etc).
Literal expression is not available in any call pointcut by itself. But you can still use literal-expression to pick out the element of the call. For instance, the following pointcut …
SelectCallMethods[("Method: 'System.Int32 System.String::IndexOf(*)' & FromMethod:Type:'Sheep.StringHelper'")]
public void StringHelper_Calling_StringIndexOf(){}
… will pick out any call to String.IndexOf(*) methods made from any method body within the StringHelper class.
Call Methods Pointcut
Criteria
| Criteria | Argument | Examples |
| Method | string | Method: (Static & Name: ‘Parse’ & Args(‘System.String’)) |
| FromMethod | Method Pointcut | FromMethod:(Public & InType:Implements:’Irepository`1′) |
| FromPropertyGet* | Property Pointcut | FromPropertyGet: (Name: ‘TotalPrice’ & InType:’ShoppingCart’) |
| FromPropertySet* | Property Pointcut | FromPropertySet: ‘Sheep.IEntity::IsDeleted()’ |
| FromConstructor* | Constructor Pointcut | FromConstructor: Type: ‘Sheep.*Service’ |
Get Fields Pointcut
Criteria
| Criteria | Argument | Examples |
| Field | string | Field: (Static & Name: ‘Empty’ & InType: ‘System.Guid’) |
| FromMethod | Method Pointcut | FromMethod:(Public & InType:Implements:’Irepository`1′) |
| FromPropertyGet* | Property Pointcut | FromPropertyGet: (Name: ‘TotalPrice’ & InType:’ShoppingCart’) |
| FromPropertySet* | Property Pointcut | FromPropertySet: ‘Sheep.IEntity::IsDeleted()’ |
| FromConstructor* | Constructor Pointcut | FromConstructor: Type: ‘Sheep.*Service’ |
Set Fields Pointcut
Criteria
(Exacly the same as Set Fields Pointcut, except that it will pick out the act of setting a field, not getting one).
Get-Properties Pointcut
(Not implemented yet in the current release of SheepAop, although it’s quite trivial)
Criteria
| Criteria | Argument | Examples |
| Property | string | Property: (Static & Name: ‘Now’ & InType: ‘System.DateTime’) |
| FromMethod | Method Pointcut | FromMethod:(Public & InType:Implements:’Irepository`1′) |
| FromPropertyGet* | Property Pointcut | FromPropertyGet: (Name: ‘TotalPrice’ & InType:’ShoppingCart’) |
| FromPropertySet* | Property Pointcut | FromPropertySet: ‘Sheep.IEntity::IsDeleted()’ |
| FromConstructor* | Constructor Pointcut | FromConstructor: Type: ‘Sheep.*Service’ |
Set Properties Pointcut
Criteria
(Exacly the same as Set Properties Pointcut, except that it will pick out the act of getting a property, not setting one).
Instantiation Pointcut
(Not implemented yet in the current release of SheepAop)
Criteria
| Criteria | Argument | Examples |
| Constructor | string | Constructor:(Type: ‘String.DateTime’ & Args(‘System.Int32′, ‘System.Int32′) |
| FromMethod | Method Pointcut | FromMethod:(Public & InType:Implements:’Irepository`1′) |
| FromPropertyGet* | Property Pointcut | FromPropertyGet: (Name: ‘TotalPrice’ & InType:’ShoppingCart’) |
| FromPropertySet* | Property Pointcut | FromPropertySet: ‘Sheep.IEntity::IsDeleted()’ |
| FromConstructor* | Constructor Pointcut | FromConstructor: Type: ‘Sheep.*Service’ |
And/Or
You can apply And and Or operations on literal-expressions, criteria, and the mix of them both. You use ‘&’ and ‘|’ operators, or alternatively ‘&&’ and ‘||’, which make no difference whatsoever apart from to satisfy your geeky obsession.
We have used And and Or quite a bit in our exhaustive list of examples above. But another example doesn’t hurt. We’ll mix a combination of literal-expression and criteria:
[SelectType("'System.Collections.ArrayList' | 'System.Array' | (ImplementsType: 'System.Collections.IEnumerable' & !Abstract & Namespace:'Sheep')")]
public void MyPointcut(){}
Negation
The operator you’re looking for here is ‘!’. Examples:
[SelectTypes("!Abstract")] // Self explanatory
[SelectTypes("!ImplementsType:'System.Collections.IEnumerable'")] // Does not implement IEnumerable
[SelectTypes("ImplementsType:!'System.Collections.IEnumerable'")] // Implements any interface that is not IEnumerable
Reference
A pointcut can be built out of other pointcuts. You can reference another pointcut using its name by using an operator similar to Razor’s: @pointcutName. For example:
[SelectMethods("Public & Name:('Get*'| 'List*') & InType:@Repositories")]
public void RepositoryQueriesPointcut(){}
[SelectMethods("Public & Name:('Update*') & InType:@Repositories")]
public void RepositoryUpdatesPointcut(){}
[SelectTypes("ImplementsType:'Sheep.Data.IRepository`1'")]
[SelectTypes("InheritsType:'Sheep.Data.DataService'")]
public void Repositories(){}
Array
Certain criteria requires multiple values, such as the Args criteria of Method Pointcut. You define array by using a ‘,’ operator. For instance:
[SelectMethods("Name: 'IndexOf' & Args: 'System.Char', 'System.Int32'")] //You can also give it a bracket if it makes you feel happier inside
Group
Nothing special, just grouping stuff together using parentheses operators to give precedence to a certain clause within an And/Or, Negation or Criteria statement. See below for the default order of precedence. Before that
[SelectMethods(
@"Name: ('Count*'|'*Length')
& InType:(!Abstract & Implements:(Name:'*Service' & Namespace:'Sheep.Domain'))
& ReturnType: ('System.Int32' | 'System.Decimal')")]
public void CountMethods(){}
Order of Precedence
The order of precedence in SAQL operators follows the standard precedence in common programming languages. For completeness sake, the order is as follows, from the highest precedence to the lowest:
- ‘literal value’
- @pointcutRef
- (parentheses)
- criteria: argument
- !negation
- array, array, array
- and & or |
Summary
This post has documented the basic mechanic of SheepAop pointcut model and various SAQL syntax and criteria, which hopefully will come in handy as a reference manual to help you in writing your first SAQL queries. I plan to make a standalone SAQL Query-Analyzer tool that will allow you to instantly type in any SAQL query and execute it to immediately view the resulting join-points it picks out from your target assemblies. That would be a fabulous learning ground to try out various SAQL syntax and check the results immediately.
The next post will discuss about the practical use of SheepAop Aspects Lifecycle, where I hope we’ll finally get a chance to explore some practical examples on aspect-oriented-programming, using Aspect Lifecycle feature in particular.
*) Not supported yet in the current release version of SheepAop
[...] Pointcuts and SAQL Basics [...]
Pingback by SheepAOP Part 1 – Getting Started « Hendry Luk — Sheep in Fence — May 10, 2011 @ 3:38 am |