[Ironruby-core] WPF Databinding with RubyObjects, continuation, dynamically create wrappers with strong typed properties

Sickboy sb at dev-heaven.net
Tue Jun 14 13:03:00 EDT 2011


Thanks! Still new to .NET and especially this stuff.

The Box/Unbox seems to have worked, except that if I set e.g 35 as new value, when reading the value back, something completely different comes back, e.g; 150007660, but correct class Fixnum.
This number seems randomized, and changes upon accessing the getter for the property more than once.

Any suggestions or alternatives welcome.
My backup plan is to generate the C# classes from Ruby dynamically during development and saving them into a .cs file to be compiled with exe/dll.

Regards


From: Tomas Matousek 
Sent: Tuesday, June 14, 2011 6:05 PM
To: ironruby-core at rubyforge.org 
Subject: Re: [Ironruby-core] WPF Databinding with RubyObjects, continuation,dynamically create wrappers with strong typed properties

My guess would be that you’re not boxing the value.

You need to emit OpCodes.Box to convert bool/int/… to an Object before calling Invoke in setter. The setter should also return Object (your’s is void). In getter you need to use OpCodes.Unbox before returning.

I’d also recommend using Func<> delegate instead of your custom delegate types if possible.

 

Tomas

 

From: ironruby-core-bounces at rubyforge.org [mailto:ironruby-core-bounces at rubyforge.org] On Behalf Of Sickboy
Sent: Tuesday, June 14, 2011 8:38 AM
To: ironruby-core at rubyforge.org
Subject: [Ironruby-core] WPF Databinding with RubyObjects, continuation, dynamically create wrappers with strong typed properties

 

Hello,

 

I recently stumbled upon this awesome code: http://rubyforge.org/pipermail/ironruby-core/2008-December/003377.html

It does exactly what I need; dynamically wrapping my RubyObject into a CLR class with CLR properties, except that it’s designed only for string properties.

 

I’ve adjusted the code to support a number of types, incl int, bool and float.

 

However the CLR crashes when-ever I assign a value to a non-string property, e.g float or int.

“The runtime has encountered a fatal error. The address of the error was at 0x5935788d, on thread 0x1adc. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.”

 

Any assistance would be deeply appreciated.

 

In essence, instead of using typeof(string) inside the TypeGenerator.Generate method, i use e.g; typeof(int) or typeof(float)

This is my current code:

 

C#:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Reflection;

using System.Reflection.Emit;

 

namespace GenerateType

{

    public class TypeGenerator

    {

        public delegate object GetPropertyDelegate(string propertyName);

        public delegate object SetPropertyDelegate(string propertyName, object value);

 

        public static Type Generate(string className, Dictionary<string, List<string>> properties)

        {

            AssemblyName asmName = new AssemblyName("BindingTypes");

            AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);

            ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("Types");

            TypeBuilder typeBuilder = modBuilder.DefineType(className,

                        TypeAttributes.Public |

                        TypeAttributes.Class |

                        TypeAttributes.AutoClass |

                        TypeAttributes.AnsiClass |

                        TypeAttributes.BeforeFieldInit |

                        TypeAttributes.AutoLayout);

 

 

            FieldBuilder getFieldBuilder = typeBuilder.DefineField("OnGet", typeof(GetPropertyDelegate), FieldAttributes.Public);

            FieldBuilder setFieldBuilder = typeBuilder.DefineField("OnSet", typeof(SetPropertyDelegate), FieldAttributes.Public);

 

            MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;

 

            Type type = null;

 

            foreach (string s in properties.Keys)

            {

                type = null;

                switch (s)

                {

                    case "float":

                        type = typeof(float);

                        break;

                    case "int":

                        type = typeof(int);

                        break;

                    case "string":

                        type = typeof(string);

                        break;

                    case "bool":

                        type = typeof(bool);

                        break;

                    //case "dynamic":

                    //    type = dynamic;

                }

                if (type != null)

                {

                    GenerateProperties(properties[s], type, typeBuilder, getSetAttr, getFieldBuilder, setFieldBuilder);

                }

            }

            return typeBuilder.CreateType();

        }

 

        public static void GenerateProperties(List<string> properties, Type type, TypeBuilder typeBuilder, MethodAttributes getSetAttr, FieldBuilder getFieldBuilder, FieldBuilder setFieldBuilder)

