February 2009 - Posts

Linear Gradient Fill example for MASM

This is an old example I've created for MASM, which was posted in MASM32 forum 2 years ago:
http://www.masm32.com/board/index.php?topic=5944.0

It has a simple approach of double buffering and working with MSIMG32 for the gradient fills.

If you're into drawing in windows (Win32), and you like to program in assembly language; this is example is for you.
Download the source from attachment, or copy the source codes below.


Cheers,

-chris

LinearGradient.asm


.386
.model flat, stdcall
option casemap:none
option proc:private

include windows.inc

include user32.inc
include kernel32.inc
include gdi32.inc
include msimg32.inc

includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
includelib msimg32.lib

include MsgCrack.inc

; ==========================================================
; Protos
; ==========================================================
WinMain proto :HINSTANCE, :HINSTANCE, :LPSTR, :dword
WndProc proto :HWND, :UINT, :WPARAM, :LPARAM
DrawFancyDivider proto :HDC, :ptr RECT

; ==========================================================
; MessageHandlers Protos
; ==========================================================
WmDestroy proto :HWND
WmCommand proto :HWND, :word, :HWND, :word
WmPaint proto :HWND

option dotname

; ==========================================================
; Local Macros
; ==========================================================
m2m    macro mem32A, mem32B
    push    mem32B
    pop        mem32A
endm

func macro functionName:req, functionParams:vararg
    invoke    functionName, functionParams
    exitm    <eax>
endm

; ==========================================================
; Data
; ==========================================================
.data?
    hInstance    dd ?
    hFont        HFONT ?
   
.data
    szClassName db "ChrisVega.WinLion", 0
    szWndText    db "Win32 Assembly ROCK!", 0
    lgFont        LOGFONT <-19, 0, 0, 0, 400, 0, 0, 0, 0, 0, 0, 0, 0, "Arial">
   
    szText        db "Linear GradientFill sample program by Chris Vega", 0
    szTextLen    dd $-szText-1

.const
    WND_WIDTH    = 540
    WND_HEIGHT    = 180
   
    mnuFileExit = 2001
    mnuHelpAbout= 3001

; ==========================================================
; Entry Point
; ==========================================================
.code
start:
    invoke    WinMain, func(GetModuleHandle, 0), NULL, NULL, SW_SHOW
    invoke    ExitProcess, 0
   
; ==========================================================
; WinMain
; ==========================================================
WinMain proc uses edi esi hInst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:dword

    local    hwnd:HWND   
    local    msg:MSG
    local    hMenu:HANDLE
    local    wc:WNDCLASSEX
   
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    ; Register the class
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    m2m        wc.hInstance, hInst
    mov        wc.lpszClassName, offset szClassName
    mov        wc.lpfnWndProc, offset WndProc
    mov        wc.style, CS_PARENTDC or CS_HREDRAW or CS_VREDRAW
    mov        wc.cbSize, sizeof WNDCLASSEX
    mov        wc.hIcon, func(LoadIcon, hInst, 100)
    mov        wc.hIconSm, eax
    mov        wc.hCursor, func(LoadCursor, NULL, IDC_ARROW)
    mov        wc.lpszMenuName, NULL
    mov        wc.cbClsExtra, 0
    mov        wc.cbWndExtra, 0
    mov        wc.hbrBackground, NULL
    .if        !func(RegisterClassEx, addr wc)
            ret
    .endif

    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    ; Create the window
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    mov        edi, func(GetSystemMetrics, SM_CXSCREEN)
    shr        edi, 1
    mov        eax, WND_WIDTH
    shr        eax, 1
    sub        edi, eax
   
    mov        esi, func(GetSystemMetrics, SM_CYSCREEN)
    shr        esi, 1
    mov        eax, WND_HEIGHT
    shr        eax, 1
    sub        esi, eax

    invoke    CreateWindowEx,\
                WS_EX_APPWINDOW or WS_EX_WINDOWEDGE, \
                addr szClassName,\
                addr szWndText,\
                   WS_OVERLAPPEDWINDOW,\
                   edi,\
                   esi,\
                   WND_WIDTH,\
                   WND_HEIGHT,\
                   NULL,\
                   func(LoadMenu, hInst, 1000),\
                   hInst,\
                   NULL
    mov        hwnd, eax
   
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    ; Set the window font
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    mov        hFont, func(CreateFontIndirect, addr lgFont)

    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    ; Make the window visible
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    invoke    SendMessage, hwnd, WM_SETICON, ICON_BIG or ICON_SMALL, func(LoadIcon, hInst, 1000)
    invoke    ShowWindow, hwnd, CmdShow
    invoke    UpdateWindow, hwnd
   
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    ; Messageloop (with IsDialogMessage)
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    .while TRUE
        invoke GetMessage, addr msg, hwnd, 0, 0
        .break .if eax!=TRUE
        .if !func(IsDialogMessage,hwnd,addr msg)
            invoke    TranslateMessage, addr msg
            invoke    DispatchMessage, addr msg
        .endif
    .endw
   
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    ; Exit WinMain
    ; -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    mov eax, msg.wParam
    ret
   
