diff --git a/src/Spring/Spring.Core/Expressions/IndexerNode.cs b/src/Spring/Spring.Core/Expressions/IndexerNode.cs index 730de743..e1ce7367 100644 --- a/src/Spring/Spring.Core/Expressions/IndexerNode.cs +++ b/src/Spring/Spring.Core/Expressions/IndexerNode.cs @@ -40,7 +40,7 @@ namespace Spring.Expressions | BindingFlags.Instance | BindingFlags.Static | BindingFlags.IgnoreCase; - private SafeIndexer indexer; + private SafeProperty indexer; /// /// Create a new instance @@ -191,7 +191,7 @@ namespace Spring.Expressions EvaluationContext evalContext = new EvaluationContext(context, variables); InitializeIndexerProperty(context, evalContext); - return indexer.IndexerProperty; + return indexer.PropertyInfo; } } @@ -260,7 +260,7 @@ namespace Spring.Expressions private void SetGenericIndexer(object context, EvaluationContext evalContext,object newValue) { object[] indices = InitializeIndexerProperty( context, evalContext ); - indexer.SetValue( context,indices,newValue ); + indexer.SetValue( context, newValue, indices ); } private object[] InitializeIndexerProperty(object context, EvaluationContext evalContext) @@ -281,7 +281,7 @@ namespace Spring.Expressions } else { - indexer = new SafeIndexer(indexerProperty); + indexer = new SafeProperty(indexerProperty); } } } diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/BaseDynamicMember.cs b/src/Spring/Spring.Core/Reflection/Dynamic/BaseDynamicMember.cs index 4dafbefd..10e4e865 100644 --- a/src/Spring/Spring.Core/Reflection/Dynamic/BaseDynamicMember.cs +++ b/src/Spring/Spring.Core/Reflection/Dynamic/BaseDynamicMember.cs @@ -34,6 +34,8 @@ namespace Spring.Reflection.Dynamic /// Aleksandar Seovic public class BaseDynamicMember { +//#if NET_2_0 +//#else /// /// Method attributes constant. /// @@ -56,12 +58,12 @@ namespace Spring.Reflection.Dynamic if (targetType.IsValueType) { LocalBuilder target = il.DeclareLocal(targetType); -#if NET_2_0 - il.Emit(OpCodes.Unbox_Any, targetType); -#else +//#if NET_2_0 +// il.Emit(OpCodes.Unbox_Any, targetType); +//#else il.Emit(OpCodes.Unbox, targetType); il.Emit(OpCodes.Ldobj, targetType); -#endif +//#endif il.Emit(OpCodes.Stloc, target); il.Emit(OpCodes.Ldloca, target); } @@ -82,12 +84,12 @@ namespace Spring.Reflection.Dynamic il.Emit(OpCodes.Ldarg, argumentPosition); if (argumentType.IsValueType) { -#if NET_2_0 - il.Emit(OpCodes.Unbox_Any, argumentType); -#else +//#if NET_2_0 +// il.Emit(OpCodes.Unbox_Any, argumentType); +//#else il.Emit(OpCodes.Unbox, argumentType); il.Emit(OpCodes.Ldobj, argumentType); -#endif +//#endif } else { @@ -142,5 +144,6 @@ namespace Spring.Reflection.Dynamic il.Emit(OpCodes.Newobj, invalidOperationException); il.Emit(OpCodes.Throw); } +//#endif } } \ No newline at end of file diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicConstructor.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicConstructor.cs index f34d0e22..9772b183 100644 --- a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicConstructor.cs +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicConstructor.cs @@ -21,6 +21,7 @@ #region Imports using System; +using System.Collections; using System.Reflection; using System.Reflection.Emit; using Spring.Util; @@ -62,7 +63,59 @@ namespace Spring.Reflection.Dynamic /// public class SafeConstructor : IDynamicConstructor { - private ConstructorInfo constructor; + private ConstructorInfo constructorInfo; + +#if NET_2_0 + #region Generated Function Cache + + private static readonly IDictionary constructorCache = new Hashtable(); + + /// + /// Obtains cached constructor info or creates a new entry, if none is found. + /// + private static ConstructorDelegate GetOrCreateDynamicConstructor(ConstructorInfo constructorInfo) + { + ConstructorDelegate method = (ConstructorDelegate)constructorCache[constructorInfo]; + if (method == null) + { + method = DynamicReflectionManager.CreateConstructor(constructorInfo); + lock (constructorCache) + { + constructorCache[constructorInfo] = method; + } + } + return method; + } + + #endregion + + private ConstructorDelegate constructor; + + /// + /// Creates a new instance of the safe constructor wrapper. + /// + /// Constructor to wrap. + public SafeConstructor(ConstructorInfo constructorInfo) + { + this.constructorInfo = constructorInfo; + this.constructor = GetOrCreateDynamicConstructor(constructorInfo); + } + + + /// + /// Invokes dynamic constructor. + /// + /// + /// Constructor arguments. + /// + /// + /// A constructor value. + /// + public object Invoke(object[] arguments) + { + return constructor(arguments); + } +#else private IDynamicConstructor dynamicConstructor; private bool isOptimized = false; @@ -72,7 +125,7 @@ namespace Spring.Reflection.Dynamic /// Constructor to wrap. public SafeConstructor(ConstructorInfo constructor) { - this.constructor = constructor; + this.constructorInfo = constructor; if (constructor.IsPublic && ReflectionUtils.IsTypeVisible(constructor.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) { @@ -80,7 +133,7 @@ namespace Spring.Reflection.Dynamic this.isOptimized = true; } } - + /// /// Invokes dynamic constructor. /// @@ -103,18 +156,40 @@ namespace Spring.Reflection.Dynamic catch (InvalidCastException) { isOptimized = false; - return constructor.Invoke(arguments); + return constructorInfo.Invoke(arguments); } } else { - return constructor.Invoke(arguments); + return constructorInfo.Invoke(arguments); } } +#endif } #endregion + +#if NET_2_0 + /// + /// Factory class for dynamic constructors. + /// + /// Aleksandar Seovic + public class DynamicConstructor : BaseDynamicMember + { + /// + /// Creates dynamic constructor instance for the specified . + /// + /// Constructor info to create dynamic constructor for. + /// Dynamic constructor for the specified . + public static IDynamicConstructor Create(ConstructorInfo constructorInfo) + { + AssertUtils.ArgumentNotNull(constructorInfo, "You cannot create a dynamic constructor for a null value."); + + return new SafeConstructor(constructorInfo); + } + } +#else /// /// Factory class for dynamic constructors. /// @@ -162,37 +237,9 @@ namespace Spring.Reflection.Dynamic ILGenerator il = invokeMethod.GetILGenerator(); - Type[] argTypes = ReflectionUtils.GetParameterTypes(constructor); - for (int i = 0; i < argTypes.Length; i++) - { - SetupConstructorArgument(il, argTypes[i], i); - } - - il.Emit(OpCodes.Newobj, constructor); - ProcessReturnValue(il, constructor.DeclaringType); - il.Emit(OpCodes.Ret); - } - - private static void SetupConstructorArgument(ILGenerator il, Type argumentType, int argumentPosition) - { - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldc_I4, argumentPosition); - il.Emit(OpCodes.Ldelem_Ref); - if (argumentType.IsValueType) - { -#if NET_2_0 - il.Emit(OpCodes.Unbox_Any, argumentType); -#else - il.Emit(OpCodes.Unbox, argumentType); - il.Emit(OpCodes.Ldobj, argumentType); -#endif - } - else - { - il.Emit(OpCodes.Castclass, argumentType); - } - } - + DynamicReflectionManager.EmitInvokeConstructor(il, constructor, true); + } #endregion } +#endif } diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicField.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicField.cs index 4c0f4710..b5af7cc2 100644 --- a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicField.cs +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicField.cs @@ -85,10 +85,10 @@ namespace Spring.Reflection.Dynamic /// private class DynamicFieldCacheEntry { - public readonly GetterDelegate Getter; - public readonly SetterDelegate Setter; + public readonly FieldGetterDelegate Getter; + public readonly FieldSetterDelegate Setter; - public DynamicFieldCacheEntry(GetterDelegate getter, SetterDelegate setter) + public DynamicFieldCacheEntry(FieldGetterDelegate getter, FieldSetterDelegate setter) { Getter = getter; Setter = setter; @@ -114,8 +114,8 @@ namespace Spring.Reflection.Dynamic #endregion - private readonly GetterDelegate getter; - private readonly SetterDelegate setter; + private readonly FieldGetterDelegate getter; + private readonly FieldSetterDelegate setter; /// /// Creates a new instance of the safe field wrapper. diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicIndexer.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicIndexer.cs index 8f706155..62ee7ca4 100644 --- a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicIndexer.cs +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicIndexer.cs @@ -1,5 +1,5 @@ -#region License - +#region License + /* * Copyright © 2002-2007 the original author or authors. * @@ -14,128 +14,280 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Reflection; +using System.Reflection.Emit; +using Spring.Reflection.Dynamic; +using Spring.Util; + +#endregion + +namespace Spring.Reflection.Dynamic +{ + #region IDynamicIndexer interface + + /// + /// Defines methods that dynamic indexer class has to implement. + /// + public interface IDynamicIndexer + { + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get the indexer value from. + /// + /// + /// Indexer argument. + /// + /// + /// A indexer value. + /// + object GetValue( object target, int index ); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get the indexer value from. + /// + /// + /// Indexer argument. + /// + /// + /// A indexer value. + /// + object GetValue( object target, object index ); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get the indexer value from. + /// + /// + /// Indexer arguments. + /// + /// + /// A indexer value. + /// + object GetValue( object target, object[] index ); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set the indexer value on. + /// + /// + /// Indexer argument. + /// + /// + /// A new indexer value. + /// + void SetValue( object target, int index, object value ); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set the indexer value on. + /// + /// + /// Indexer argument. + /// + /// + /// A new indexer value. + /// + void SetValue( object target, object index, object value ); + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set the indexer value on. + /// + /// + /// Indexer arguments. + /// + /// + /// A new indexer value. + /// + void SetValue( object target, object[] index, object value ); + } + + #endregion + + #region Safe wrapper + +#if NET_2_0 + /// + /// Safe wrapper for the dynamic indexer. + /// + /// + /// will attempt to use dynamic + /// indexer if possible, but it will fall back to standard + /// reflection if necessary. + /// + [Obsolete("Use SafeProperty instead", false)] + public class SafeIndexer : IDynamicIndexer + { + private PropertyInfo indexerProperty; + + /// + /// Internal PropertyInfo accessor. + /// + internal PropertyInfo IndexerProperty + { + get { return indexerProperty; } + } + + private SafeProperty property; + + /// + /// Creates a new instance of the safe indexer wrapper. + /// + /// Indexer to wrap. + public SafeIndexer( PropertyInfo indexerInfo ) + { + AssertUtils.ArgumentNotNull( indexerInfo, "You cannot create a dynamic indexer for a null value." ); + + this.indexerProperty = indexerInfo; + this.property = new SafeProperty( indexerInfo ); + } + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get indexer value from. + /// + /// + /// Indexer arguments. + /// + /// + /// A indexer value. + /// + public object GetValue( object target, int index ) + { + return property.GetValue( target, index ); + } + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get the indexer value from. + /// + /// + /// Indexer argument. + /// + /// + /// A indexer value. + /// + public object GetValue( object target, object index ) + { + return property.GetValue( target, index ); + } + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get indexer value from. + /// + /// + /// Indexer arguments. + /// + /// + /// A indexer value. + /// + public object GetValue( object target, object[] index ) + { + return property.GetValue( target, index ); + } + + /// + /// Sets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set indexer value on. + /// + /// + /// Indexer arguments. + /// + /// + /// A new indexer value. + /// + public void SetValue( object target, int index, object value ) + { + property.SetValue( target, value, index ); + } + + /// + /// Sets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set indexer value on. + /// + /// + /// Indexer arguments. + /// + /// + /// A new indexer value. + /// + public void SetValue( object target, object index, object value ) + { + property.SetValue( target, value, index ); + } + + /// + /// Sets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set indexer value on. + /// + /// + /// Indexer arguments. + /// + /// + /// A new indexer value. + /// + public void SetValue( object target, object[] index, object value ) + { + property.SetValue( target, value, index ); + } + } + +#else -#endregion + /// + /// Safe wrapper for the dynamic indexer. + /// + /// + /// will attempt to use dynamic + /// indexer if possible, but it will fall back to standard + /// reflection if necessary. + /// + public class SafeIndexer : IDynamicIndexer + { + private PropertyInfo indexerProperty; + + /// + /// Internal PropertyInfo accessor. + /// + internal PropertyInfo IndexerProperty + { + get { return indexerProperty; } + } -#region Imports - -using System; -using System.Reflection; -using System.Reflection.Emit; -using Spring.Util; - -#endregion - -namespace Spring.Reflection.Dynamic -{ - #region IDynamicIndexer interface - - /// - /// Defines methods that dynamic indexer class has to implement. - /// - public interface IDynamicIndexer - { - /// - /// Gets the value of the dynamic indexer for the specified target object. - /// - /// - /// Target object to get the indexer value from. - /// - /// - /// Indexer argument. - /// - /// - /// A indexer value. - /// - object GetValue(object target, int index); - - /// - /// Gets the value of the dynamic indexer for the specified target object. - /// - /// - /// Target object to get the indexer value from. - /// - /// - /// Indexer argument. - /// - /// - /// A indexer value. - /// - object GetValue(object target, object index); - - /// - /// Gets the value of the dynamic indexer for the specified target object. - /// - /// - /// Target object to get the indexer value from. - /// - /// - /// Indexer arguments. - /// - /// - /// A indexer value. - /// - object GetValue(object target, object[] index); - - /// - /// Gets the value of the dynamic indexer for the specified target object. - /// - /// - /// Target object to set the indexer value on. - /// - /// - /// Indexer argument. - /// - /// - /// A new indexer value. - /// - void SetValue(object target, int index, object value); - - /// - /// Gets the value of the dynamic indexer for the specified target object. - /// - /// - /// Target object to set the indexer value on. - /// - /// - /// Indexer argument. - /// - /// - /// A new indexer value. - /// - void SetValue(object target, object index, object value); - - /// - /// Gets the value of the dynamic indexer for the specified target object. - /// - /// - /// Target object to set the indexer value on. - /// - /// - /// Indexer arguments. - /// - /// - /// A new indexer value. - /// - void SetValue(object target, object[] index, object value); - } - - #endregion - - #region Safe wrapper - - /// - /// Safe wrapper for the dynamic indexer. - /// - /// - /// will attempt to use dynamic - /// indexer if possible, but it will fall back to standard - /// reflection if necessary. - /// - public class SafeIndexer : IDynamicIndexer - { - private PropertyInfo indexerProperty; private IDynamicIndexer dynamicIndexer; private bool isOptimizedGet = false; private bool isOptimizedSet = false; @@ -334,18 +486,38 @@ namespace Spring.Reflection.Dynamic indexerProperty.SetValue(target, value, index); } } - - /// - /// Internal PropertyInfo accessor. - /// - internal PropertyInfo IndexerProperty - { - get { return indexerProperty; } - } - } - - #endregion - + } +#endif + + #endregion + +#if NET_2_0 + /// + /// Factory class for dynamic indexers. + /// + /// Aleksandar Seovic + [Obsolete( "Use DynamicProperty instead", false )] + public sealed class DynamicIndexer : BaseDynamicMember + { + /// + /// Prevent instantiation + /// + private DynamicIndexer() { } + + /// + /// Creates dynamic indexer instance for the specified . + /// + /// Indexer info to create dynamic indexer for. + /// Dynamic indexer for the specified . + public static IDynamicIndexer Create( PropertyInfo indexer ) + { + AssertUtils.ArgumentNotNull( indexer, "You cannot create a dynamic indexer for a null value." ); + + IDynamicIndexer dynamicIndexer = new SafeIndexer( indexer ); + return dynamicIndexer; + } + } +#else /// /// Factory class for dynamic indexers. /// @@ -354,7 +526,7 @@ namespace Spring.Reflection.Dynamic { private static readonly CreateIndexerCallback s_createCallback = new CreateIndexerCallback(CreateInternal); - #region Create Method + #region Create Method /// /// Creates dynamic indexer instance for the specified . @@ -500,12 +672,8 @@ namespace Spring.Reflection.Dynamic } if (argumentType.IsValueType) { -#if NET_2_0 - il.Emit(OpCodes.Unbox_Any, argumentType); -#else il.Emit(OpCodes.Unbox, argumentType); il.Emit(OpCodes.Ldobj, argumentType); -#endif } else { @@ -513,6 +681,8 @@ namespace Spring.Reflection.Dynamic } } - #endregion + #endregion } -} +#endif + +} // namespace \ No newline at end of file diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs index 608dbba5..eb2d6757 100644 --- a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs @@ -49,7 +49,7 @@ namespace Spring.Reflection.Dynamic /// /// A method return value. /// - object Invoke(object target, object[] arguments); + object Invoke(object target, params object[] arguments); } #endregion @@ -66,7 +66,71 @@ namespace Spring.Reflection.Dynamic /// public class SafeMethod : IDynamicMethod { - private MethodInfo method; + private readonly MethodInfo methodInfo; + + /// + /// Gets the class, that declares this method + /// + public Type DeclaringType + { + get { return methodInfo.DeclaringType; } + } + +#if NET_2_0 + #region Generated Function Cache + + private static readonly IDictionary methodCache = new Hashtable(); + + /// + /// Obtains cached property info or creates a new entry, if none is found. + /// + private static FunctionDelegate GetOrCreateDynamicMethod(MethodInfo methodInfo) + { + FunctionDelegate method = (FunctionDelegate)methodCache[methodInfo]; + if (method == null) + { + method = DynamicReflectionManager.CreateMethod(methodInfo); + lock (methodCache) + { + methodCache[methodInfo] = method; + } + } + return method; + } + + #endregion + + private readonly FunctionDelegate method; + + /// + /// Creates a new instance of the safe method wrapper. + /// + /// Method to wrap. + public SafeMethod(MethodInfo methodInfo) + { + AssertUtils.ArgumentNotNull(methodInfo, "You cannot create a dynamic method for a null value."); + + this.methodInfo = methodInfo; + this.method = GetOrCreateDynamicMethod(methodInfo); + } + + /// + /// Invokes dynamic method. + /// + /// + /// Target object to invoke method on. + /// + /// + /// Method arguments. + /// + /// + /// A method return value. + /// + public object Invoke(object target, object[] arguments) + { + return this.method(target, arguments); + } +#else private IDynamicMethod dynamicMethod; private bool isOptimized = false; @@ -76,7 +140,7 @@ namespace Spring.Reflection.Dynamic /// Method to wrap. public SafeMethod(MethodInfo method) { - this.method = method; + this.methodInfo = method; if (method.IsPublic && ReflectionUtils.IsTypeVisible(method.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME)) { @@ -85,14 +149,6 @@ namespace Spring.Reflection.Dynamic } } - /// - /// Gets the class, that declares this method - /// - public Type DeclaringType - { - get { return method.DeclaringType; } - } - /// /// Invokes dynamic method. /// @@ -123,12 +179,12 @@ namespace Spring.Reflection.Dynamic throw; } isOptimized = false; - return method.Invoke(target, arguments); + return methodInfo.Invoke(target, arguments); } } else { - return method.Invoke(target, arguments); + return methodInfo.Invoke(target, arguments); } } @@ -136,10 +192,32 @@ namespace Spring.Reflection.Dynamic { return e.TargetSite.DeclaringType.FullName.IndexOf(DynamicReflectionManager.ASSEMBLY_NAME) >= 0; } +#endif } #endregion +#if NET_2_0 + /// + /// Factory class for dynamic methods. + /// + /// Aleksandar Seovic + public class DynamicMethod : BaseDynamicMember + { + /// + /// Creates dynamic method instance for the specified . + /// + /// Method info to create dynamic method for. + /// Dynamic method for the specified . + public static IDynamicMethod Create(MethodInfo method) + { + AssertUtils.ArgumentNotNull(method, "You cannot create a dynamic method for a null value."); + + IDynamicMethod dynamicMethod = new SafeMethod(method); + return dynamicMethod; + } + } +#else /// /// Factory class for dynamic methods. /// @@ -187,104 +265,11 @@ namespace Spring.Reflection.Dynamic invokeMethod.DefineParameter(2, ParameterAttributes.None, "args"); ILGenerator il = invokeMethod.GetILGenerator(); - bool isValueType = method.DeclaringType.IsValueType; - bool isStatic = method.IsStatic; - - IDictionary outArgs = new Hashtable(); - ParameterInfo[] args = method.GetParameters(); - for (int i = 0; i < args.Length; i++) - { - if (IsOutputOrRefArgument(args[i])) - { - SetupOutputArgument(il, args[i], outArgs); - } - } - - if (!isStatic) - { - SetupTargetInstance(il, method.DeclaringType); - } - - for (int i = 0; i < args.Length; i++) - { - SetupMethodArgument(il, args[i], outArgs); - } - InvokeMethod(il, isStatic, isValueType, method); - for (int i = 0; i < args.Length; i++) - { - if (IsOutputOrRefArgument(args[i])) - { - ProcessOutputArgument(il, args[i], outArgs); - } - } - ProcessReturnValue(il, method.ReturnType); - il.Emit(OpCodes.Ret); - } - - private static bool IsOutputOrRefArgument(ParameterInfo argInfo) - { - return argInfo.IsOut || argInfo.ParameterType.Name.EndsWith("&"); - } - - private static void SetupOutputArgument(ILGenerator il, ParameterInfo argInfo, IDictionary outArgs) - { - Type argType = argInfo.ParameterType.GetElementType(); - - LocalBuilder lb = il.DeclareLocal(argType); - if (!argInfo.IsOut) - { - PushArgumentValue(il, argType, argInfo.Position); - il.Emit(OpCodes.Stloc, lb); - } - outArgs[argInfo.Position] = lb; - } - - private static void ProcessOutputArgument(ILGenerator il, ParameterInfo argInfo, IDictionary outArgs) - { - Type argType = argInfo.ParameterType.GetElementType(); - - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldc_I4, argInfo.Position); - il.Emit(OpCodes.Ldloc, (LocalBuilder)outArgs[argInfo.Position]); - if (argType.IsValueType) - { - il.Emit(OpCodes.Box, argType); - } - il.Emit(OpCodes.Stelem_Ref); - } - - private static void SetupMethodArgument(ILGenerator il, ParameterInfo argInfo, IDictionary outArgs) - { - if (IsOutputOrRefArgument(argInfo)) - { - il.Emit(OpCodes.Ldloca_S, (LocalBuilder)outArgs[argInfo.Position]); - } - else - { - PushArgumentValue(il, argInfo.ParameterType, argInfo.Position); - } - } - - private static void PushArgumentValue(ILGenerator il, Type argumentType, int argumentPosition) - { - il.Emit(OpCodes.Ldarg_2); - il.Emit(OpCodes.Ldc_I4, argumentPosition); - il.Emit(OpCodes.Ldelem_Ref); - if (argumentType.IsValueType) - { -#if NET_2_0 - il.Emit(OpCodes.Unbox_Any, argumentType); -#else - il.Emit(OpCodes.Unbox, argumentType); - il.Emit(OpCodes.Ldobj, argumentType); -#endif - } - else - { - il.Emit(OpCodes.Castclass, argumentType); - } + DynamicReflectionManager.EmitInvokeMethod(il, method, true); } #endregion } + +#endif } diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicProperty.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicProperty.cs index a7b04643..743597cf 100644 --- a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicProperty.cs +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicProperty.cs @@ -60,6 +60,30 @@ namespace Spring.Reflection.Dynamic /// A new property value. /// void SetValue(object target, object value); + + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to get property value from. + /// + /// Optional index values for indexed properties. This value should be null reference for non-indexed properties. + /// + /// A property value. + /// + object GetValue(object target, params object[] index); + + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to set property value on. + /// + /// + /// A new property value. + /// + /// Optional index values for indexed properties. This value should be null reference for non-indexed properties. + void SetValue(object target, object value, params object[] index); } #endregion @@ -91,10 +115,10 @@ namespace Spring.Reflection.Dynamic /// private class DynamicPropertyCacheEntry { - public readonly GetterDelegate Getter; - public readonly SetterDelegate Setter; + public readonly PropertyGetterDelegate Getter; + public readonly PropertySetterDelegate Setter; - public DynamicPropertyCacheEntry(GetterDelegate getter, SetterDelegate setter) + public DynamicPropertyCacheEntry(PropertyGetterDelegate getter, PropertySetterDelegate setter) { Getter = getter; Setter = setter; @@ -120,8 +144,8 @@ namespace Spring.Reflection.Dynamic #endregion - private readonly GetterDelegate getter; - private readonly SetterDelegate setter; + private readonly PropertyGetterDelegate getter; + private readonly PropertySetterDelegate setter; /// /// Creates a new instance of the safe property wrapper. @@ -151,6 +175,21 @@ namespace Spring.Reflection.Dynamic return getter(target); } + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to get property value from. + /// + /// Optional index values for indexed properties. This value should be null reference for non-indexed properties. + /// + /// A property value. + /// + public object GetValue(object target, params object[] index) + { + return getter(target, index); + } + /// /// Gets the value of the dynamic property for the specified target object. /// @@ -165,6 +204,21 @@ namespace Spring.Reflection.Dynamic setter(target, value); } + /// + /// Gets the value of the dynamic property for the specified target object. + /// + /// + /// Target object to set property value on. + /// + /// + /// A new property value. + /// + /// Optional index values for indexed properties. This value should be null reference for non-indexed properties. + public void SetValue(object target, object value, params object[] index) + { + setter(target, value, index); + } + #else private readonly IDynamicProperty dynamicProperty; private readonly bool isOptimizedGet = false; @@ -239,8 +293,9 @@ namespace Spring.Reflection.Dynamic { dynamicProperty.SetValue(target, value); } - catch (InvalidCastException) + catch (InvalidCastException ex) { + Log.Debug("Failed optimized set", ex); isOptimizedSet = false; propertyInfo.SetValue(target, value, null); } @@ -268,6 +323,64 @@ namespace Spring.Reflection.Dynamic throw; } } + + /// + /// Gets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to get indexer value from. + /// + /// + /// Indexer arguments. + /// + /// + /// A indexer value. + /// + public object GetValue(object target, object[] index) + { + if (isOptimizedGet) + { + try + { + return dynamicProperty.GetValue(target, index); + } + catch (InvalidCastException) + { + isOptimizedSet = false; + } + } + return propertyInfo.GetValue(target, index); + } + + /// + /// Sets the value of the dynamic indexer for the specified target object. + /// + /// + /// Target object to set indexer value on. + /// + /// + /// Indexer arguments. + /// + /// + /// A new indexer value. + /// + public void SetValue(object target, object value, object[] index) + { + if (isOptimizedSet) + { + try + { + dynamicProperty.SetValue(target, value, index); + return; + } + catch (InvalidCastException ex) + { + Log.Debug("Failed optimized set", ex); + isOptimizedSet = false; + } + } + propertyInfo.SetValue(target, value, index); + } #endif /// /// Internal PropertyInfo accessor. @@ -364,7 +477,9 @@ namespace Spring.Reflection.Dynamic tb.AddInterfaceImplementation(typeof(IDynamicProperty)); GenerateGetValue(tb, property); + GenerateGetIndexedValue(tb, property); GenerateSetValue(tb, property); + GenerateSetIndexedValue(tb, property); Type dynamicPropertyType = tb.CreateType(); ConstructorInfo ctor = dynamicPropertyType.GetConstructor(Type.EmptyTypes); @@ -401,6 +516,41 @@ namespace Spring.Reflection.Dynamic } } + private static void GenerateGetIndexedValue(TypeBuilder tb, PropertyInfo indexer) + { + MethodBuilder getValueMethod = + tb.DefineMethod("GetValue", METHOD_ATTRIBUTES, typeof(object), new Type[] { typeof(object), typeof(object[]) }); + getValueMethod.DefineParameter(1, ParameterAttributes.None, "target"); + getValueMethod.DefineParameter(2, ParameterAttributes.None, "index"); + + ILGenerator il = getValueMethod.GetILGenerator(); + if (indexer.CanRead) + { + MethodInfo getMethod = indexer.GetGetMethod(); + bool isValueType = indexer.DeclaringType.IsValueType; + bool isStatic = getMethod.IsStatic; + + if (!isStatic) + { + SetupTargetInstance(il, indexer.DeclaringType); + } + + Type[] argTypes = ReflectionUtils.GetParameterTypes(getMethod); + for (int i = 0; i < argTypes.Length; i++) + { + SetupIndexerArgument(il, 2, argTypes[i], i, true); + } + + InvokeMethod(il, isStatic, isValueType, getMethod); + ProcessReturnValue(il, indexer.PropertyType); + il.Emit(OpCodes.Ret); + } + else + { + ThrowInvalidOperationException(il, "Cannot get value of a non-readable indexer"); + } + } + private static void GenerateSetValue(TypeBuilder tb, PropertyInfo property) { MethodBuilder setValueMethod = @@ -440,6 +590,71 @@ namespace Spring.Reflection.Dynamic } } + private static void GenerateSetIndexedValue(TypeBuilder tb, PropertyInfo indexer) + { + MethodBuilder setValueMethod = + tb.DefineMethod("SetValue", METHOD_ATTRIBUTES, typeof(void), + new Type[] { typeof(object), typeof(object), typeof(object[]) }); + setValueMethod.DefineParameter(1, ParameterAttributes.None, "target"); + setValueMethod.DefineParameter(2, ParameterAttributes.None, "value"); + setValueMethod.DefineParameter(3, ParameterAttributes.None, "index"); + + ILGenerator il = setValueMethod.GetILGenerator(); + if (indexer.CanWrite) + { + bool isValueType = indexer.DeclaringType.IsValueType; + if (isValueType) + { + ThrowInvalidOperationException(il, "Cannot set indexer value on a value type due to boxing."); + } + else + { + MethodInfo setMethod = indexer.GetSetMethod(); + bool isStatic = setMethod.IsStatic; + + if (!isStatic) + { + SetupTargetInstance(il, indexer.DeclaringType); + } + + Type[] argTypes = ReflectionUtils.GetParameterTypes(setMethod); + for (int i = 0; i < argTypes.Length - 1; i++) + { + SetupIndexerArgument(il, 3, argTypes[i], i, true); + } + + SetupArgument(il, indexer.PropertyType, 2); + InvokeMethod(il, isStatic, isValueType, setMethod); + il.Emit(OpCodes.Ret); + } + } + else + { + ThrowInvalidOperationException(il, "Cannot set value of a read-only indexer"); + } + } + + private static OpCode[] LdArgOpCodes = { OpCodes.Ldarg_0, OpCodes.Ldarg_1, OpCodes.Ldarg_2, OpCodes.Ldarg_3 }; + + private static void SetupIndexerArgument(ILGenerator il, int indexArgumentPosition, Type argumentType, int argumentPosition, bool isObjectArray) + { + il.Emit(LdArgOpCodes[indexArgumentPosition]); + if (isObjectArray) + { + il.Emit(OpCodes.Ldc_I4, argumentPosition); + il.Emit(OpCodes.Ldelem_Ref); + } + if (argumentType.IsValueType) + { + il.Emit(OpCodes.Unbox, argumentType); + il.Emit(OpCodes.Ldobj, argumentType); + } + else + { + il.Emit(OpCodes.Castclass, argumentType); + } + } + #endregion } diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs index ea0426f9..c76ffeac 100644 --- a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs +++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicReflectionManager.cs @@ -1,5 +1,5 @@ -#region License - +#region License + /* * Copyright © 2002-2007 the original author or authors. * @@ -14,543 +14,817 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ + */ + +#endregion + +#region Imports + +using System; +using System.Collections; +using System.Diagnostics; +using System.Reflection; +using System.Reflection.Emit; +using Spring.Util; + +#if NET_2_0 +using NetDynamicMethod = System.Reflection.Emit.DynamicMethod; +#endif + +#endregion + +namespace Spring.Reflection.Dynamic +{ + /// + /// Represents a Get method + /// + /// the target instance when calling an instance method + /// the value return by the Get method + public delegate object FieldGetterDelegate( object target ); + + /// + /// Represents a Set method + /// + /// the target instance when calling an instance method + /// the value to be set + public delegate void FieldSetterDelegate( object target, object value ); + + /// + /// Represents an Indexer Get method + /// + /// the target instance when calling an instance method + /// + /// the value return by the Get method + public delegate object PropertyGetterDelegate( object target, params object[] index ); + + /// + /// Represents a Set method + /// + /// the target instance when calling an instance method + /// the value to be set + /// + public delegate void PropertySetterDelegate( object target, object value, params object[] index ); + + /// + /// Represents a method + /// + /// the target instance when calling an instance method + /// arguments to be passed to the method + /// the value return by the method. null when calling a void method + public delegate object FunctionDelegate( object target, params object[] args ); + + /// + /// Represents a constructor + /// + /// arguments to be passed to the method + /// the new object instance + public delegate object ConstructorDelegate( params object[] args ); + + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicProperty CreatePropertyCallback( PropertyInfo property ); + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicField CreateFieldCallback( FieldInfo property ); + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicMethod CreateMethodCallback( MethodInfo method ); + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicConstructor CreateConstructorCallback( ConstructorInfo constructor ); + /// + /// Represents a callback method used to create an from a instance. + /// + internal delegate IDynamicIndexer CreateIndexerCallback( PropertyInfo indexer ); + + /// + /// Allows easy access to existing and creation of new dynamic relection members. + /// + /// Aleksandar Seovic + public sealed class DynamicReflectionManager + { + #region Obsolete Code + + + #region Fields + + /// + /// The name of the assembly that defines reflection types created. + /// + public const string ASSEMBLY_NAME = "Spring.DynamicReflection"; + + /// + /// The attributes of the reflection type to generate. + /// + private const TypeAttributes TYPE_ATTRIBUTES = TypeAttributes.BeforeFieldInit | TypeAttributes.Public; + + /// + /// Cache for dynamic property types. + /// + private readonly static IDictionary propertyCache = new Hashtable(); + + /// + /// Cache for dynamic field types. + /// + private readonly static IDictionary fieldCache = new Hashtable(); + + /// + /// Cache for dynamic indexer types. + /// + private readonly static IDictionary indexerCache = new Hashtable(); + + /// + /// Cache for dynamic method types. + /// + private readonly static IDictionary methodCache = new Hashtable(); + + /// + /// Cache for dynamic constructor types. + /// + private readonly static IDictionary constructorCache = new Hashtable(); + + #endregion + + /// + /// Creates an appropriate type builder. + /// + /// + /// The base name to use for the reflection type name. + /// + /// The type builder to use. + internal static TypeBuilder CreateTypeBuilder( string name ) + { + // Generates type name + string typeName = String.Format( "{0}.{1}_{2}", + ASSEMBLY_NAME, name, Guid.NewGuid().ToString( "N" ) ); + + ModuleBuilder module = DynamicCodeManager.GetModuleBuilder( ASSEMBLY_NAME ); + return module.DefineType( typeName, TYPE_ATTRIBUTES ); + } + + /// + /// Returns dynamic property if one exists. + /// + /// Property to look up. + /// callback function that will be called to create the dynamic property + /// An for the given property info. + internal static IDynamicProperty GetDynamicProperty( PropertyInfo property, CreatePropertyCallback createCallback ) + { + lock (propertyCache.SyncRoot) + { + IDynamicProperty dynamicProperty = (IDynamicProperty)propertyCache[property]; + if (dynamicProperty == null) + { + dynamicProperty = createCallback( property ); + propertyCache[property] = dynamicProperty; + } + return dynamicProperty; + } + } + + /// + /// Returns dynamic field if one exists. + /// + /// Field to look up. + /// callback function that will be called to create the dynamic field + /// An for the given field info. + internal static IDynamicField GetDynamicField( FieldInfo field, CreateFieldCallback createCallback ) + { + lock (fieldCache.SyncRoot) + { + IDynamicField dynamicField = (IDynamicField)fieldCache[field]; + if (dynamicField == null) + { + dynamicField = createCallback( field ); + fieldCache[field] = dynamicField; + } + return dynamicField; + } + } + + /// + /// Returns dynamic indexer if one exists. + /// + /// Indexer to look up. + /// callback function that will be called to create the dynamic indexer + /// An for the given indexer. + internal static IDynamicIndexer GetDynamicIndexer( PropertyInfo indexer, CreateIndexerCallback createCallback ) + { + lock (indexerCache.SyncRoot) + { + IDynamicIndexer dynamicIndexer = (IDynamicIndexer)indexerCache[indexer]; + if (dynamicIndexer == null) + { + dynamicIndexer = createCallback( indexer ); + indexerCache[indexer] = dynamicIndexer; + } + return dynamicIndexer; + } + } + + /// + /// Returns dynamic method if one exists. + /// + /// Method to look up. + /// callback function that will be called to create the dynamic method + /// An for the given method. + internal static IDynamicMethod GetDynamicMethod( MethodInfo method, CreateMethodCallback createCallback ) + { + lock (methodCache.SyncRoot) + { + IDynamicMethod dynamicMethod = (IDynamicMethod)methodCache[method]; + if (dynamicMethod == null) + { + dynamicMethod = createCallback( method ); + methodCache[method] = dynamicMethod; + } + return dynamicMethod; + } + } + + /// + /// Returns dynamic constructor if one exists. + /// + /// Constructor to look up. + /// callback function that will be called to create the dynamic constructor + /// An for the given constructor. + internal static IDynamicConstructor GetDynamicConstructor( ConstructorInfo constructor, CreateConstructorCallback createCallback ) + { + lock (constructorCache.SyncRoot) + { + IDynamicConstructor dynamicConstructor = (IDynamicConstructor)constructorCache[constructor]; + if (dynamicConstructor == null) + { + dynamicConstructor = createCallback( constructor ); + constructorCache[constructor] = dynamicConstructor; + } + return dynamicConstructor; + } + } + + /// + /// Saves dynamically generated assembly to disk. + /// Can only be called in DEBUG mode, per ConditionalAttribute rules. + /// + [Conditional( "DEBUG_DYNAMIC" )] + public static void SaveAssembly() + { + DynamicCodeManager.SaveAssembly( ASSEMBLY_NAME ); + } + + #endregion + +#if NET_2_0 + /// + /// Create a new Get method delegate for the specified field using + /// + /// the field to create the delegate for + /// a delegate that can be used to read the field + public static FieldGetterDelegate CreateFieldGetter( FieldInfo fieldInfo ) + { + AssertUtils.ArgumentNotNull( fieldInfo, "You cannot create a delegate for a null value." ); + + System.Reflection.Emit.DynamicMethod dmGetter = new System.Reflection.Emit.DynamicMethod( "getter", typeof( object ), new Type[] { typeof( object ) }, fieldInfo.DeclaringType.Module, true ); + ILGenerator il = dmGetter.GetILGenerator(); + EmitFieldGetter( il, fieldInfo, false ); + return (FieldGetterDelegate)dmGetter.CreateDelegate( typeof( FieldGetterDelegate ) ); + } + + /// + /// Create a new Set method delegate for the specified field using + /// + /// the field to create the delegate for + /// a delegate that can be used to read the field. + /// + /// If the field's returns true, the returned method + /// will throw an when called. + /// + public static FieldSetterDelegate CreateFieldSetter( FieldInfo fieldInfo ) + { + AssertUtils.ArgumentNotNull( fieldInfo, "You cannot create a delegate for a null value." ); + + System.Reflection.Emit.DynamicMethod dmSetter = new System.Reflection.Emit.DynamicMethod( "setter", null, new Type[] { typeof( object ), typeof( object ) }, fieldInfo.DeclaringType.Module, true ); + ILGenerator il = dmSetter.GetILGenerator(); + EmitFieldSetter( il, fieldInfo, false ); + return (FieldSetterDelegate)dmSetter.CreateDelegate( typeof( FieldSetterDelegate ) ); + } + + /// + /// Create a new Get method delegate for the specified property using + /// + /// the property to create the delegate for + /// a delegate that can be used to read the property. + /// + /// If the property's returns false, the returned method + /// will throw an when called. + /// + public static PropertyGetterDelegate CreatePropertyGetter( PropertyInfo propertyInfo ) + { + AssertUtils.ArgumentNotNull( propertyInfo, "You cannot create a delegate for a null value." ); + + NetDynamicMethod dm = new NetDynamicMethod( string.Empty, typeof( object ), new Type[] { typeof( object ), typeof( object[] ) }, propertyInfo.DeclaringType.Module, true ); + ILGenerator il = dm.GetILGenerator(); + EmitPropertyGetter( il, propertyInfo, false ); + return (PropertyGetterDelegate)dm.CreateDelegate( typeof( PropertyGetterDelegate ) ); + } + + /// + /// Create a new Set method delegate for the specified property using + /// + /// the property to create the delegate for + /// a delegate that can be used to write the property. + /// + /// If the property's returns false, the returned method + /// will throw an when called. + /// + public static PropertySetterDelegate CreatePropertySetter( PropertyInfo propertyInfo ) + { + AssertUtils.ArgumentNotNull( propertyInfo, "You cannot create a delegate for a null value." ); + + NetDynamicMethod dm = new NetDynamicMethod( string.Empty, null, new Type[] { typeof( object ), typeof( object ), typeof( object[] ) }, propertyInfo.DeclaringType.Module, true ); + ILGenerator il = dm.GetILGenerator(); + EmitPropertySetter( il, propertyInfo, false ); + return (PropertySetterDelegate)dm.CreateDelegate( typeof( PropertySetterDelegate ) ); + } + + /// + /// Create a new method delegate for the specified method using + /// + /// the method to create the delegate for + /// a delegate that can be used to invoke the method. + public static FunctionDelegate CreateMethod( MethodInfo methodInfo ) + { + AssertUtils.ArgumentNotNull( methodInfo, "You cannot create a delegate for a null value." ); + + NetDynamicMethod dm = new NetDynamicMethod( string.Empty, typeof( object ), new Type[] { typeof( object ), typeof( object[] ) }, methodInfo.DeclaringType.Module, true ); + ILGenerator il = dm.GetILGenerator(); + EmitInvokeMethod( il, methodInfo, false ); + return (FunctionDelegate)dm.CreateDelegate( typeof( FunctionDelegate ) ); + } + + /// + /// Creates a new delegate for the specified constructor. + /// + ///the constructor to create the delegate for + ///delegate that can be used to invoke the constructor. + public static ConstructorDelegate CreateConstructor(ConstructorInfo constructorInfo) + { + AssertUtils.ArgumentNotNull(constructorInfo, "You cannot create a dynamic constructor for a null value."); + + System.Reflection.Emit.DynamicMethod dmGetter = new System.Reflection.Emit.DynamicMethod( string.Empty, typeof( object ), new Type[] { typeof( object[] ) }, constructorInfo.DeclaringType.Module, true ); + ILGenerator il = dmGetter.GetILGenerator(); + EmitInvokeConstructor( il, constructorInfo, false ); + ConstructorDelegate ctor = (ConstructorDelegate)dmGetter.CreateDelegate( typeof( ConstructorDelegate ) ); + return ctor; + } +#endif + + #region Shared Code Generation + + private static void EmitFieldGetter( ILGenerator il, FieldInfo fieldInfo, bool isInstanceMethod ) + { + if (fieldInfo.IsLiteral) + { + object value = fieldInfo.GetValue( null ); + EmitConstant( il, value ); + } + else if (fieldInfo.IsStatic) + { + il.Emit( OpCodes.Ldsfld, fieldInfo ); + } + else + { + EmitTarget( il, fieldInfo.DeclaringType, isInstanceMethod ); + il.Emit( OpCodes.Ldfld, fieldInfo ); + } + + if (fieldInfo.FieldType.IsValueType) + { + il.Emit( OpCodes.Box, fieldInfo.FieldType ); + } + il.Emit( OpCodes.Ret ); + } + + internal static void EmitFieldSetter( ILGenerator il, FieldInfo fieldInfo, bool isInstanceMethod ) + { + if (!fieldInfo.IsLiteral + && !fieldInfo.IsInitOnly + && !(fieldInfo.DeclaringType.IsValueType && !fieldInfo.IsStatic)) + { + if (!fieldInfo.IsStatic) + { + EmitTarget( il, fieldInfo.DeclaringType, isInstanceMethod ); + } + il.Emit( OpCodes.Ldarg_1 ); + if (fieldInfo.FieldType.IsValueType) + { + EmitUnbox( il, fieldInfo.FieldType ); + } + if (fieldInfo.IsStatic) + { + il.Emit( OpCodes.Stsfld, fieldInfo ); + } + else + { + il.Emit( OpCodes.Stfld, fieldInfo ); + } + il.Emit( OpCodes.Ret ); + } + else + { + EmitThrowInvalidOperationException( il, string.Format( "Cannot write to read-only field '{0}.{1}'", fieldInfo.DeclaringType.FullName, fieldInfo.Name ) ); + } + } + + internal static void EmitPropertyGetter( ILGenerator il, PropertyInfo propertyInfo, bool isInstanceMethod ) + { + if (propertyInfo.CanRead) + { + MethodInfo getMethod = propertyInfo.GetGetMethod( true ); + EmitInvokeMethod( il, getMethod, isInstanceMethod ); + } + else + { + EmitThrowInvalidOperationException( il, string.Format( "Cannot read from write-only property '{0}.{1}'", propertyInfo.DeclaringType.FullName, propertyInfo.Name ) ); + } + } + + internal static void EmitPropertySetter( ILGenerator il, PropertyInfo propertyInfo, bool isInstanceMethod ) + { + MethodInfo method = propertyInfo.GetSetMethod( true ); + + if (propertyInfo.CanWrite + && !(propertyInfo.DeclaringType.IsValueType && !method.IsStatic)) + { + // Note: last arg is property value! + // property set method signature: + // void set_MyOtherProperty( [indexArg1, indexArg2, ...], string value) + + const int paramsArrayPosition = 2; + IDictionary outArgs = new Hashtable(); + ParameterInfo[] args = propertyInfo.GetIndexParameters(); // get indexParameters here! + for (int i = 0; i < args.Length; i++) + { + SetupOutputArgument( il, paramsArrayPosition, args[i], outArgs ); + } + + // load target + if (!method.IsStatic) + { + EmitTarget( il, method.DeclaringType, isInstanceMethod ); + } + + // load indexer arguments + for (int i = 0; i < args.Length; i++) + { + SetupMethodArgument( il, paramsArrayPosition, args[i], outArgs ); + } + + // load value + il.Emit( OpCodes.Ldarg_1 ); + if (propertyInfo.PropertyType.IsValueType) + { + EmitUnbox( il, propertyInfo.PropertyType ); + } + + // call setter + EmitCall( il, method ); + + for (int i = 0; i < args.Length; i++) + { + ProcessOutputArgument( il, paramsArrayPosition, args[i], outArgs ); + } + il.Emit( OpCodes.Ret ); + } + else + { + EmitThrowInvalidOperationException( il, string.Format( "Cannot write to read-only property '{0}.{1}'", propertyInfo.DeclaringType.FullName, propertyInfo.Name ) ); + } + } + + /// + /// Delegates a Method(object target, params object[] args) call to the actual underlying method. + /// + internal static void EmitInvokeMethod( ILGenerator il, MethodInfo method, bool isInstanceMethod ) + { + int paramsArrayPosition = (isInstanceMethod) ? 2 : 1; + ParameterInfo[] args = method.GetParameters(); + IDictionary outArgs = new Hashtable(); + for (int i = 0; i < args.Length; i++) + { + SetupOutputArgument( il, paramsArrayPosition, args[i], outArgs ); + } + + if (!method.IsStatic) + { + EmitTarget( il, method.DeclaringType, isInstanceMethod ); + } + + for (int i = 0; i < args.Length; i++) + { + SetupMethodArgument( il, paramsArrayPosition, args[i], outArgs ); + } + + EmitCall( il, method ); + + for (int i = 0; i < args.Length; i++) + { + ProcessOutputArgument( il, paramsArrayPosition, args[i], outArgs ); + } + + EmitMethodReturn( il, method.ReturnType ); + } + + internal static void EmitInvokeConstructor(ILGenerator il, ConstructorInfo constructor, bool isInstanceMethod) + { + int paramsArrayPosition = (isInstanceMethod) ? 1 : 0; + ParameterInfo[] args = constructor.GetParameters(); -#endregion + IDictionary outArgs = new Hashtable(); + for (int i = 0; i < args.Length; i++) + { + SetupOutputArgument( il, paramsArrayPosition, args[i], outArgs ); + } -#region Imports + for (int i = 0; i < args.Length; i++) + { + SetupMethodArgument(il, paramsArrayPosition, args[i], null); + } + + il.Emit(OpCodes.Newobj, constructor); -using System; -using System.Collections; -using System.Diagnostics; -using System.Reflection; -using System.Reflection.Emit; -using Spring.Util; + for (int i = 0; i < args.Length; i++) + { + ProcessOutputArgument( il, paramsArrayPosition, args[i], outArgs ); + } -#if NET_2_0 -using NetDynamicMethod = System.Reflection.Emit.DynamicMethod; -#endif - -#endregion - -namespace Spring.Reflection.Dynamic -{ - /// - /// Represents a Get method - /// - /// the target instance when calling an instance method - /// the value return by the Get method - public delegate object GetterDelegate(object target); - - /// - /// Represents a Set method - /// - /// the target instance when calling an instance method - /// the value to be set - public delegate void SetterDelegate(object target, object value); - - /// - /// Represents a method - /// - /// the target instance when calling an instance method - /// arguments to be passed to the method - /// the value return by the method. null when calling a void method - public delegate object FunctionDelegate(object target, params object[] args); - - /// - /// Represents a callback method used to create an from a instance. - /// - internal delegate IDynamicProperty CreatePropertyCallback(PropertyInfo property); - /// - /// Represents a callback method used to create an from a instance. - /// - internal delegate IDynamicField CreateFieldCallback(FieldInfo property); - /// - /// Represents a callback method used to create an from a instance. - /// - internal delegate IDynamicMethod CreateMethodCallback(MethodInfo method); - /// - /// Represents a callback method used to create an from a instance. - /// - internal delegate IDynamicConstructor CreateConstructorCallback(ConstructorInfo constructor); - /// - /// Represents a callback method used to create an from a instance. - /// - internal delegate IDynamicIndexer CreateIndexerCallback(PropertyInfo indexer); - - /// - /// Allows easy access to existing and creation of new dynamic relection members. - /// - /// Aleksandar Seovic - public sealed class DynamicReflectionManager - { - #region Fields - - /// - /// The name of the assembly that defines reflection types created. - /// - public const string ASSEMBLY_NAME = "Spring.DynamicReflection"; - - /// - /// The attributes of the reflection type to generate. - /// - private const TypeAttributes TYPE_ATTRIBUTES = TypeAttributes.BeforeFieldInit | TypeAttributes.Public; - - /// - /// Cache for dynamic property types. - /// - private readonly static IDictionary propertyCache = new Hashtable(); - - /// - /// Cache for dynamic field types. - /// - private readonly static IDictionary fieldCache = new Hashtable(); - - /// - /// Cache for dynamic indexer types. - /// - private readonly static IDictionary indexerCache = new Hashtable(); - - /// - /// Cache for dynamic method types. - /// - private readonly static IDictionary methodCache = new Hashtable(); - - /// - /// Cache for dynamic constructor types. - /// - private readonly static IDictionary constructorCache = new Hashtable(); - - #endregion - - #region Public Methods - - /// - /// Creates an appropriate type builder. - /// - /// - /// The base name to use for the reflection type name. - /// - /// The type builder to use. - internal static TypeBuilder CreateTypeBuilder(string name) - { - // Generates type name - string typeName = String.Format("{0}.{1}_{2}", - ASSEMBLY_NAME, name, Guid.NewGuid().ToString("N")); - - ModuleBuilder module = DynamicCodeManager.GetModuleBuilder(ASSEMBLY_NAME); - return module.DefineType(typeName, TYPE_ATTRIBUTES); + EmitMethodReturn(il, constructor.DeclaringType); } - - /// - /// Returns dynamic property if one exists. - /// - /// Property to look up. - /// callback function that will be called to create the dynamic property - /// An for the given property info. - internal static IDynamicProperty GetDynamicProperty(PropertyInfo property, CreatePropertyCallback createCallback) - { - lock (propertyCache.SyncRoot) - { - IDynamicProperty dynamicProperty = (IDynamicProperty)propertyCache[property]; - if (dynamicProperty == null) - { - dynamicProperty = createCallback(property); - propertyCache[property] = dynamicProperty; - } - return dynamicProperty; - } - } - - /// - /// Returns dynamic field if one exists. - /// - /// Field to look up. - /// callback function that will be called to create the dynamic field - /// An for the given field info. - internal static IDynamicField GetDynamicField(FieldInfo field, CreateFieldCallback createCallback) - { - lock (fieldCache.SyncRoot) - { - IDynamicField dynamicField = (IDynamicField)fieldCache[field]; - if (dynamicField == null) - { - dynamicField = createCallback(field); - fieldCache[field] = dynamicField; - } - return dynamicField; - } - } - - /// - /// Returns dynamic indexer if one exists. - /// - /// Indexer to look up. - /// callback function that will be called to create the dynamic indexer - /// An for the given indexer. - internal static IDynamicIndexer GetDynamicIndexer(PropertyInfo indexer, CreateIndexerCallback createCallback) - { - lock (indexerCache.SyncRoot) - { - IDynamicIndexer dynamicIndexer = (IDynamicIndexer)indexerCache[indexer]; - if (dynamicIndexer == null) - { - dynamicIndexer = createCallback(indexer); - indexerCache[indexer] = dynamicIndexer; - } - return dynamicIndexer; - } - } - - /// - /// Returns dynamic method if one exists. - /// - /// Method to look up. - /// callback function that will be called to create the dynamic method - /// An for the given method. - internal static IDynamicMethod GetDynamicMethod(MethodInfo method, CreateMethodCallback createCallback) - { - lock (methodCache.SyncRoot) - { - IDynamicMethod dynamicMethod = (IDynamicMethod)methodCache[method]; - if (dynamicMethod == null) - { - dynamicMethod = createCallback(method); - methodCache[method] = dynamicMethod; - } - return dynamicMethod; - } - } - - /// - /// Returns dynamic constructor if one exists. - /// - /// Constructor to look up. - /// callback function that will be called to create the dynamic constructor - /// An for the given constructor. - internal static IDynamicConstructor GetDynamicConstructor(ConstructorInfo constructor, CreateConstructorCallback createCallback) - { - lock (constructorCache.SyncRoot) - { - IDynamicConstructor dynamicConstructor = (IDynamicConstructor)constructorCache[constructor]; - if (dynamicConstructor == null) - { - dynamicConstructor = createCallback(constructor); - constructorCache[constructor] = dynamicConstructor; - } - return dynamicConstructor; - } - } - - /// - /// Saves dynamically generated assembly to disk. - /// Can only be called in DEBUG mode, per ConditionalAttribute rules. - /// - [Conditional("DEBUG_DYNAMIC")] - public static void SaveAssembly() - { - DynamicCodeManager.SaveAssembly(ASSEMBLY_NAME); - } - - #endregion - -#if NET_2_0 - private static readonly ConstructorInfo NewInvalidOperationException = - typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) }); - - /// - /// Create a new Get method delegate for the specified field using - /// - /// the field to create the delegate for - /// a delegate that can be used to read the field - public static GetterDelegate CreateFieldGetter(FieldInfo fieldInfo) - { - AssertUtils.ArgumentNotNull(fieldInfo, "You cannot create a delegate for a null value."); - - System.Reflection.Emit.DynamicMethod dmGetter = new System.Reflection.Emit.DynamicMethod("getter", typeof(object), new Type[] { typeof(object) }, fieldInfo.DeclaringType.Module, true); - ILGenerator il = dmGetter.GetILGenerator(); - if (fieldInfo.IsLiteral) - { - object value = fieldInfo.GetValue(null); - EmitConstant(il, value); - } - else if (fieldInfo.IsStatic) - { - il.Emit(OpCodes.Ldsfld, fieldInfo); - } - else - { - EmitTarget(il, fieldInfo.DeclaringType); - il.Emit(OpCodes.Ldfld, fieldInfo); - } - - if (fieldInfo.FieldType.IsValueType) - { - il.Emit(OpCodes.Box, fieldInfo.FieldType); - } - il.Emit(OpCodes.Ret); - return (GetterDelegate)dmGetter.CreateDelegate(typeof(GetterDelegate)); - } - - /// - /// Create a new Set method delegate for the specified field using - /// - /// the field to create the delegate for - /// a delegate that can be used to read the field. - /// - /// If the field's returns true, the returned method - /// will throw an when called. - /// - public static SetterDelegate CreateFieldSetter(FieldInfo fieldInfo) - { - AssertUtils.ArgumentNotNull(fieldInfo, "You cannot create a delegate for a null value."); - - System.Reflection.Emit.DynamicMethod dmSetter = new System.Reflection.Emit.DynamicMethod("setter", null, new Type[] { typeof(object), typeof(object) }, fieldInfo.DeclaringType.Module, true); - ILGenerator il = dmSetter.GetILGenerator(); - - if (!fieldInfo.IsLiteral - && !fieldInfo.IsInitOnly - && !(fieldInfo.DeclaringType.IsValueType && !fieldInfo.IsStatic)) - { - if (!fieldInfo.IsStatic) - { - EmitTarget(il, fieldInfo.DeclaringType); - } - il.Emit(OpCodes.Ldarg_1); - if (fieldInfo.FieldType.IsValueType) - { - il.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType); - } - if (fieldInfo.IsStatic) - { - il.Emit(OpCodes.Stsfld, fieldInfo); - } - else - { - il.Emit(OpCodes.Stfld, fieldInfo); - } - il.Emit(OpCodes.Ret); - } - else - { - EmitThrowInvalidOperationException(il, string.Format("Cannot write to constant field '{0}.{1}'", fieldInfo.DeclaringType.FullName, fieldInfo.Name)); - } - return (SetterDelegate)dmSetter.CreateDelegate(typeof(SetterDelegate)); - } - - /// - /// Create a new Get method delegate for the specified property using - /// - /// the property to create the delegate for - /// a delegate that can be used to read the property. - /// - /// If the property's returns false, the returned method - /// will throw an when called. - /// - public static GetterDelegate CreatePropertyGetter(PropertyInfo propertyInfo) - { - AssertUtils.ArgumentNotNull(propertyInfo, "You cannot create a delegate for a null value."); - - NetDynamicMethod dm = new NetDynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object) }, propertyInfo.DeclaringType.Module, true); - ILGenerator il = dm.GetILGenerator(); - - if (propertyInfo.CanRead) - { - MethodInfo method = propertyInfo.GetGetMethod(true); - if (!method.IsStatic) - { - EmitTarget(il, method.DeclaringType); - } - EmitCall(il, method); - if (propertyInfo.PropertyType.IsValueType) - { - il.Emit(OpCodes.Box, propertyInfo.PropertyType); - } - il.Emit(OpCodes.Ret); - } - else - { - EmitThrowInvalidOperationException(il, string.Format("Cannot read from write-only property '{0}.{1}'", propertyInfo.DeclaringType.FullName, propertyInfo.Name)); - } - return (GetterDelegate)dm.CreateDelegate(typeof(GetterDelegate)); - } - - /// - /// Create a new Set method delegate for the specified property using - /// - /// the property to create the delegate for - /// a delegate that can be used to write the property. - /// - /// If the property's returns false, the returned method - /// will throw an when called. - /// - public static SetterDelegate CreatePropertySetter(PropertyInfo propertyInfo) - { - AssertUtils.ArgumentNotNull(propertyInfo, "You cannot create a delegate for a null value."); - - NetDynamicMethod dm = new NetDynamicMethod(string.Empty, null, new Type[] { typeof(object), typeof(object) }, propertyInfo.DeclaringType.Module, true); - ILGenerator il = dm.GetILGenerator(); - - if (propertyInfo.CanWrite - && !(propertyInfo.DeclaringType.IsValueType && !propertyInfo.GetSetMethod(true).IsStatic)) - { - MethodInfo method = propertyInfo.GetSetMethod(true); - if (!method.IsStatic) - { - EmitTarget(il, method.DeclaringType); - } - - il.Emit(OpCodes.Ldarg_1); - if (propertyInfo.PropertyType.IsValueType) - { - il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType); - } - EmitCall(il, method); - il.Emit(OpCodes.Ret); - } - else - { - EmitThrowInvalidOperationException(il, string.Format("Cannot write to read-only property '{0}.{1}'", propertyInfo.DeclaringType.FullName, propertyInfo.Name)); - } - - return (SetterDelegate)dm.CreateDelegate(typeof(SetterDelegate)); - } - - /// - /// Create a new method delegate for the specified method using - /// - /// the method to create the delegate for - /// a delegate that can be used to invoke the method. - private static FunctionDelegate CreateFunctionDelegate(MethodInfo method) - { - NetDynamicMethod dm = new NetDynamicMethod(string.Empty, typeof(object), new Type[] { typeof(object), typeof(object[]) }, method.DeclaringType.Module, true); - ILGenerator il = dm.GetILGenerator(); - ParameterInfo[] parameters = method.GetParameters(); - for (int i = 0; i < parameters.Length; i++) - { - ParameterInfo p = parameters[i]; - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldc_I4, i); - il.Emit(OpCodes.Ldelem_Ref); - if (p.ParameterType.IsValueType) - { - il.Emit(OpCodes.Unbox_Any, p.ParameterType); - } - } - - if (!method.IsStatic) - { - EmitTarget(il, method.DeclaringType); - } - EmitCall(il, method); - - if (method.ReturnType == typeof(void)) - { - il.Emit(OpCodes.Ldnull); - } - il.Emit(OpCodes.Ret); - return (FunctionDelegate)dm.CreateDelegate(typeof(FunctionDelegate)); - } - - private static void EmitTarget(ILGenerator il, Type targetType) - { - il.Emit(OpCodes.Ldarg_0); - if (targetType.IsValueType) - { - LocalBuilder local = il.DeclareLocal(targetType); - il.Emit(OpCodes.Unbox_Any, targetType); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloca_S, 0); - } - } - - private static void EmitCall(ILGenerator il, MethodInfo method) - { - il.Emit((method.IsVirtual) ? OpCodes.Callvirt : OpCodes.Call, method); - } - - private static void EmitConstant(ILGenerator il, object value) - { - if (value is String) - { - il.Emit(OpCodes.Ldstr, (string)value); - return; - } - - if (value is bool) - { - if ((bool)value) - { - il.Emit(OpCodes.Ldc_I4_1); - } - else - { - il.Emit(OpCodes.Ldc_I4_0); - } - return; - } - - if (value is Char) - { - il.Emit(OpCodes.Ldc_I4, (Char)value); - il.Emit(OpCodes.Conv_I2); - return; - } - - if (value is byte) - { - il.Emit(OpCodes.Ldc_I4, (byte)value); - il.Emit(OpCodes.Conv_I1); - } - else if (value is Int16) - { - il.Emit(OpCodes.Ldc_I4, (Int16)value); - il.Emit(OpCodes.Conv_I2); - } - else if (value is Int32) - { - il.Emit(OpCodes.Ldc_I4, (Int32)value); - } - else if (value is Int64) - { - il.Emit(OpCodes.Ldc_I8, (Int64)value); - } - else if (value is UInt16) - { - il.Emit(OpCodes.Ldc_I4, (UInt16)value); - il.Emit(OpCodes.Conv_U2); - } - else if (value is UInt32) - { - il.Emit(OpCodes.Ldc_I4, (UInt32)value); - il.Emit(OpCodes.Conv_U4); - } - else if (value is UInt64) - { - il.Emit(OpCodes.Ldc_I8, (UInt64)value); - il.Emit(OpCodes.Conv_U8); - } - else if (value is Single) - { - il.Emit(OpCodes.Ldc_R4, (Single)value); - } - else if (value is Double) - { - il.Emit(OpCodes.Ldc_R8, (Double)value); - } - } - - /// - /// Generates code that throws . - /// - /// IL generator to use. - /// Error message to use. - private static void EmitThrowInvalidOperationException(ILGenerator il, string message) - { - il.Emit(OpCodes.Ldstr, message); - il.Emit(OpCodes.Newobj, NewInvalidOperationException); - il.Emit(OpCodes.Throw); - } -#endif - } + + #endregion + + private static OpCode[] LdArgOpCodes = { OpCodes.Ldarg_0, OpCodes.Ldarg_1, OpCodes.Ldarg_2 }; + + private static void SetupOutputArgument( ILGenerator il, int paramsArrayPosition, ParameterInfo argInfo, IDictionary outArgs ) + { + if (!IsOutputOrRefArgument( argInfo )) + return; + + Type argType = argInfo.ParameterType.GetElementType(); + + LocalBuilder lb = il.DeclareLocal( argType ); + if (!argInfo.IsOut) + { + PushParamsArgumentValue( il, paramsArrayPosition, argType, argInfo.Position ); + il.Emit( OpCodes.Stloc, lb ); + } + outArgs[argInfo.Position] = lb; + } + + private static bool IsOutputOrRefArgument( ParameterInfo argInfo ) + { + return argInfo.IsOut || argInfo.ParameterType.Name.EndsWith( "&" ); + } + + private static void ProcessOutputArgument( ILGenerator il, int paramsArrayPosition, ParameterInfo argInfo, IDictionary outArgs ) + { + if (!IsOutputOrRefArgument( argInfo )) + return; + + Type argType = argInfo.ParameterType.GetElementType(); + + il.Emit( LdArgOpCodes[paramsArrayPosition] ); + il.Emit( OpCodes.Ldc_I4, argInfo.Position ); + il.Emit( OpCodes.Ldloc, (LocalBuilder)outArgs[argInfo.Position] ); + if (argType.IsValueType) + { + il.Emit( OpCodes.Box, argType ); + } + il.Emit( OpCodes.Stelem_Ref ); + } + + private static void SetupMethodArgument( ILGenerator il, int paramsArrayPosition, ParameterInfo argInfo, IDictionary outArgs ) + { + if ( IsOutputOrRefArgument( argInfo )) + { + il.Emit( OpCodes.Ldloca_S, (LocalBuilder)outArgs[argInfo.Position] ); + } + else + { + PushParamsArgumentValue( il, paramsArrayPosition, argInfo.ParameterType, argInfo.Position ); + } + } + + private static void PushParamsArgumentValue( ILGenerator il, int paramsArrayPosition, Type argumentType, int argumentPosition ) + { + il.Emit( LdArgOpCodes[paramsArrayPosition] ); + il.Emit( OpCodes.Ldc_I4, argumentPosition ); + il.Emit( OpCodes.Ldelem_Ref ); + if (argumentType.IsValueType) + { + // call ConvertArgumentIfNecessary() to convert e.g. int32 to double if necessary + il.Emit( OpCodes.Ldtoken, argumentType ); + EmitCall( il, FnGetTypeFromHandle ); + il.Emit( OpCodes.Ldc_I4, argumentPosition ); + EmitCall( il, FnConvertArgumentIfNecessary ); + EmitUnbox( il, argumentType ); + } + else + { + il.Emit( OpCodes.Castclass, argumentType ); + } + } + + private static void EmitUnbox( ILGenerator il, Type argumentType ) + { +#if NET_2_0 + il.Emit( OpCodes.Unbox_Any, argumentType ); +#else + il.Emit(OpCodes.Unbox, argumentType); + il.Emit(OpCodes.Ldobj, argumentType); +#endif + } + + /// + /// Generates code to process return value if necessary. + /// + /// IL generator to use. + /// Type of the return value. + private static void EmitMethodReturn( ILGenerator il, Type returnValueType ) + { + if (returnValueType == typeof( void )) + { + il.Emit( OpCodes.Ldnull ); + } + else if (returnValueType.IsValueType) + { + il.Emit( OpCodes.Box, returnValueType ); + } + il.Emit( OpCodes.Ret ); + } + + private delegate Type GetTypeFromHandleDelegate( RuntimeTypeHandle handle ); + private static readonly MethodInfo FnGetTypeFromHandle = new GetTypeFromHandleDelegate( Type.GetTypeFromHandle ).Method; + private delegate object ChangeTypeDelegate( object value, Type targetType, int argIndex ); + private static readonly MethodInfo FnConvertArgumentIfNecessary = new ChangeTypeDelegate( ConvertValueTypeArgumentIfNecessary ).Method; + + /// + /// Converts to an instance of if necessary to + /// e.g. avoid e.g. double/int cast exceptions. + /// + /// + /// + /// This method mimics the behavior of the compiler that + /// automatically performs casts like int to double in "Math.Sqrt(4)".
+ /// See about implicit, widening type conversions on MSDN - Type Conversion Tables + ///
+ /// + /// Note: is expected to be a value type! + /// + ///
+ public static object ConvertValueTypeArgumentIfNecessary( object value, Type targetType, int argIndex ) + { + // targetType is guaranteed to be a ValueType! + if (value == null) + { + throw new InvalidCastException( string.Format( "Cannot convert NULL at position {0} to argument type {1}", argIndex, targetType.FullName ) ); + } + + Type valueType = value.GetType(); + + // no conversion necessary? + if (valueType == targetType) + { + return value; + } + + if (!valueType.IsValueType) + { + // we're facing a reftype/valuetype mix that never can convert + throw new InvalidCastException( string.Format( "Cannot convert value '{0}' of type {1} at position {2} to argument type {3}", value, valueType.FullName, argIndex, targetType.FullName ) ); + } + + // we're dealing only with ValueType's now - try to convert them + try + { + // TODO: allow widening conversions only + return Convert.ChangeType( value, targetType ); + } + catch (Exception ex) + { + throw new InvalidCastException( string.Format( "Cannot convert value '{0}' of type {1} at position {2} to argument type {3}", value, valueType.FullName, argIndex, targetType.FullName ), ex ); + } + } + + private static void EmitTarget( ILGenerator il, Type targetType, bool isInstanceMethod ) + { + il.Emit( (isInstanceMethod) ? OpCodes.Ldarg_1 : OpCodes.Ldarg_0 ); + if (targetType.IsValueType) + { + LocalBuilder local = il.DeclareLocal( targetType ); + EmitUnbox( il, targetType ); + il.Emit( OpCodes.Stloc_0 ); + il.Emit( OpCodes.Ldloca_S, 0 ); + } + else + { + il.Emit( OpCodes.Castclass, targetType ); + } + } + + private static void EmitCall( ILGenerator il, MethodInfo method ) + { + il.EmitCall( (method.IsVirtual) ? OpCodes.Callvirt : OpCodes.Call, method, null ); + } + + private static void EmitConstant( ILGenerator il, object value ) + { + if (value is String) + { + il.Emit( OpCodes.Ldstr, (string)value ); + return; + } + + if (value is bool) + { + if ((bool)value) + { + il.Emit( OpCodes.Ldc_I4_1 ); + } + else + { + il.Emit( OpCodes.Ldc_I4_0 ); + } + return; + } + + if (value is Char) + { + il.Emit( OpCodes.Ldc_I4, (Char)value ); + il.Emit( OpCodes.Conv_I2 ); + return; + } + + if (value is byte) + { + il.Emit( OpCodes.Ldc_I4, (byte)value ); + il.Emit( OpCodes.Conv_I1 ); + } + else if (value is Int16) + { + il.Emit( OpCodes.Ldc_I4, (Int16)value ); + il.Emit( OpCodes.Conv_I2 ); + } + else if (value is Int32) + { + il.Emit( OpCodes.Ldc_I4, (Int32)value ); + } + else if (value is Int64) + { + il.Emit( OpCodes.Ldc_I8, (Int64)value ); + } + else if (value is UInt16) + { + il.Emit( OpCodes.Ldc_I4, (UInt16)value ); + il.Emit( OpCodes.Conv_U2 ); + } + else if (value is UInt32) + { + il.Emit( OpCodes.Ldc_I4, (UInt32)value ); + il.Emit( OpCodes.Conv_U4 ); + } + else if (value is UInt64) + { + il.Emit( OpCodes.Ldc_I8, (UInt64)value ); + il.Emit( OpCodes.Conv_U8 ); + } + else if (value is Single) + { + il.Emit( OpCodes.Ldc_R4, (Single)value ); + } + else if (value is Double) + { + il.Emit( OpCodes.Ldc_R8, (Double)value ); + } + } + + private static readonly ConstructorInfo NewInvalidOperationException = + typeof( InvalidOperationException ).GetConstructor( new Type[] { typeof( string ) } ); + + /// + /// Generates code that throws . + /// + /// IL generator to use. + /// Error message to use. + private static void EmitThrowInvalidOperationException( ILGenerator il, string message ) + { + il.Emit( OpCodes.Ldstr, message ); + il.Emit( OpCodes.Newobj, NewInvalidOperationException ); + il.Emit( OpCodes.Throw ); + } + } } \ No newline at end of file diff --git a/src/Spring/Spring.Web/Spring.Web.2003.csproj b/src/Spring/Spring.Web/Spring.Web.2003.csproj index be8b6f6b..288baa03 100644 --- a/src/Spring/Spring.Web/Spring.Web.2003.csproj +++ b/src/Spring/Spring.Web/Spring.Web.2003.csproj @@ -260,16 +260,6 @@ SubType = "Code" BuildAction = "Compile" /> - - + + + + + @@ -88,4 +90,6 @@ namespace Spring.Web.Support return null; } } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeAssemblyHolderTests.cs b/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeAssemblyHolderTests.cs index 85054f43..c8bc66e8 100644 --- a/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeAssemblyHolderTests.cs +++ b/test/Spring/Spring.Core.Tests/Core/TypeResolution/TypeAssemblyHolderTests.cs @@ -21,10 +21,13 @@ #region Imports using System; -using System.Collections.Generic; using NUnit.Framework; using Spring.Objects; +#if NET_2_0 +using System.Collections.Generic; +#endif + #endregion namespace Spring.Core.TypeResolution @@ -39,10 +42,6 @@ namespace Spring.Core.TypeResolution [Test] public void CanTakeQualifiedType() { - string tn = typeof(int[]).AssemblyQualifiedName; - tn = typeof(TestGenericObject[]).AssemblyQualifiedName; - tn = typeof(List).AssemblyQualifiedName; - Type testType = typeof(TestObject); TypeAssemblyHolder tah = new TypeAssemblyHolder(testType.AssemblyQualifiedName); Assert.IsTrue(tah.IsAssemblyQualified); diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/BasePropertyTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/BasePropertyTests.cs index 87d2328c..31238a25 100644 --- a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/BasePropertyTests.cs +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/BasePropertyTests.cs @@ -1,6 +1,9 @@ using System; +using System.CodeDom.Compiler; using System.Diagnostics; +using System.IO; using System.Reflection; +using System.Text; using NUnit.Framework; using Spring.Context.Support; @@ -74,6 +77,44 @@ namespace Spring.Reflection.Dynamic Assert.AreEqual(mostImportantDayInTheWorldEver.Year, year.GetValue(mostImportantDayInTheWorldEver)); } + [Test] + public void AccessInheritedPropertyFromBaseClass() + { + IDynamicProperty p = Create(typeof(ClassWithNonReadableProperty).GetProperty("MyBaseProperty")); + BaseClass baseObject = new BaseClass(); + baseObject.MyBaseProperty = "testtext"; + Assert.AreEqual("testtext", p.GetValue(baseObject)); + } + + [Test] + public void AccessInheritedPropertyFromDerivedClass() + { + IDynamicProperty p = Create(typeof(BaseClass).GetProperty("MyBaseProperty")); + ClassWithNonReadableProperty derivedObject = new ClassWithNonReadableProperty(); + derivedObject.MyBaseProperty = "testtext"; + Assert.AreEqual("testtext", p.GetValue(derivedObject)); + } + + [Test] + public void AccessOverriddenProperty() + { + IDynamicProperty pVirt = Create(typeof(BaseClass).GetProperty("MyVirtualBaseProperty")); + IDynamicProperty pOverridden = Create(typeof(ClassWithNonReadableProperty).GetProperty("MyVirtualBaseProperty")); + + Assert.AreEqual("MyVirtualBasePropertyText", pVirt.GetValue(new BaseClass())); + try + { + Assert.AreEqual("MyVirtualBasePropertyText", pOverridden.GetValue(new BaseClass())); + Assert.Fail(); + } + catch (InvalidCastException) + { + } + Assert.AreEqual("MyOverridenDerivedPropertyText", pVirt.GetValue(new ClassWithNonReadableProperty())); + Assert.AreEqual("MyOverridenDerivedPropertyText", pOverridden.GetValue(new ClassWithNonReadableProperty())); + } + + [Test] public void TestStaticProperties() { @@ -178,7 +219,7 @@ namespace Spring.Reflection.Dynamic #region IL generation helper classes (they help if you look at them in Reflector ;-) - public class ValueTypeProperty : IDynamicProperty + public class ValueTypeProperty //: IDynamicProperty { public object GetValue(object target) { @@ -191,7 +232,7 @@ namespace Spring.Reflection.Dynamic } } - public class ValueTypeTarget : IDynamicProperty + public class ValueTypeTarget //: IDynamicProperty { public object GetValue(object target) { @@ -205,7 +246,7 @@ namespace Spring.Reflection.Dynamic } } - public class StaticProperty : IDynamicProperty + public class StaticProperty //: IDynamicProperty { public object GetValue(object target) { @@ -256,7 +297,23 @@ namespace Spring.Reflection.Dynamic } } - public class ClassWithNonReadableProperty + public class BaseClass + { + private string myBaseProperty; + + public string MyBaseProperty + { + set { myBaseProperty = value; } + get { return myBaseProperty; } + } + + public virtual string MyVirtualBaseProperty + { + get { return "MyVirtualBasePropertyText"; } + } + } + + public class ClassWithNonReadableProperty : BaseClass { private string myProperty; @@ -264,6 +321,11 @@ namespace Spring.Reflection.Dynamic { set { myProperty = value; } } + + public override string MyVirtualBaseProperty + { + get { return "MyOverridenDerivedPropertyText"; } + } } #if NET_2_0 diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs index b4933522..e48f5392 100644 --- a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicMethodTests.cs @@ -108,6 +108,38 @@ namespace Spring.Reflection.Dynamic Assert.IsFalse((bool) isNullOrEmpty.Invoke(null, new object[] { "Ana Maria" })); } + [Test] + public void TestArgumentTypeCasts() + { + IDynamicMethod sqrt = DynamicMethod.Create(typeof(Math).GetMethod("Sqrt")); + object result = sqrt.Invoke(null, new object[] { 4 } ); + Assert.AreEqual( Math.Sqrt(4), result ); + + try + { + sqrt.Invoke(null, new object[] { null } ); + Assert.Fail(); + } + catch (InvalidCastException) + { + } + + try + { + sqrt.Invoke(null, new object[] { "4" } ); + Assert.Fail(); + } + catch (InvalidCastException) + {} + } + + private void CodeForReflection() + { + object val = 4; + Type argType = typeof(double); + Math.Sqrt( (double)Convert.ChangeType(val, argType) ); + } + [Test] public void TestRefOutMethods() { diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicPropertyTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicPropertyTests.cs index 69978cc0..a789751e 100644 --- a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicPropertyTests.cs +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/DynamicPropertyTests.cs @@ -21,8 +21,11 @@ #region Imports using System; +using System.CodeDom.Compiler; using System.Diagnostics; +using System.IO; using System.Reflection; +using System.Text; using NUnit.Framework; using Spring.Context.Support; @@ -30,52 +33,52 @@ using Spring.Context.Support; namespace Spring.Reflection.Dynamic { - /// + /// /// Unit tests for the DynamicProperty class. /// /// Aleksandar Seovic - [TestFixture] + [TestFixture] public class DynamicPropertyTests : BasePropertyTests { - protected override IDynamicProperty Create(PropertyInfo property) + protected override IDynamicProperty Create( PropertyInfo property ) { - return DynamicProperty.Create(property); + return DynamicProperty.Create( property ); } [Test] - [ExpectedException(typeof(InvalidOperationException))] + [ExpectedException( typeof( InvalidOperationException ) )] public void TestNonReadableProperties() { - IDynamicProperty nonReadableProperty = - Create(typeof(ClassWithNonReadableProperty).GetProperty("MyProperty")); - nonReadableProperty.GetValue(null); + IDynamicProperty nonReadableProperty = + Create( typeof( ClassWithNonReadableProperty ).GetProperty( "MyProperty" ) ); + nonReadableProperty.GetValue( null ); } [Test] - [ExpectedException(typeof(InvalidOperationException))] + [ExpectedException( typeof( InvalidOperationException ) )] public void TestNonWritableInstanceProperty() { IDynamicProperty nonWritableProperty = - Create(typeof(Inventor).GetProperty("PlaceOfBirth")); - nonWritableProperty.SetValue(null, null); + Create( typeof( Inventor ).GetProperty( "PlaceOfBirth" ) ); + nonWritableProperty.SetValue( null, null ); } [Test] - [ExpectedException(typeof(InvalidOperationException))] + [ExpectedException( typeof( InvalidOperationException ) )] public void TestAttemptingToSetPropertyOfValueTypeInstance() { MyStruct myYearHolder = new MyStruct(); - IDynamicProperty year = Create(typeof(MyStruct).GetProperty("Year")); - year.SetValue(myYearHolder, 2004); + IDynamicProperty year = Create( typeof( MyStruct ).GetProperty( "Year" ) ); + year.SetValue( myYearHolder, 2004 ); } - + [Test] - [ExpectedException(typeof(InvalidOperationException))] + [ExpectedException( typeof( InvalidOperationException ) )] public void TestNonWritableStaticProperty() { IDynamicProperty nonWritableProperty = - Create(typeof(DateTime).GetProperty("Today")); - nonWritableProperty.SetValue(null, null); - } + Create( typeof( DateTime ).GetProperty( "Today" ) ); + nonWritableProperty.SetValue( null, null ); + } } } diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafePropertyTests.cs b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafePropertyTests.cs index 0b3407bc..b37f1624 100644 --- a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafePropertyTests.cs +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafePropertyTests.cs @@ -21,7 +21,11 @@ #region Imports using System; +using System.CodeDom.Compiler; +using System.IO; using System.Reflection; +using System.Text; +using Microsoft.VisualBasic; using NUnit.Framework; #endregion @@ -41,6 +45,53 @@ namespace Spring.Reflection.Dynamic return new SafeProperty(property); } + + [Test] + public void CanGetSetSimpleProperty() + { + object o = GetVisualBasicTestObject(); + IDynamicProperty simpleProperty = Create(o.GetType().GetProperty("SimpleProperty")); + simpleProperty.SetValue(o, "CanGetSimpleText", "args"); + Assert.AreEqual("CanGetSimpleText", ThisLastPropertyValue.GetValue(o)); + Assert.AreEqual("CanGetSimpleText", simpleProperty.GetValue(o)); + } + + [Test] + public void CanGetSetSimpleIndexer() + { + object o = GetVisualBasicTestObject(); + IDynamicProperty simpleProperty = Create(o.GetType().GetProperty("SimpleIndexer")); + + // write + simpleProperty.SetValue(o, "CanGetSetSimpleIndexer", 2); + Assert.AreEqual("CanGetSetSimpleIndexer", ThisLastPropertyValue.GetValue(o)); + Assert.AreEqual(2, ThisArg1.GetValue(o)); + + // read + object value = simpleProperty.GetValue(o, 3); + Assert.AreEqual("CanGetSetSimpleIndexer", value); + Assert.AreEqual(3, ThisArg1.GetValue(o)); + } + + [Test] + public void CanGetSetComplexIndexer() + { + object o = GetVisualBasicTestObject(); + IDynamicProperty property = Create(o.GetType().GetProperty("ComplexIndexer")); + + // write + property.SetValue(o, "CanGetSetComplexIndexer", 2, "Arg2"); + Assert.AreEqual("CanGetSetComplexIndexer", ThisLastPropertyValue.GetValue(o)); + Assert.AreEqual(2.0, (double)ThisArg1.GetValue(o)); + Assert.AreEqual("Arg2", ThisArg2.GetValue(o)); + + // read + object value = property.GetValue(o, 3, "Arg3"); + Assert.AreEqual("CanGetSetComplexIndexer", value); + Assert.AreEqual(3.0, (double)ThisArg1.GetValue(o)); + Assert.AreEqual("Arg3", ThisArg2.GetValue(o)); + } + #if NET_2_0 [Test] public void TestForRestrictiveSetterWithSafeWrapper() @@ -76,6 +127,60 @@ namespace Spring.Reflection.Dynamic Assert.AreEqual(123, first.GetValue(something)); } #endif + + #region VB TestClass Code + + private static Type s__visualBasicTestObjectType; + private static IDynamicField ThisLastPropertyValue; + private static IDynamicField ThisArg1; + private static IDynamicField ThisArg2; + private static IDynamicField ThisOptionalArg; + private static IDynamicField ThisParamsArg; + + protected static object GetVisualBasicTestObject() + { + if (s__visualBasicTestObjectType == null) + { + // compile vb test class + string vbSourceCode = new StreamReader( Assembly.GetExecutingAssembly().GetManifestResourceStream( typeof( BasePropertyTests ), "SafePropertyTests_TestObject.vb" ) ).ReadToEnd(); + + CompilerParameters args = new CompilerParameters(); + args.OutputAssembly = "VbTestObject.dll"; + args.GenerateInMemory = true; + args.GenerateExecutable = false; + args.IncludeDebugInformation = true; + args.Evidence = Assembly.GetExecutingAssembly().Evidence; +#if NET_2_0 + CodeDomProvider provider = CodeDomProvider.CreateProvider( "VisualBasic" ); + CompilerResults results = provider.CompileAssemblyFromSource( args, vbSourceCode ); +#else + CodeDomProvider provider = new VBCodeProvider(); + ICodeCompiler compiler = provider.CreateCompiler(); + CompilerResults results = compiler.CompileAssemblyFromSource( args, vbSourceCode ); +#endif + if (results.Errors.HasErrors) + { + StringBuilder sb = new StringBuilder(); + foreach (CompilerError error in results.Errors) + { + sb.Append( error.ToString() ).Append( "\n\r" ); + } + throw new TypeLoadException( "failed compiling test class: " + sb ); + } + s__visualBasicTestObjectType = results.CompiledAssembly.GetType( "VbTestObject" ); + ThisLastPropertyValue = DynamicField.Create( s__visualBasicTestObjectType.GetField("ThisLastPropertyValue") ); + ThisArg1 = DynamicField.Create( s__visualBasicTestObjectType.GetField("ThisArg1") ); + ThisArg2 = DynamicField.Create( s__visualBasicTestObjectType.GetField("ThisArg2") ); + ThisOptionalArg = DynamicField.Create( s__visualBasicTestObjectType.GetField("ThisOptionalArg") ); + ThisParamsArg = DynamicField.Create( s__visualBasicTestObjectType.GetField("ThisParamsArgs") ); + } + + object s__visualBasicTestObject = Activator.CreateInstance(s__visualBasicTestObjectType); + + return s__visualBasicTestObject; + } + + #endregion } #region Test Classes diff --git a/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafePropertyTests_TestObject.vb b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafePropertyTests_TestObject.vb new file mode 100644 index 00000000..b0708492 --- /dev/null +++ b/test/Spring/Spring.Core.Tests/Reflection/Dynamic/SafePropertyTests_TestObject.vb @@ -0,0 +1,68 @@ +Public Class VbTestObject + Public ThisArg1 As Object + Public ThisArg2 As Object + Public ThisOptionalArg As Object + Public ThisParamsArgs As Object() + Public ThisLastPropertyValue As Object + + Property SimpleProperty() As String + Get + Return ThisLastPropertyValue + End Get + Set(ByVal value As String) + ThisLastPropertyValue = value + End Set + End Property + + Property SimpleIndexer(ByVal arg1 As Integer) As String + Get + ThisArg1 = arg1 + Return ThisLastPropertyValue + End Get + Set(ByVal value As String) + ThisArg1 = arg1 + ThisLastPropertyValue = value + End Set + End Property + + Default Property ComplexIndexer(ByVal arg1 As Double, ByVal arg2 As Object) As String + Get + ThisArg1 = arg1 + ThisArg2 = arg2 + Return ThisLastPropertyValue + End Get + Set(ByVal value As String) + ThisArg1 = arg1 + ThisArg2 = arg2 + ThisLastPropertyValue = value + End Set + End Property + + Property PropertyWithParamsArgs(ByVal arg1 As String, ByVal ParamArray paramsArgs As Object()) As String + Get + ThisArg1 = arg1 + ThisParamsArgs = paramsArgs + Return ThisLastPropertyValue + End Get + Set(ByVal value As String) + ThisArg1 = arg1 + ThisParamsArgs = paramsArgs + ThisLastPropertyValue = value + End Set + End Property + + + Property PropertyWithOptionalArg(ByVal arg1 As String, Optional ByVal optionalArg As Object = "Empty") As String + Get + ThisArg1 = arg1 + ThisOptionalArg = optionalArg + Return ThisLastPropertyValue + End Get + Set(ByVal value As String) + ThisArg1 = arg1 + ThisOptionalArg = optionalArg + ThisLastPropertyValue = value + End Set + End Property + +End Class \ No newline at end of file diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj index 58be75c1..61ea52cc 100644 --- a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.2003.csproj @@ -1744,6 +1744,10 @@ SubType = "Code" BuildAction = "Compile" /> + Code + diff --git a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.build b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.build index a1085c2d..1345f8f0 100644 --- a/test/Spring/Spring.Core.Tests/Spring.Core.Tests.build +++ b/test/Spring/Spring.Core.Tests/Spring.Core.Tests.build @@ -30,6 +30,7 @@ + diff --git a/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2003.csproj b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2003.csproj index 89fe78d2..deacee02 100644 --- a/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2003.csproj +++ b/test/Spring/Spring.Data.Integration.Tests/Spring.Data.Integration.Tests.2003.csproj @@ -151,6 +151,11 @@ SubType = "Code" BuildAction = "Compile" /> + + + + + + + + + + + + + + Local - 9.0.21022 + 9.0.30729 2.0 {91766D21-C568-459F-9BEA-759B011F23CF} Debug @@ -127,6 +127,7 @@ + @@ -185,6 +186,12 @@ + + + + + + diff --git a/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2003.csproj b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2003.csproj index cd511db9..4abdc4fb 100644 --- a/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2003.csproj +++ b/test/Spring/Spring.Web.Tests/Spring.Web.Tests.2003.csproj @@ -244,6 +244,11 @@ SubType = "Code" BuildAction = "Compile" /> + + - - + + + + /// Unit tests for the ControlInterceptor class. @@ -40,7 +40,7 @@ namespace Spring.Util public class ControlInterceptionTests { private const string RES_OBJECTS = - "assembly://Spring.Web.Tests/Spring.Util/ControlInterceptionTests.objects.xml"; + "assembly://Spring.Web.Tests/Spring.Web.Support/ControlInterceptionTests.objects.xml"; static ControlInterceptionTests() { diff --git a/test/Spring/Spring.Web.Tests/Util/ControlInterceptionTests.objects.xml b/test/Spring/Spring.Web.Tests/Web/Support/ControlInterceptionTests.objects.xml similarity index 68% rename from test/Spring/Spring.Web.Tests/Util/ControlInterceptionTests.objects.xml rename to test/Spring/Spring.Web.Tests/Web/Support/ControlInterceptionTests.objects.xml index 320c4faf..ae4dccfa 100644 --- a/test/Spring/Spring.Web.Tests/Util/ControlInterceptionTests.objects.xml +++ b/test/Spring/Spring.Web.Tests/Web/Support/ControlInterceptionTests.objects.xml @@ -2,7 +2,7 @@ - +