SheepAOP Part 2 – Pointcut and SAQL Basics

This Series

  1. Getting Started with SheepAOP
  2. Pointcuts and SAQL Basics
  3. Aspects Lifecycles & Instantiations
  4. Integrating with IoC containers
  5. Aspects Inheritance & Polymorphism
  6. Attributive Aspects (ala PostSharp)
  7. 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
  • Name: ‘*Customer’
  • Name: (‘*Service’ | ‘*Repository’)
Namespace string
  • !Namespace: ‘System.*’
ImplementsType Type Pointcut
  • ImplementsType:’System.Collections.IEnumerable’
  • ImplementsType: (Name: ‘*Repository’ | Namespace: ‘*.DataContexts’)
AssignableToType Type Pointcut
  • AssignableToType:(‘System.Collections.*’ & Interface)
  • AssignableToType: Namespace: ‘System.Collections.*’
HasMethod Method Pointcut
  • HasMethod: Name: ‘Get*’
  • HasMethod: (Public & Args(‘System.Int32’)
HasProperty Property Pointcut
  • HasProperty:Name:’Length’
  • HasProperty:Type:Implements:’*.*Service’
HasField Field Pointcut
  • HasField:Name:(‘_createdDate’ | ‘_entryDate’)
  • HasField:((Public & Static) | Protected)
ThisAspect (none)
  • ThisAspect
  • Implements:ThisAspect
  • Namespace:’Sheep.*’ & !ThisAspect
HasCustomAttributeType Type Pointcut
  • HasCustomAttributeType:ImplementsType:’BindableAttribute’
InheritsType* Type Pointcut
  • InheritsType:Namespace:’System.Collections’
Interface* (none)
  • Interface
  • !Interface
Abstract* (none)
  • Abstract & Name:’*Strategy’
ValueType* (none)
  • ValueType & HasMethod:Name:’Equals’
Class* (none)
  • Class & Implements:’Sheep.Irepository’

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
  • Name:(‘Get*’ | ‘List*’)
Args
(Compare the first N arguments, not the entirety)
TypePointcut[…]
  • Args: (‘System.Int*’,  *, ‘System.String’)
  • Public & Args:(*, *, (Namespace:’Sheep.Service’ | Name:’*Service’))
InType Type Pointcut
  • InType:’System.String’ & Name:’IndexOf’
  • Public & InType:!ThisAspect
ReturnType Type Pointcut
  • Name:(‘IndexOf’|’LastIndexOf’) & ReturnType: ‘System.Int*’
  • ReturnType:((Interface | Abstract) & Name:’*Visitor’)
ReturnsVoid (none)
  • ReturnsVoid
  • ReturnType:’System.Boolean’ | ReturnsVoid
Static (none)
  • !Static
Virtual (none)
  • Virtual | Public
Public (none)
  • Public & Name: ‘Get*’
Private (none)
  • Private & InType:Implements:’System.Collections.IList’
Protected (none)
  • Protected & !Static
Internal (none)
  • Internal | Protected
Abstract* (none)
  • Virtual | Abstract
Implements* Method Pointcut
  • Private & Implements:’System.Collection.Ienumerable::GetEnumerator()’
HasCustomAttributeType Type Pointcut
  • HasCustomAttributeType:ImplementsType:’ValidationAttribute’

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
  • Public & InType:’System.Collections.List’ & Name: ‘Count’
Type Type Pointcut
  • Name:’IsEmpty’ & Type:’System.Boolean’
InType Type Pointcut
  • Name: ‘Length’ & InType: ‘System.Array’
Static (none)
  • Static & InType:’System.DateTime’ & Name:’Now’
Virtual (none)
  • Virtual | Public
Public (none)
  • Public & Name: ‘*Count’
Private (none)
  • Private & InType:Implements:’System.Collections.IList’
Protected (none)
  • Protected & !Static
Internal (none)
  • Internal | Protected
HasCustomAttributeType Type Pointcut
  • HasCustomAttributeType:Name:’Required’
Abstract* (none)
  • Virtual | Abstract

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:

  1. ‘literal value’
  2. @pointcutRef
  3. (parentheses)
  4. criteria: argument
  5. !negation
  6. array, array, array
  7. 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

Advertisements

One thought on “SheepAOP Part 2 – Pointcut and SAQL Basics

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s