WinMain endp

; ==========================================================
; Window Procedure
; ==========================================================
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   
    .handle_msg_begin
        .handle_msg WM_DESTROY, WmDestroy
        .handle_msg WM_COMMAND, WmCommand
        .handle_msg WM_PAINT, WmPaint
    .handle_msg_end
   
    invoke    DefWindowProc, hWnd, uMsg, wParam, lParam
    ret

WndProc endp

; ==========================================================
; DrawFancyDivider
; ==========================================================
DrawFancyDivider proc uses ebx edi hdc:HDC, lpRect:ptr RECT
    local    pen:HPEN
    local    pen2:HPEN
   
    mov        ebx, lpRect
    assume    ebx: ptr RECT
   
    invoke    CreatePen, PS_SOLID, 1, 00404040h
    mov        pen, eax
   
    invoke    CreatePen, PS_SOLID, 1, 00707070h
    mov        pen2, eax
   
    invoke    SelectObject, hdc, pen
    push    eax
   
    mov        edi, [ebx].top
    add        edi, 3
    invoke    MoveToEx, hdc, [ebx].left, edi, NULL
    invoke    LineTo, hdc, [ebx].right, edi
    dec        edi
    invoke    SelectObject, hdc, pen2
    invoke    MoveToEx, hdc, [ebx].left, edi, NULL
    invoke    LineTo, hdc, [ebx].right, edi
    sub        edi, 2
    invoke    SelectObject, hdc, pen2
    invoke    MoveToEx, hdc, [ebx].left, edi, NULL
    invoke    LineTo, hdc, [ebx].right, edi
    inc        edi
    invoke    SelectObject, hdc, func(GetStockObject, WHITE_PEN)
    invoke    MoveToEx, hdc, [ebx].left, edi, NULL
    invoke    LineTo, hdc, [ebx].right, edi
   
    invoke    DeleteObject, pen
    invoke    DeleteObject, pen2
   
    pop        eax
    invoke    SelectObject, hdc, eax

    ret
DrawFancyDivider endp

; ==========================================================
; WmDestroy : Handles WM_DESTROY message
; ==========================================================
WmDestroy proc hWnd:HWND
    invoke    PostQuitMessage, 0
    ret
WmDestroy endp

; ==========================================================
; WmCommand : Handles WM_COMMAND message
; ==========================================================
WmCommand proc hWnd:HWND, id:word, hwndCtl:HWND, codeNotify:word

    ; ----------------------------------------------
    .if id==mnuFileExit
   
    push    hWnd
    call    WmDestroy

    ; ----------------------------------------------
    .elseif id==mnuHelpAbout

    invoke    MessageBox, hWnd, addr szText, addr szWndText, MB_ICONINFORMATION

    ; ----------------------------------------------
    .endif
   
    ret
WmCommand endp

