December 20, 2014
Hot Topics:

Speed Up Your Reflection Processes

  • October 28, 2008
  • By Paul Kimmel
  • Send Email »
  • More Articles »

Implementing a Dynamic Emitter Assembly

The problem is defined as not wanting to write a ToString override for every object where you manually write the code that dumps and object's state. Further, if your code uses Reflection to dump an object's state, it's an improvement because you only have to write it one time, but if it's called a lot your application's performance will suffer. Our solution is to write an emitter that acts like the fast hard-coded version but you only have to write it one time because you are writing an emitter that generates the equivalent of the hard-coded version.

Listing 3 contains the complete codification of the emitter. Generally, long methods are broken up into pieces, but in the case of emitters and code generators they are easier to follow if they are listed consecutively.

Listing 3: An emitter that generates a dynamic assembly that loops through a list and dumps the state of each object.

Imports System.Reflection
Imports System.IO
Imports System.Runtime.CompilerServices
Imports System.Reflection.Emit

Module DynamicAssemblyCreator


   Public Sub CreateAssembly(Of T)(ByVal list As IEnumerable(Of T))

   Dim FunctionName As String = "Dump" + GetType(T).Name

   Dim name As AssemblyName = New AssemblyName()
   name.Name = GetType(T).Name + "Dumper"
   Dim domain As AppDomain = System.Threading.Thread.GetDomain()
   Dim builder As AssemblyBuilder = _
      domain.DefineDynamicAssembly(name, _
      AssemblyBuilderAccess.RunAndSave)
   Dim moduleBuilder As ModuleBuilder = _
      builder.DefineDynamicModule(GetType(T).Name + _
      "Dumper.dll", True)
   Dim typeBuilder As TypeBuilder = _
      moduleBuilder.DefineType("MyType", _
      TypeAttributes.Public Or TypeAttributes.Class)

   Dim methodBuilder As MethodBuilder = _
      typeBuilder.DefineMethod(FunctionName, _
      MethodAttributes.Static Or MethodAttributes.Public)


   methodBuilder.SetParameters(GetType(IEnumerable(Of T)))
   Dim parmBuilder As ParameterBuilder = _
      methodBuilder.DefineParameter( _
      1, ParameterAttributes.In, "list")

   ' Some methods
   Dim write As MethodInfo = _
      GetType(Console).GetMethod("WriteLine", _
      New Type() {GetType(String)})

   Dim concat As MethodInfo = _
      GetType(System.String).GetMethod("Concat", _
      New Type() {GetType(String), GetType(String)})

   Dim MoveNext As MethodInfo = _
      GetType(System.Collections.IEnumerator).GetMethod("MoveNext", _
      New Type() {})

   Dim enumer As MethodInfo = _
      GetType(IEnumerable(Of T)).GetMethod("GetEnumerator", _
      New Type() {})

   Dim get_Current As MethodInfo = _
      GetType(System.Collections.IEnumerator).GetMethod("get_Current")

      ' get the properties and use them to build writeline statements
      Dim properties() As PropertyInfo = GetType(T).GetProperties()
      Dim generator As ILGenerator = methodBuilder.GetILGenerator()

      ' loc 1
      Dim localT As LocalBuilder = generator.DeclareLocal(GetType(T))
      ' loc 2
      Dim localI As LocalBuilder = _
         generator.DeclareLocal(GetType(IEnumerator(Of T)))
      'loc 3
      Dim localB As LocalBuilder = _
         generator.DeclareLocal(GetType(Boolean))

      generator.Emit(OpCodes.Nop)
      generator.Emit(OpCodes.Nop)
      ' Try
      generator.BeginExceptionBlock()
      generator.Emit(OpCodes.Ldarg_0)
      generator.EmitCall(OpCodes.Callvirt, enumer, Nothing)
      generator.Emit(OpCodes.Stloc_1)
      Dim IL_000b As Label = generator.DefineLabel()
      Dim IL_003f As Label = generator.DefineLabel()
      generator.Emit(OpCodes.Br, IL_003f)    ' BR_S is wrong
      generator.MarkLabel(IL_000b)
      generator.Emit(OpCodes.Ldloc_1)
      generator.EmitCall(OpCodes.Callvirt, get_Current, Nothing)
      ' added this
      generator.Emit(OpCodes.Castclass, GetType(T))
   generator.Emit(OpCodes.Stloc_0)

      ' write each property
      For Each prop As PropertyInfo In properties
         generator.Emit(OpCodes.Ldstr, prop.Name + "=")
         generator.Emit(OpCodes.Ldloc_0)
         Dim get_Prop As MethodInfo = _
         GetType(T).GetMethod("get_" + prop.Name, New Type() {})
         generator.EmitCall(OpCodes.Callvirt, get_Prop, Nothing)

         ' Handle value types that need to be boxed for ToString
         If (prop.PropertyType.IsValueType) Then
            generator.Emit(OpCodes.Box, prop.PropertyType)
         End If

         generator.Emit(OpCodes.Callvirt, _
            GetType(Object).GetMethod("ToString", New Type() {}))
         generator.Emit(OpCodes.Call, concat)
         generator.Emit(OpCodes.Call, write)
         generator.Emit(OpCodes.Nop)
      Next

      generator.Emit(OpCodes.Nop)
      generator.MarkLabel(IL_003f)
      generator.Emit(OpCodes.Ldloc_1)
      generator.EmitCall(OpCodes.Callvirt, MoveNext, Nothing)
      generator.Emit(OpCodes.Stloc_2)
      generator.Emit(OpCodes.Ldloc_2)
      ' Brtrue_S is wrong
      generator.Emit(OpCodes.Brtrue, IL_000b)
      generator.Emit(OpCodes.Nop)
      Dim IL_0060 As Label = generator.DefineLabel()
      generator.Emit(OpCodes.Leave_S, IL_0060)
      ' begin finally
      generator.BeginFinallyBlock()
      generator.Emit(OpCodes.Ldloc_1)
      generator.Emit(OpCodes.Ldnull)
      generator.Emit(OpCodes.Ceq)
      generator.Emit(OpCodes.Ldc_I4_0)
      generator.Emit(OpCodes.Ceq)
      generator.Emit(OpCodes.Stloc_2)
      generator.Emit(OpCodes.Ldloc_2)
      Dim IL_005e As Label = generator.DefineLabel
      generator.Emit(OpCodes.Brfalse_S, IL_005e)
      generator.Emit(OpCodes.Ldloc_1)
      Dim dispose As MethodInfo = _
         GetType(System.IDisposable).GetMethod("Dispose", New Type() {})
      generator.EmitCall(OpCodes.Callvirt, dispose, Nothing)
      generator.Emit(OpCodes.Nop)
      generator.MarkLabel(IL_005e)
      generator.Emit(OpCodes.Nop)
      generator.EndExceptionBlock()
      generator.MarkLabel(IL_0060)
      generator.Emit(OpCodes.Nop)
      generator.Emit(OpCodes.Ret)

      Dim test As Type = typeBuilder.CreateType()

      builder.Save(GetType(T).Name + "Dumper.dll")
      test.GetMethod(FunctionName).Invoke(Nothing, New Object() {list})

   End Sub

EndModule

In the remaining sections of the article, the emitter is deeconstructed, permitting you to understand what is going on and write your own emitters for new solutions to existing problems.





Page 2 of 5



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel