Hello, I recently stumbled upon this awesome code: http://rubyforge.org/pipermail/ironruby-core/2008-... 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
WPF Databinding with RubyObjects, continuation, dynamically create wrappers with strong typed proper
on 2011-06-14 17:47
Re: WPF Databinding with RubyObjects, continuation, dynamically create wrappers with strong typed pr
on 2011-06-14 18:21
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@rubyforge.org [mailto:ironruby-core-bounces@rubyforge.org] On Behalf Of Sickboy Sent: Tuesday, June 14, 2011 8:38 AM To: ironruby-core@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-... 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
Re: WPF Databinding with RubyObjects, continuation, dynamically create wrappers with strong typed pr
on 2011-06-14 19:15
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@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@rubyforge.org [mailto:ironruby-core-bounces@rubyforge.org] On Behalf Of Sickboy Sent: Tuesday, June 14, 2011 8:38 AM To: ironruby-core@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-... 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
Re: WPF Databinding with RubyObjects, continuation, dynamically create wrappers with strong typed pr
on 2011-06-14 19:50
Your backup plan might actually be better – emitting code at runtime is definitely slower than loading already compiled code. You’re still emitting some wrong instructions. Can you send your new code? Tomas From: ironruby-core-bounces@rubyforge.org [mailto:ironruby-core-bounces@rubyforge.org] On Behalf Of Sickboy Sent: Tuesday, June 14, 2011 10:03 AM To: ironruby-core@rubyforge.org Subject: Re: [Ironruby-core] WPF Databinding with RubyObjects, continuation, dynamically create wrappers with strong typed properties 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<mailto:Tomas.Matousek@microsoft.com> Sent: Tuesday, June 14, 2011 6:05 PM To: ironruby-core@rubyforge.org<mailto:ironruby-core@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@rubyforge.org [mailto:ironruby-core-bounces@rubyforge.org] On Behalf Of Sickboy Sent: Tuesday, June 14, 2011 8:38 AM To: ironruby-core@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-... 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
Re: WPF Databinding with RubyObjects, continuation, dynamically create wrappers with strong typed pr
on 2011-06-14 20:00
Thanks! Currently on my way with the backup plan.
But here’s the C# and Ruby for the dynamic implementation:
C#
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"));
if (type != typeof(string))
{
//ilGen.Emit(OpCodes.Unbox, type);
}
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);
if (type == typeof(int) || type == typeof(float))
{
ilGen.Emit(OpCodes.Box, type);
}
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:
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 = System::Int16.new(35) # !!!!! Crashes here !!!!!
puts "Assigned"
puts wrapped.test, wrapped.test.inspect, wrapped.test.class
From: Tomas Matousek
Sent: Tuesday, June 14, 2011 7:49 PM
To: ironruby-core@rubyforge.org
Subject: Re: [Ironruby-core] WPF Databinding with RubyObjects,
continuation,dynamically create wrappers with strong typed properties
Your backup plan might actually be better – emitting code at runtime is
definitely slower than loading already compiled code.
You’re still emitting some wrong instructions. Can you send your new
code?
Tomas
From: ironruby-core-bounces@rubyforge.org
[mailto:ironruby-core-bounces@rubyforge.org] On Behalf Of Sickboy
Sent: Tuesday, June 14, 2011 10:03 AM
To: ironruby-core@rubyforge.org
Subject: Re: [Ironruby-core] WPF Databinding with RubyObjects,
continuation, dynamically create wrappers with strong typed properties
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@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@rubyforge.org
[mailto:ironruby-core-bounces@rubyforge.org] On Behalf Of Sickboy
Sent: Tuesday, June 14, 2011 8:38 AM
To: ironruby-core@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-...
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@rubyforge.org
http://rubyforge.org/mailman/listinfo/ironruby-core
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.