; ==========================================================
; WmPaint : Handles WM_PAINT message
; ==========================================================
WmPaint proc uses ebx hWnd:HWND

    local hdc:HDC
    local rect:RECT
    local sz:SIZEL
   
    local memDC:HDC
    local memBM:HBITMAP
   
    local mesh:GRADIENT_RECT
    local vertex[3]:TRIVERTEX
   
    ; ---------------------------------------------------------------------
    ; Get the handle to the device context
    ; ---------------------------------------------------------------------
    mov        hdc, func(GetDC, hWnd)
   
    ; ---------------------------------------------------------------------
    ; Get the client rectangle
    ; ---------------------------------------------------------------------
    invoke    GetClientRect, hWnd, addr rect
   
    ; ---------------------------------------------------------------------
    ; Create an off-screen device context (double buffering) [1]
    ; ---------------------------------------------------------------------
    mov        memDC, func(CreateCompatibleDC, hdc)
    mov        memBM, func(CreateCompatibleBitmap, hdc, rect.right, rect.bottom)
    invoke    SelectObject, memDC, memBM
   
    ; ---------------------------------------------------------------------
    ; Populate vertex 1 structure
    ; ---------------------------------------------------------------------
    lea        ebx, vertex
    assume    ebx: ptr TRIVERTEX
    m2m        [ebx].x, rect.left
    m2m        [ebx].y, rect.top
    mov        [ebx].Alpha, 0
    mov        [ebx].Red, 07d00h
    mov        [ebx].Green, 0aa00h
    mov        [ebx].Blue, 0c300h
   
    ; ---------------------------------------------------------------------
    ; Populate vertex 2 structure
    ; ---------------------------------------------------------------------
    add        ebx, sizeof TRIVERTEX
    m2m        [ebx].x, rect.right
    m2m        [ebx].y, rect.bottom
    mov        [ebx].Alpha, 0
    mov        [ebx].Red, 00100h
    mov        [ebx].Green, 02a00h
    mov        [ebx].Blue, 04400h
   
    ; ---------------------------------------------------------------------
    ; Set the mesh (gradient rectangle) point
    ; ---------------------------------------------------------------------
    mov        mesh.UpperLeft, 0
    mov        mesh.LowerRight, 1
   
    ; ---------------------------------------------------------------------
    ; Call GradientFill function
    ; ---------------------------------------------------------------------
    invoke    GradientFill, memDC, addr vertex, 2, addr mesh, 1, GRADIENT_FILL_RECT_V
   
    ; ---------------------------------------------------------------------
    ; Draw the text, centered to the window
    ; ---------------------------------------------------------------------
    invoke    SetBkMode, memDC, TRANSPARENT
    invoke    SelectObject, memDC, hFont
    invoke    GetTextExtentPoint32, memDC, addr szText, szTextLen, addr sz
    invoke    SetTextColor, memDC, 0
    mov        eax, sz.x
    shr        eax, 1
    mov        sz.x, eax
    mov        eax, sz.y
    shr        eax, 1
    mov        sz.y, eax
    mov        eax, rect.right
    sub        eax, rect.left
    shr        eax, 1
    sub        eax, sz.x
    mov        edx, rect.bottom
    sub        edx, rect.top
    shr        edx, 1
    sub        edx, sz.y
    push    eax
    push    edx
    invoke    TextOut, memDC, eax, edx, addr szText, szTextLen
    invoke    SetTextColor, memDC, 00f0f0f0h
    pop        edx
    pop        eax
    sub        edx, 2
    sub        eax, 2
    invoke    TextOut, memDC, eax, edx, addr szText, szTextLen
   
    ; ---------------------------------------------------------------------
    ; Draw some fancy divider
    ; ---------------------------------------------------------------------
    invoke    DrawFancyDivider, memDC, addr rect
   
    ; ---------------------------------------------------------------------
    ; Flush the result to the window
    ; ---------------------------------------------------------------------
    invoke    BitBlt, hdc, 0, 0, rect.right, rect.bottom, memDC, 0, 0, SRCCOPY
   
    ; ---------------------------------------------------------------------
    ; Release all the resources we've used
    ; ---------------------------------------------------------------------
    invoke    DeleteDC, memDC
    invoke    DeleteObject, memBM

    ; ---------------------------------------------------------------------
    ; Release the device context
    ; ---------------------------------------------------------------------
    invoke    ReleaseDC, hWnd, hdc
   
    ; Notes:
    ;    [1] We need to create an off-screen device context for this demo,
    ;        this is because filling a rectangle with GradientFill function
    ;        will take some time to complete, and shows a lot of flickering
    ;        if we let the function use the window's DC.
    ret