        {

            foreach (string propertyName in properties)

            {

                PropertyBuilder propBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.None, typeof(object), new Type[] {});

                MethodBuilder getter = typeBuilder.DefineMethod("get_" + propertyName,

                                   getSetAttr,

                                   type,

                                   Type.EmptyTypes);

 

                ILGenerator ilGen = getter.GetILGenerator();

 

                ilGen.Emit(OpCodes.Ldarg_0);

                ilGen.Emit(OpCodes.Ldfld, getFieldBuilder);

                ilGen.Emit(OpCodes.Ldstr, propertyName);

                ilGen.Emit(OpCodes.Callvirt, typeof(GetPropertyDelegate).GetMethod("Invoke"));

                ilGen.Emit(OpCodes.Ret);

 

                // Define the "set" accessor method for CustomerName.

                MethodBuilder setter = typeBuilder.DefineMethod("set_" + propertyName,

                                               getSetAttr,

                                               null,

                                               new Type[] { type });

 

                ilGen = setter.GetILGenerator();

 

                ilGen.Emit(OpCodes.Ldarg_0);

                ilGen.Emit(OpCodes.Ldfld, setFieldBuilder);

                ilGen.Emit(OpCodes.Ldstr, propertyName);

                ilGen.Emit(OpCodes.Ldarg_1);

                ilGen.Emit(OpCodes.Callvirt, typeof(SetPropertyDelegate).GetMethod("Invoke"));

                ilGen.Emit(OpCodes.Pop);

                ilGen.Emit(OpCodes.Ret);

 

                // Last, we must map the two methods created above to our PropertyBuilder to

                // their corresponding behaviors, "get" and "set" respectively.

                propBuilder.SetGetMethod(getter);

                propBuilder.SetSetMethod(setter);

            }

        }

    }

}

 

 

 

 

Ruby Code:

 

include System::Data

include System::Windows::Data

include System::ComponentModel

 

include GenerateType 

class WrapperGenerator

  def initialize

    @wrapper_cache = {}

  end

 

  def wrap(ruby_object)

    if ruby_object.is_a? Array

      ruby_object.map {|o| wrap(o) }

    else

      cache(ruby_object) unless cached(ruby_object)

      wrapper_class = cached(ruby_object)

      wrapper_class.new(ruby_object)

    end

  end

 

  def invalidate

    @wrapper_cache.clear

  end

private

  def cached(object)

    @wrapper_cache[object.class.name]

  end

 

  def cache(object)

    @wrapper_cache[object.class.name] = generate_wrapper(object)

  end

 

  def generate_wrapper(object)

    wrapper_name = "#{object.class.name}Wrapper"

    properties = Dictionary.of(System::String, List.of(System::String)).new

 

    if defined?(object.class::PROPERTY_DESCRIPTORS)

      object.class::PROPERTY_DESCRIPTORS.each_pair do |k, v|

        properties[k.to_clr_string] = List.of(System::String).new

        properties[k.to_clr_string].replace v.map{|e| e.to_clr_string}

      end

    else

      # Default to String properties

      properties["string"] = List.of(System::String).new

      properties["string"].replace (object.methods - Object.instance_methods).map{|e| e.to_clr_string}

    end

 

    wrapper_base_type = TypeGenerator.generate("#{wrapper_name}Base", properties)

    base_instance = System::Activator.create_instance wrapper_base_type

 

    eval <<EOS

      class #{wrapper_name} < base_instance.class

        def initialize(original)

          self.on_get = lambda do |prop|

            original.send prop

          end

 

          self.on_set = lambda do |prop, val|

            original.send "\#{prop}=", val

          end

        end

      end

      return #{wrapper_name} # return the class

EOS

  end

end

 

class Person

  attr_accessor :test

  PROPERTY_DESCRIPTORS = { :int => [:test] }

end

 

wrapper = WrapperGenerator.new

wrapped = wrapper.wrap(Person.new)

puts "Wrapped"

# wrapped.test = "35" # properly generates Exception: Cannot convert String to Fixnum

wrapped.test = 35 # !!!!! Crashes here !!!!!

puts "Assigned"

puts wrapped.test, wrapped.test.inspect, wrapped.test.class



--------------------------------------------------------------------------------
_______________________________________________
Ironruby-core mailing list
Ironruby-core at rubyforge.org
http://rubyforge.org/mailman/listinfo/ironruby-core
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20110614/09472f63/attachment-0001.html>


More information about the Ironruby-core mailing list