Creating AOP IL Emitting framework – Part 3

16 באוגוסט 2008

In the previous post I talked about how to create a simple, not really helpful (yet), proxy class.

In this post I'll add, for each method, the complete implementation – includes try-catch block and method calls the to intercepting library that will in turn propagate the call to all the registered advices based on the pointcusts.

What do I need in order to accomplish this? Well, I saw how to call methods, I'm missing the try catch part, the pointcuts and generics. I'll leave generics for the last part of this series.

All the advices are managed using AdviceBroker class, so we need the generated class to call for the Begin, End and Error parts of the AdviceBroker.

We need to take the following code:

   1: public override void Method2()
   2: {
   3:     this.m_instance.Method2();
   4: }

And wrap it as the following code:

   1: public override void Method2()
   2: {
   3:     //We need the AdviceBroker and the current method
   4:     IAdvice instance = AdviceBroker.Instance;
   5:     MethodBase currentMethod = MethodBase.GetCurrentMethod();
   6:  
   7:     object[] parameters = new object[0];
   8:     try
   9:     {
  10:         //the Begin action
  11:         instance.BeforeMethodCall(this, this.m_instance, currentMethod, parameters);
  12:  
  13:         //the call to the original class
  14:         this.m_instance.Method2();
  15:  
  16:         //the End action
  17:         instance.AfterMethodCall(this, this.m_instance, currentMethod, parameters);
  18:     }
  19:     catch (Exception exception)
  20:     {
  21:         //if we wan't we can "swallow" the exception using the advice code for errors
  22:         if (instance.ErrorMethodCall(this, this.m_instance, currentMethod, exception))
  23:         {
  24:             throw;
  25:         }
  26:     }
  27: }

This transformation needed to be applied to all the methods and properties of the proxy class (remember what I told you about properties?).

In IL you declare local variables just be type (and using the index as the accessor to the variable).

I abstracted all the variable types to IVariable interface based classes.  So you can DOWNLOAD and browse around (for the local variable see implementation of LocalVariable class).

Let's just see how it's done using IL Helper class I've created.

The code bellow will emit MethodBase currentMethod = MethodBase.GetCurrentMethod();

   1: using (IGenerationContext context = ilHelper.CreateMethod(typeBuilder, methodToCreate))
   2: {
   3:     //Create local variable type of MethodBase
   4:     IVariable m_MethodBase = ilHelper.CreateLocalVariable(typeof(MethodBase));
   5:  
   6:     //m_MethodBase = MethodBase.GetCurrentMethod()
   7:     ilHelper.Invoke(
   8:             m_MethodBase,
   9:             null,
  10:             METHOD_BASE_TYPE.GetMethod("GetCurrentMethod"));
  11: ....
  12: }

The Invoke method has the following signature:

   1: /// <summary>
   2: /// Invokes method
   3: /// </summary>
   4: /// <param name="setTo">optional set target</param>
   5: /// <param name="invokeFrom">which variable's method to invoke optional for static methods</param>
   6: /// <param name="methodToInvoke"></param>
   7: /// <param name="parametersToPushToTheStack"></param>
   8: /// <returns></returns>
   9: public Helper Invoke(
  10:                         IVariable setTo,
  11:                         IVariable invokeFrom, 
  12:                         MethodBase invoke, 
  13:                         params IVariable[] parametersToPushToTheStack)
  14: { ... }

It makes all the heavy lifting and pushes all the variables to the stack before calling the required method and setting the method result to a target variable.

Note: invokeFrom is only for non static methods.

So, in order to create a full method proxy the code below should be used (the TryEmmiter and CatchEmmiter are delegates to method that responsible on generating the try and catch blocks respectively).

   1: //create method signature
   2: using (IGenerationContext context = ilHelper.CreateMethod(typeBuilder, methodToCreate))
   3: {
   4:     IVariable m_IInjectedLogic = ilHelper.CreateLocalVariable(typeof(IAdvice));
   5:     IVariable m_MethodBase = ilHelper.CreateLocalVariable(typeof(MethodBase));
   6:     IVariable m_parametersArray = ilHelper.CreateLocalVariable(typeof(object[]));
   7:  
   8:     //m_IInjectedLogic = AdviceBroker.Instance;
   9:     ilHelper.Invoke(
  10:         m_IInjectedLogic,
  11:         null,
  12:         typeof(AdviceBroker).GetMethod("get_Instance"));
  13:  
  14:     //m_MethodBase = MethodBase.GetCurrentMethod()
  15:     ilHelper.Invoke(
  16:         m_MethodBase,
  17:         null,
  18:         METHOD_BASE_TYPE.GetMethod("GetCurrentMethod"));
  19:  
  20:     //object[] m_parametersArray = new object[...];
  21:     ilHelper.CreateArray(
  22:         m_parametersArray,
  23:         typeof(object),
  24:         methodToCreate.GetParameters().Length);
  25:  
  26:     //begin try block
  27:     IVariable m_return;
  28:     ilHelper.BeginTry(m_parametersArray,
  29:             TryEmmiter, /*delegate to try generator*/);
  30:             out m_return);
  31:  
  32:     //end try and begin catch block
  33:     ilHelper.EndTryBeginCatch(typeof(Exception), 
  34:                 CatchEmmiter/*delegate to catch generator*/);
  35:     ilHelper.EndCatch();
  36:  
  37:     //return m_return;
  38:     ilHelper.Return( m_return);
  39: }

To see the complete code of the emitters see ProxyGenerator class.

 

That's it for this time as before you can download the relevant change set (36217) or browse the code in this project's codeplex site .

Next time generics support.

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים

2 תגובות

  1. Eyal17 באוגוסט 2008 ב 2:59

    I know that there are more AOP frameworks see my post on AOP and search for the word framework in it.

    The reason for this article is purly academic.

    Although I have and Idea for a feature that all the frameworks missing.

    להגיב