WmPaint endp


end start

Posted by cvega with 1 comment(s)
Filed under: ,

Using CIL to set/get values from fields and properties

Background:

I'm currently working on a freelance project to optimize (yay, optimization again!) a .NET Entity Persister library of a Software Development firm here in Makati City -- much like a custom built NHibernate, but heavily dependent on reflection to get/set field values (their library has no support for Properties). They hired me because they kind of complaining that their system is so slow, it is taking about several seconds (to minutes) just to retrieve some thousand records -- considering the internals of their library is built on top of DataReader (Imagine, looping DataReader to fill a collection of Entities). According to their big boss, using thrid party O/R solution is not an option, they have to put a fix to their current library.

Looking at the problem:

Assuming we have an entity class SampleEntity:

  public class SampleEntity {
    public string stringField = "Sample Value";
    public string StringProperty { get; set; }
   }

and here's how they are setting the field:

  /// <summary>

  /// Dynamic field setter

  /// </summary>

  public class FieldSetter {

    /// <summary>

    /// Set field dynamically

    /// </summary>

    public static void SetField(object instance,
                               string fieldName, object value) {

      var t = instance.GetType();

      var fieldInfo = t.GetField(fieldName,
                                 BindingFlags.Instance | BindingFlags.Public);

      if (fieldInfo == null)

        throw new ArgumentException("There is no publicly accessible " +

                                    fieldName +
                                    " field found in " + t.FullName + ".");

      fieldInfo.SetValue(instance, value);

    }

  }

the library is actually doing this in loop to setup an entire entity instance, and they repeatedly doing it until the DataReader says stop:

  /// <summary>

  /// Get all entities from underlying data store.

  /// </summary>

  public List<T> SelectAll() {

    Open();

    var selectAll = new SqlCommand(BuildSelectStatement<T>(), Connection);

    var reader = selectAll.ExecuteReader();

    var list = new List<T>();

    if (reader != null)

      while (reader.Read()) {

        var entity = default(T);

        for (var i = 0; i < reader.FieldCount; i++) {

          var fieldName = reader.GetName(i);

          var data = reader.GetData(i);

          FieldSetter.SetField(entity, fieldName, data);

        }

      }

    Close();

    return list;

  }

the only problem I've notice that may be the cause of slow execution is the use of reflection.

Doing it the hard way, and maybe the faster way:

If you play long enough with reflection, you will get the experience that it is a slow because of internal loop overheads (search for matching name and binding attributes).

The approach I thought to workaround the problem is to emit CIL codes directly and call them as dynamic method, something that look like:

   public void SetField(SampleEntity instance, string value) {
     instance.stringField = value;
   }

In CIL, this is how it is coded:

  .method public hidebysig instance void SetField(
          class SampleEntity instance, string 'value') cil managed

  {

    .maxstack 8

    SEG_0001: 0x0003  ldarg.0

    SEG_0002: 0x0004  ldarg.1

    SEG_0003: 0x007   stfld string SampleEntity::stringField

    SEG_0008: 0x002   ret

  }

The lines I highlighted in red are the lines I'm interrested in making dynamic, so let's work on a DynamicMethod for doing just that:

  // We still need to get the field information from reflection

  var fieldInfo = t.GetField(fieldName,
                             BindingFlags.Instance | BindingFlags.Public);

  if (fieldInfo == null)

    throw new ArgumentException("There is no publicly accessible " +
                                fieldName +
                                " field found in " + t.FullName + ".");

 

