#region License
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* 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.Util;
#endregion
namespace Spring.Reflection.Dynamic{
#region IDynamicField interface
/// <summary>
/// Defines methods that dynamic field class has to implement.
/// </summary>
public interface IDynamicField
{
/// <summary>
/// Gets the value of the dynamic field for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get field value from.
/// </param>
/// <returns>
/// A field value.
/// </returns>
object GetValue(object target);
/// <summary>
/// Gets the value of the dynamic field for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set field value on.
/// </param>
/// <param name="value">
/// A new field value.
/// </param>
void SetValue(object target, object value);
}
#endregion
#region Safe wrapper
/// <summary>
/// Safe wrapper for the dynamic field.
/// </summary>
/// <remarks>
/// <see cref="SafeField"/> will attempt to use dynamic
/// field if possible, but it will fall back to standard
/// reflection if necessary.
/// </remarks>
public class SafeField : IDynamicField
{
private readonly FieldInfo fieldInfo;
#if NET_2_0
#region Cache
private static readonly IDictionary fieldCache = new Hashtable();
/// <summary>
/// Holds cached Getter/Setter delegates for a Field
/// </summary>
private class DynamicFieldCacheEntry
{
public readonly FieldGetterDelegate Getter;
public readonly FieldSetterDelegate Setter;
public DynamicFieldCacheEntry(FieldGetterDelegate getter, FieldSetterDelegate setter)
{
Getter = getter;
Setter = setter;
}
}
/// <summary>
/// Obtains cached fieldInfo or creates a new entry, if none is found.
/// </summary>
private static DynamicFieldCacheEntry GetOrCreateDynamicField(FieldInfo field)
{
DynamicFieldCacheEntry fieldInfo = (DynamicFieldCacheEntry)fieldCache[field];
if (fieldInfo == null)
{
fieldInfo = new DynamicFieldCacheEntry(DynamicReflectionManager.CreateFieldGetter(field), DynamicReflectionManager.CreateFieldSetter(field));
lock (fieldCache)
{
fieldCache[field] = fieldInfo;
}
}
return fieldInfo;
}
#endregion
private readonly FieldGetterDelegate getter;
private readonly FieldSetterDelegate setter;
/// <summary>
/// Creates a new instance of the safe field wrapper.
/// </summary>
/// <param name="field">Field to wrap.</param>
public SafeField(FieldInfo field)
{
AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value.");
fieldInfo = field;
DynamicFieldCacheEntry fi = GetOrCreateDynamicField(field);
getter = fi.Getter;
setter = fi.Setter;
}
/// <summary>
/// Gets the value of the dynamic field for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get field value from.
/// </param>
/// <returns>
/// A field value.
/// </returns>
public object GetValue(object target)
{
return getter(target);
}
/// <summary>
/// Gets the value of the dynamic field for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set field value on.
/// </param>
/// <param name="value">
/// A new field value.
/// </param>
public void SetValue(object target, object value)
{
setter(target, value);
}
#else
/// <summary>
/// Returns a <see cref="IDynamicField"/> implementation
/// by determining the fastest possible dynamic access strategy
/// </summary>
/// <param name="field">the field to be wrapped</param>
/// <returns>an <see cref="IDynamicField"/> instance for accessing the
/// field represented by the given <see cref="FieldInfo"/></returns>
public static IDynamicField CreateFrom(FieldInfo field)
{
AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value.");
IDynamicField dynamicField;
if (field.IsPublic &&
ReflectionUtils.IsTypeVisible(field.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME))
{
dynamicField = DynamicField.Create(field);
}
else
{
dynamicField = new SafeField(field);
}
return dynamicField;
}
private readonly IDynamicField dynamicField;
private readonly bool isOptimizedGet = false;
private bool isOptimizedSet = false;
private readonly bool canSet;
/// <summary>
/// Creates a new instance of the safe field wrapper.
/// </summary>
/// <param name="field">Field to wrap.</param>
public SafeField(FieldInfo field)
{
AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value.");
this.fieldInfo = field;
this.canSet = (!fieldInfo.IsLiteral
&& !fieldInfo.IsInitOnly
&& !(fieldInfo.DeclaringType.IsValueType && !fieldInfo.IsStatic));
if (fieldInfo.IsPublic &&
ReflectionUtils.IsTypeVisible(fieldInfo.DeclaringType, DynamicReflectionManager.ASSEMBLY_NAME))
{
dynamicField = DynamicField.Create(fieldInfo);
isOptimizedGet = isOptimizedSet = true;
}
}
/// <summary>
/// Gets the value of the dynamic field for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to get field value from.
/// </param>
/// <returns>
/// A field value.
/// </returns>
public object GetValue(object target)
{
if (isOptimizedGet)
{
return dynamicField.GetValue(target);
}
else
{
return fieldInfo.GetValue(target);
}
}
/// <summary>
/// Gets the value of the dynamic field for the specified target object.
/// </summary>
/// <param name="target">
/// Target object to set field value on.
/// </param>
/// <param name="value">
/// A new field value.
/// </param>
public void SetValue(object target, object value)
{
if (isOptimizedSet)
{
try
{
dynamicField.SetValue(target, value);
return;
}
catch (InvalidCastException)
{
isOptimizedSet = false;
}
}
if (!canSet)
{
throw new InvalidOperationException("Cannot set value of a read-only field or a constant.");
}
try
{
fieldInfo.SetValue(target, value);
}
catch(ArgumentException)
{
if (value != null && !fieldInfo.GetType().IsAssignableFrom(value.GetType()))
{
throw new InvalidCastException();
}
throw;
}
}
#endif
internal FieldInfo FieldInfo
{
get { return fieldInfo; }
}
}
#endregion
#if NET_2_0
/// <summary>
/// Factory class for dynamic fields.
/// </summary>
/// <author>Aleksandar Seovic</author>
public class DynamicField : BaseDynamicMember
{
/// <summary>
/// Creates dynamic field instance for the specified <see cref="FieldInfo"/>.
/// </summary>
/// <param name="field">Field info to create dynamic field for.</param>
/// <returns>Dynamic field for the specified <see cref="FieldInfo"/>.</returns>
public static IDynamicField Create(FieldInfo field)
{
AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value.");
IDynamicField dynamicField = new SafeField(field);
return dynamicField;
}
}
#else
/// <summary>
/// Factory class for dynamic fields.
/// </summary>
/// <author>Aleksandar Seovic</author>
public class DynamicField : BaseDynamicMember
{
private static readonly CreateFieldCallback s_createCallback = new CreateFieldCallback(CreateInternal);
#region Create Method
/// <summary>
/// Creates dynamic field instance for the specified <see cref="FieldInfo"/>.
/// </summary>
/// <param name="field">Field info to create dynamic field for.</param>
/// <returns>Dynamic field for the specified <see cref="FieldInfo"/>.</returns>
public static IDynamicField Create(FieldInfo field)
{
AssertUtils.ArgumentNotNull(field, "You cannot create a dynamic field for a null value.");
IDynamicField dynamicField = DynamicReflectionManager.GetDynamicField(field, s_createCallback);
return dynamicField;
}
private static IDynamicField CreateInternal(FieldInfo field)
{
IDynamicField dynamicField = null;
TypeBuilder tb = DynamicReflectionManager.CreateTypeBuilder("Field_" + field.Name);
tb.AddInterfaceImplementation(typeof(IDynamicField));
GenerateGetValue(tb, field);
GenerateSetValue(tb, field);
Type dynamicFieldType = tb.CreateType();
ConstructorInfo ctor = dynamicFieldType.GetConstructor(Type.EmptyTypes);
dynamicField = (IDynamicField)ctor.Invoke(ObjectUtils.EmptyObjects);
return dynamicField;
}
private static void GenerateGetValue(TypeBuilder tb, FieldInfo field)
{
MethodBuilder getValueMethod =
tb.DefineMethod("GetValue", METHOD_ATTRIBUTES, typeof(object), new Type[] { typeof(object) });
getValueMethod.DefineParameter(1, ParameterAttributes.None, "target");
ILGenerator il = getValueMethod.GetILGenerator();
if (field.IsLiteral)
{
EmitConstant(il, field.GetValue(field.FieldType));
}
else if (field.IsStatic)
{
il.Emit(OpCodes.Ldsfld, field);
}
else
{
SetupTargetInstance(il, field.DeclaringType);
il.Emit(OpCodes.Ldfld, field);
}
ProcessReturnValue(il, field.FieldType);
il.Emit(OpCodes.Ret);
}
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 void GenerateSetValue(TypeBuilder tb, FieldInfo field)
{
MethodBuilder setValueMethod =
tb.DefineMethod("SetValue", METHOD_ATTRIBUTES, typeof(void),
new Type[] { typeof(object), typeof(object) });
setValueMethod.DefineParameter(1, ParameterAttributes.None, "target");
setValueMethod.DefineParameter(2, ParameterAttributes.None, "value");
ILGenerator il = setValueMethod.GetILGenerator();
if (!(field.IsInitOnly || field.IsLiteral))
{
bool isValueType = field.DeclaringType.IsValueType;
if (isValueType && !field.IsStatic)
{
ThrowInvalidOperationException(il, "Cannot set field value on a value type due to boxing.");
}
else
{
bool isStatic = field.IsStatic;
if (!isStatic)
{
SetupTargetInstance(il, field.DeclaringType);
}
SetupArgument(il, field.FieldType, 2);
if (!isStatic)
{
il.Emit(OpCodes.Stfld, field);
}
else
{
il.Emit(OpCodes.Stsfld, field);
}
il.Emit(OpCodes.Ret);
}
}
else
{
ThrowInvalidOperationException(il, "Cannot set value of a read-only field or a constant.");
}
}
#endregion
}
#endif // NET_2_0
}
|