January 17, 2021
Hot Topics:

Speed Up Your Reflection Processes

  • By Paul Kimmel
  • Send Email »
  • More Articles »

Emitting Lines of Code

Basically what the emitter has to do is emit an algorithm that reads roughly like the following pseudocode:

For each item in list

   Write property1's name and property1's value

   Write property2's name and property2's value

Next item

Of course, this is very easy with VB code, but if you did that you'd have to revert to pure reflection or write the lines of code yourself. The code in Listing 7 writes that code for you but in MSIL (Microsoft Intermediate Language, which is an implementation of the standard CIL (Common Intermediate Language). MSIL is like assembly language, so you have to do it in little itty bitty pieces. The lines of code are numbered for reference only to assist with the long listing.

Listing 7: Some code that iterates over each property and writes the lines of MSIL to display the property name and value.

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

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

 7. generator.Emit(OpCodes.Nop)
 8. generator.Emit(OpCodes.Nop)
 9. ' Try
10. generator.BeginExceptionBlock()
11. generator.Emit(OpCodes.Ldarg_0)
12. generator.EmitCall(OpCodes.Callvirt, enumer, Nothing)
13. generator.Emit(OpCodes.Stloc_1)
14. Dim IL_000b As Label = generator.DefineLabel()
15. Dim IL_003f As Label = generator.DefineLabel()
16. generator.Emit(OpCodes.Br, IL_003f)    ' BR_S is wrong
17. generator.MarkLabel(IL_000b)
18. generator.Emit(OpCodes.Ldloc_1)
19. generator.EmitCall(OpCodes.Callvirt, get_Current, Nothing)
20. ' added this
21. generator.Emit(OpCodes.Castclass, GetType(T))
22. generator.Emit(OpCodes.Stloc_0)

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

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

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

37. generator.Emit(OpCodes.Nop)
38. generator.MarkLabel(IL_003f)
39. generator.Emit(OpCodes.Ldloc_1)
40. generator.EmitCall(OpCodes.Callvirt, MoveNext, Nothing)
41. generator.Emit(OpCodes.Stloc_2)
42. generator.Emit(OpCodes.Ldloc_2)
43. ' Brtrue_S is wrong
44. generator.Emit(OpCodes.Brtrue, IL_000b)
45. generator.Emit(OpCodes.Nop)
46. Dim IL_0060 As Label = generator.DefineLabel()
47. generator.Emit(OpCodes.Leave_S, IL_0060)
48. ' begin finally
49. generator.BeginFinallyBlock()
50. generator.Emit(OpCodes.Ldloc_1)
51. generator.Emit(OpCodes.Ldnull)
52. generator.Emit(OpCodes.Ceq)
53. generator.Emit(OpCodes.Ldc_I4_0)
54. generator.Emit(OpCodes.Ceq)
55. generator.Emit(OpCodes.Stloc_2)
56. generator.Emit(OpCodes.Ldloc_2)
57. Dim IL_005e As Label = generator.DefineLabel
58. generator.Emit(OpCodes.Brfalse_S, IL_005e)
59. generator.Emit(OpCodes.Ldloc_1)
60. Dim dispose As MethodInfo = _
    GetType(System.IDisposable).GetMethod("Dispose", New Type() {})
61. generator.EmitCall(OpCodes.Callvirt, dispose, Nothing)
62. generator.Emit(OpCodes.Nop)
63. generator.MarkLabel(IL_005e)
64. generator.Emit(OpCodes.Nop)
65. generator.EndExceptionBlock()
66. generator.MarkLabel(IL_0060)
67. generator.Emit(OpCodes.Nop)
68. generator.Emit(OpCodes.Ret)

Line 2 gets all of the properties for type T, which in the example is a Customer. Line 3 requests an ILGenerator from the MethodBuilder. The ILGenerator will convert the symbolic OpCodes to actual MSIL.

Lines 4, 5, and 6 define local variables for the dynamic method. There is the local variable representing a Customer object, an instance of IEnumerator(Of T), and a Boolean.

Lines 7 and 8 generate a Nop (pronounced no-op). These placeholders do nothing other than add one byte padding. I believe, although on this I could be wrong (sorry), their purpose is to add a little padding so instructions break on even 32-bit boundaries, which helps with moving the instruction pointer and other registers to locations that match the size of microprocessor registers.

Page 4 of 5

This article was originally published on October 28, 2008

Enterprise Development Update

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

Thanks for your registration, follow us on our social networks to keep up-to-date