We only need to get the field information once, during the setup of our DynamicMethod:

  var setter = new DynamicMethod("__set_field_" + fieldName,
                                 null,
                                 new[] { t, fieldInfo.FieldType },
                                 typeof(FieldSetter));

  var setterIL = setter.GetILGenerator();


This is because we need the FieldInfo as a parameter to stfld:

  // ldarg.0 - load argument 1 to eval.stack.0
  setterIL.Emit(OpCodes.Ldarg_0);

   
// ldarg.1 - load argument 2 to eval.stack.1

  setterIL.Emit(OpCodes.Ldarg_1);      

  // stfld f - set the field f of instance stack.0 
  //           with the value from stack.1  

  setterIL.Emit(OpCodes.Stfld, fieldInfo); 
         
  // ret     - exit the method                                
  setterIL.Emit(OpCodes.Ret);               


If you take a closer look at the emitted OpCodes, you'll see that it is simply a replica of CIL above, but with a little twist --  the fieldInfo we've taken from given fieldName.

 

That means we are almost done, we just have to call the DynamicMethod we've created for assigning value to field like this:

 

  setter.Invoke(null,
         new object[] { t,
                        "Kamusta mundo, ok ka lang ba?" });

Saving it for succeeding calls:

There's not much improvement at all if we are going to create DynamicMethod repeatedly, in fact that's even slower than using reflection. We have to save the created DynamicMethods for succeeding calls.


Saving it to Dictionary and then retrieving it every time we have a need for it, will boost the performance real fast (because we only have to create DynamicMethod once for every field we want to set value).

 

Summing it all together, with addition of controlling the creation of setter DynamicMethods, we will be able to write a new FieldSetter class:

  public class FieldSetter {

    public static DynamicMethod GetFieldSetterMethod(object instance, string fieldName) {

      var t = instance.GetType();

      var key = (t.FullName + "_" + fieldName).Replace(".", "_");

      if (!fieldSetter.ContainsKey(key)) {

        lock (fsync) {

          // We still need to get the field information from reflection

          var fieldInfo = t.GetField(fieldName,
                          BindingFlags.Instance | BindingFlags.Public);

          if (fieldInfo == null)

            throw new ArgumentException("There is no publicly accessible " +
                                        fieldName +
                                        " field found in " + t.FullName + ".");

          var setter = new DynamicMethod("__set_field_" + key, null,
                                         new[] { t, fieldInfo.FieldType },
                                         typeof(FieldSetter));

          var setterIL = setter.GetILGenerator();

 

          // ldarg.0 - load argument 1 to eval.stack.0

          setterIL.Emit(OpCodes.Ldarg_0);          

         

          // ldarg.1 - load argument 2 to eval.stack.1
          setterIL.Emit(OpCodes.Ldarg_1);          

         
          // stfld f - set the field f of instance stack.0
          //           with the value from stack.1

          setterIL.Emit(OpCodes.Stfld, fieldInfo); 

                      
          // ret     - exit the method                             

          setterIL.Emit(OpCodes.Ret);              

 

          fieldSetter.Add(key, setter);

        }

      }

      return fieldSetter[key];

    }

 

    /// <summary>

    /// Set the field value of given instance, using the name of the field.

    /// </summary>

    /// <param name="instance">The instance.</param>

    /// <param name="fieldName">The name of the field.</param>

    /// <param name="value">The value to set.</param>

    public static object SetFieldValue(object instance,
                                       string fieldName,
                                       object value) {

      return GetFieldSetterMethod(instance, fieldName)
             .Invoke(null, new[] { instance });

    }

 

    private static readonly Dictionary<string, DynamicMethod> fieldSetter =
                        new Dictionary<string, DynamicMethod>();

    private static readonly object fsync = new object();

  }

That's about it; I hope you guys find it useful for your own purpose.

Did it worked?

After I've delivered the code to them, they immediately tested it.., and tested it.., and tested it more. Long story short, I guess I made them happy because I've got an extra from the paycheck, and the another deal to support property on their custom EntityPerstister.

