This is a preview of a new Reflector addin to well let you search for Program Patterns in your assemblies. The addin is mainly an adaptation to .Net of the article from Santany Paul and Atul Prakash, "A Framework for Source Code Search using Program Patterns".
What are Program Patterns ?
In their article, the authors use a pattern language to specify high-level patterns for making queries on the source code. It smells like regular expression but it operates on statements and expression instead of strings. The basic pieces of the pattern languages are:
More specific patterns, like if, while, throw, are also defined. In fact, for each interface of the Reflector.CodeModel namespace, a corresponding pattern class is needed. Let's illustrate this with a simple example of query: Find all occurence of a if followed by a throw statement.In the original paper notation, the pattern is defined as follows:
if (#) throw #;
Of course this will have to change a bit for C#.
C# implementation
In my implementaiton, the IPattern interface defines a Program Pattern:
public interface IPattern { bool Match(Object target); }
The interface is intentionaly simple to enable great extensibility. This interface is then specialized for IStatement, IExpression, IBlockStatement, etc.. For example, the pattern to match a throw statement will be implemented as follows:
IStatement
IExpression
IBlockStatement
public class ThrowExceptionStatementPattern : StatementPattern { // pattern for the expression that is throwed. Pats is a helper class private ExpressionPattern expression = Pats.AnyExpression; ... public override bool Match(IStatement statement) { // trying to cast statement to IThrowExceptionStatement IThrowExceptionStatement th = statement as IThrowExceptionStatement; // did not cast, no match if (th==null) return false; // expression did not match, no match if (!this.Expression.Match(th.Expression)) return false; // we have a match! return true; } }
To ease up things, a helper class containing static methods (Pats) takes care of creating those objects.
Implementing the example
Implementing the example is now just a matter of putting the pieces together. We need a pattern for the if, one for the throw and a pattern that will recurse in all the statements:
if
throw
// provided by Reflector IMethodDeclaration visitedMethod = ... // if pattern ConditionStatementPattern ifthen = Pats.If(); // setting the Then pattern // StatementInBlock says the pattern should be in the IBlockStatement // Pats.Throw() returns a ThrowExceptionStatementPattern ifthen.Then = Pats.StatementInBlock(Pats.Throw()); // a pattern that will recurse all the statements RecursiveStatementPattern rec = Pats.Recursive(ifthen); // launching the seach rec.Match(visitedMethod.Body);
Testing the example
The above pattern has been applied to the following class where 2 methods match the pattern, and 2 do not match it:
public class CodeMatchingTest { public void If(bool arg) { Console.WriteLine("This method has a if"); if (arg) Console.WriteLine("arg is true"); else Console.WriteLine("arg is false"); } public void Throw() { throw new Exception(); } public void IfThrow(bool arg) { if (arg) throw new Exception(); } public void IfThrowHiddenInsideWhileLoop(bool arg) { int i = 0; while(i<10) { if (i>5) throw new Exception(); Console.WriteLine(i.ToString()); } } }
And the result in Reflector is displayed below. As expected, IfThrow and IfThrowHiddenInsideWhileLoop have matched the pattern.
Page rendered at Monday, October 13, 2008 11:44:42 AM UTC
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.