I'll be working more with their library, specifically the adding of support to Property and Method with one parameter. That's not much of a work left, and I think I almost got everything (except for the method with one parameter, but it’s practically the same as Property since properties are methods too!).


More codes:

Here, take a look at the classes I've put-up for setting/getting field and property (if you don't know how I’ve implemented it, you can re-read this post, or read more about CIL and DynamicMethod):

// FieldSetter.cs

 

  public class FieldSetter {

    private static DynamicMethod GetFieldSetterMethod(
                                 object instance, string fieldName) {

      var t = instance.GetType();

      var key = (t.FullName + "_" + fieldName).Replace(".", "_");

      if (!fieldSetter.ContainsKey(key)) {

        lock (fsync) {

          // We still need to get the field information from reflection

          var fieldInfo = t.GetField(fieldName,
                                     BindingFlags.Instance |
                                     BindingFlags.Public);

          if (fieldInfo == null)

            throw new ArgumentException("There is no publicly " +                  
                                       
"accessible " +
                                        fieldName +
                                        " field found in " +
                                        t.FullName + ".");

          var setter = new DynamicMethod("__set_field_" + key,
                       null, new[] { t, fieldInfo.FieldType },
                       typeof(FieldSetter));

          var setterIL = setter.GetILGenerator();

 

          // ldarg.0 - load argument 1 to eval.stack.0

          setterIL.Emit(OpCodes.Ldarg_0);          

 

          // ldarg.1 - load argument 2 to eval.stack.1
          setterIL.Emit(OpCodes.Ldarg_1);          

         

          // stfld f - set the field f of instance stack.0
          //           with the value from stack.1

          setterIL.Emit(OpCodes.Stfld, fieldInfo); 

 

          // ret     - exit the method                               

          setterIL.Emit(OpCodes.Ret);              

 

          fieldSetter.Add(key, setter);

        }

      }

      return fieldSetter[key];

    }

 

    public static void SetFieldValue(
                 object instance, string fieldName, object value) {

      GetFieldSetterMethod(instance, fieldName)
                .Invoke(null, new[] { instance, value });

    }

 

    private static readonly Dictionary<string, DynamicMethod>
            fieldSetter = new Dictionary<string, DynamicMethod>();

    private static readonly object fsync = new object();

  }

 

// FieldGetter.cs

 

  public class FieldGetter {

    private static DynamicMethod GetFieldGetterMethod(
                            object instance, string fieldName) {

      var t = instance.GetType();

      var key = (t.FullName + "_" + fieldName).Replace(".", "_");

      if (!fieldGetters.ContainsKey(key)) {

        lock (fsync) {

          var fieldInfo = t.GetField(fieldName,
                            BindingFlags.Instance |
                            BindingFlags.Public);

          if (fieldInfo == null)

            throw new ArgumentException("There is no publicly " + 
                                        "accessible "
+ fieldName +
                                        " field found in " +
                                        t.FullName + ".");

          var getter = new DynamicMethod("__get_field_" + key,
                             fieldInfo.FieldType, new[] { t },
                             typeof(FieldGetter));

          var getterIL = getter.GetILGenerator();

 

          // ldarg.0  - load argument 0 to eval.stack.0

          getterIL.Emit(OpCodes.Ldarg_0);   

 

          // ldfld f  - get the value of field from
          // instance in eval.stack
 and store it
          // on top of eval.stack
                      

          getterIL.Emit(OpCodes.Ldfld, fieldInfo); 

          // ret - return what's on top of eval.stack           
          getterIL.Emit(OpCodes.Ret);              

 

          fieldGetters.Add(key, getter);

        }

      }

      return fieldGetters[key];

    }

 

    public static object GetFieldValue(
                object instance, string fieldName) {

      return GetFieldGetterMethod(instance, fieldName)
                .Invoke(null, new[] { instance });

    }

 

    private static readonly Dictionary<string, DynamicMethod>
             fieldGetters = new Dictionary<string, DynamicMethod>();

    private static readonly object fsync = new object();

  }

 

// PropertySetter.cs

 

  public class PropertySetter {

    private static DynamicMethod GetPropertySetterMethod(
                            object instance, string propertyName) {

      var t = instance.GetType();

      var key = (t.FullName + "_" + propertyName).Replace(".", "_");

      if (!propertySetter.ContainsKey(key)) {

        lock (fsync) {

          var propertyInfo = t.GetProperty(propertyName,
                                           BindingFlags.Instance |
                                           BindingFlags.Public);

          if (propertyInfo == null)

            throw new ArgumentException("There is no publicly " +
                                        "accessible " + propertyName +
                                        " property found in " +
                                        t.FullName + ".");

          if (!propertyInfo.CanWrite)

            throw new ArgumentException("The property " +
                                        propertyName +
                               " has no publicly accessible setter.");

          var setter = new DynamicMethod("__set_property_" + key, null,
                           new[] { t, propertyInfo.PropertyType },
                           typeof(PropertySetter));

          var setterIL = setter.GetILGenerator();

 

          // ldarg.0    - load argument 1 to eval.stack.0

          setterIL.Emit(OpCodes.Ldarg_0); 

 

          // ldarg.1    - load argument 2 to eval.stack.1
         
setterIL.Emit(OpCodes.Ldarg_1); 

          // callvirt p - call set_* method to set the
          // value from eval.stack.1
                             

          setterIL.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());

          // ret        - exit the method

          setterIL.Emit(OpCodes.Ret);                                  

 

          propertySetter.Add(key, setter);

        }

      }

      return propertySetter[key];

    }

 

    public static void SetPropertyValue(
                 object instance, string propertyName, object value) {

      GetPropertySetterMethod(instance, propertyName)
                .Invoke(null, new[] { instance, value });

    }

 

    private static readonly Dictionary<string, DynamicMethod>
             propertySetter = new Dictionary<string, DynamicMethod>();

    private static readonly object fsync = new object();

  }

 

 

// PropertyGetter.cs

 

  public class PropertyGetter {

    private static DynamicMethod GetPropertyGetterMethod(
                               object instance, string propertyName) {

      var t = instance.GetType();

      var key = (t.FullName + "_" + propertyName).Replace(".", "_");

      if (!propertyGetters.ContainsKey(key)) {

        lock (fsync) {

          var propertyInfo = t.GetProperty(propertyName,
                                           BindingFlags.Instance |
                                           BindingFlags.Public);

          if (propertyInfo == null)

            throw new ArgumentException("There is no publicly " +
                                        "accessible "
+ propertyName +
                                        " property found in " +
                                        t.FullName + ".");

          if (!propertyInfo.CanRead)

            throw new ArgumentException("The property " + propertyName
                            + " has no publicly accessible getter.");

 

          var getter = new DynamicMethod("__get_property_" + key,
                            propertyInfo.PropertyType, new[] { t },
                            typeof(PropertyGetter));

          var getterIL = getter.GetILGenerator();

 

          // ldarg.0 - load argument 0 to eval.stack.0

          getterIL.Emit(OpCodes.Ldarg_0);    

 

          // callvirt m - a call to get_* method to get
          // the property value
                          

          getterIL.Emit(OpCodes.Callvirt, propertyInfo.GetGetMethod());

 

          // ret - return the result of get_* call

          getterIL.Emit(OpCodes.Ret);                                  

 

          propertyGetters.Add(key, getter);

        }

      }

      return propertyGetters[key];

    }

 

    public static object SetPropertyValue(
                 object instance, string propertyName) {

      return GetPropertyGetterMethod(instance, propertyName)
                .Invoke(null, new[] { instance });

    }

 

    private static readonly Dictionary<string, DynamicMethod>
           propertyGetters = new Dictionary<string, DynamicMethod>();

    private static readonly object fsync = new object();

  }

 


I hope this still fall under the category of assembly language Big Smile
(even though CIL is not exactly assembly language)

See you again next time.

 

 

 

Posted by cvega with 5 comment(s)
Filed under: