Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / xsp / System / Web / Util / FastPropertyAccessor.cs / 3 / FastPropertyAccessor.cs
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//-----------------------------------------------------------------------------
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
using System.Collections;
using System.Security;
using System.Security.Permissions;
namespace System.Web.Util {
/*
* Property Accessor Generator class
*
* The purpose of this class is to generate some IL code on the fly that can efficiently
* access properties (and fields) of objects. This is an alternative to using
* very slow reflection.
*/
internal class FastPropertyAccessor {
private static object s_lockObject = new object();
private static FastPropertyAccessor s_accessorGenerator;
private static Hashtable s_accessorCache;
private static MethodInfo _getPropertyMethod;
private static MethodInfo _setPropertyMethod;
private static Type[] _getPropertyParameterList = new Type[] { typeof(object) };
private static Type[] _setPropertyParameterList = new Type[] { typeof(object), typeof(object) };
private static Type[] _interfacesToImplement;
private static int _uniqueId; // Used to generate unique type ID's.
// Property getter/setter must be public for codegen to access it.
// Static properties are ignored, since this class only works on instances of objects.
// Need to use DeclaredOnly to avoid AmbiguousMatchException if a property with
// a different return type is hidden.
private const BindingFlags _declaredFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly;
private ModuleBuilder _dynamicModule = null;
static FastPropertyAccessor() {
// Get the SetProperty method, and make sure it has
// the correct signature.
_getPropertyMethod = typeof(IWebPropertyAccessor).GetMethod("GetProperty");
_setPropertyMethod = typeof(IWebPropertyAccessor).GetMethod("SetProperty");
// This will be needed later, when building the dynamic class.
_interfacesToImplement = new Type[1];
_interfacesToImplement[0] = typeof(IWebPropertyAccessor);
}
private static String GetUniqueCompilationName() {
return Guid.NewGuid().ToString().Replace('-', '_');
}
[ReflectionPermission(SecurityAction.Assert, Flags=ReflectionPermissionFlag.ReflectionEmit)]
Type GetPropertyAccessorTypeWithAssert(Type type, string propertyName,
PropertyInfo propInfo, FieldInfo fieldInfo) {
// Create the dynamic assembly if needed.
Type accessorType;
MethodInfo getterMethodInfo = null;
MethodInfo setterMethodInfo = null;
Type propertyType;
if (propInfo != null) {
// It'a a property
getterMethodInfo = propInfo.GetGetMethod();
setterMethodInfo = propInfo.GetSetMethod();
propertyType = propInfo.PropertyType;
}
else {
// If not, it must be a field
propertyType = fieldInfo.FieldType;
}
if (_dynamicModule == null) {
lock (this) {
if (_dynamicModule == null) {
// Use a unique name for each assembly.
String name = GetUniqueCompilationName();
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = "A_" + name;
// Create a new assembly.
AssemblyBuilder newAssembly =
Thread.GetDomain().DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.Run,
null, //directory to persist assembly
null, //evidence copied from caller
null, //requiredPermissions
null, //optionalPermissions
null, //refusedPermissions
true //isSynchronized
);
// Create a single module in the assembly.
_dynamicModule = newAssembly.DefineDynamicModule("M_" + name);
}
}
}
// Give the factory a unique name.
String typeName = System.Web.UI.Util.MakeValidTypeNameFromString(type.Name) +
"_" + propertyName + "_" + (_uniqueId++);
TypeBuilder accessorTypeBuilder = _dynamicModule.DefineType("T_" + typeName,
TypeAttributes.Public,
typeof(object),
_interfacesToImplement);
//
// Define the GetProperty method. It must be virtual to be an interface implementation.
//
MethodBuilder method = accessorTypeBuilder.DefineMethod("GetProperty",
MethodAttributes.Public |
MethodAttributes.Virtual,
typeof(Object),
_getPropertyParameterList);
// Generate IL. The generated IL corresponds to:
// "return ((TargetType) target).Blah;"
ILGenerator il = method.GetILGenerator();
if (getterMethodInfo != null) {
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, type);
// Generate the getter call based on whether it's a Property or Field
if (propInfo != null)
il.EmitCall(OpCodes.Callvirt, getterMethodInfo, null);
else
il.Emit(OpCodes.Ldfld, fieldInfo);
il.Emit(OpCodes.Box, propertyType);
il.Emit(OpCodes.Ret);
// Specify that this method implements GetProperty from the inherited interface.
accessorTypeBuilder.DefineMethodOverride(method, _getPropertyMethod);
}
else {
// Generate IL. The generated IL corresponds to "throw new InvalidOperationException"
ConstructorInfo cons = typeof(InvalidOperationException).GetConstructor(Type.EmptyTypes);
il.Emit(OpCodes.Newobj, cons);
il.Emit(OpCodes.Throw);
}
//
// Define the SetProperty method. It must be virtual to be an interface implementation.
//
method = accessorTypeBuilder.DefineMethod("SetProperty",
MethodAttributes.Public |
MethodAttributes.Virtual,
null,
_setPropertyParameterList);
il = method.GetILGenerator();
// Don't generate any code in the setter if it's a readonly property.
// We still need to have an implementation of SetProperty, but it does nothing.
if (fieldInfo != null || setterMethodInfo != null) {
// Generate IL. The generated IL corresponds to:
// "((TargetType) target).Blah = (PropType) val;"
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Castclass, type);
il.Emit(OpCodes.Ldarg_2);
if (propertyType.IsPrimitive) {
// Primitive type: deal with boxing
il.Emit(OpCodes.Unbox, propertyType);
// Emit the proper instruction for the type
if (propertyType == typeof(sbyte)) {
il.Emit(OpCodes.Ldind_I1);
}
else if (propertyType == typeof(byte)) {
il.Emit(OpCodes.Ldind_U1);
}
else if (propertyType == typeof(short)) {
il.Emit(OpCodes.Ldind_I2);
}
else if (propertyType == typeof(ushort)) {
il.Emit(OpCodes.Ldind_U2);
}
else if (propertyType == typeof(uint)) {
il.Emit(OpCodes.Ldind_U4);
}
else if (propertyType == typeof(int)) {
il.Emit(OpCodes.Ldind_I4);
}
else if (propertyType == typeof(long)) {
il.Emit(OpCodes.Ldind_I8);
}
else if (propertyType == typeof(ulong)) {
il.Emit(OpCodes.Ldind_I8); // Somehow, there is no Ldind_u8
}
else if (propertyType == typeof(bool)) {
il.Emit(OpCodes.Ldind_I1);
}
else if (propertyType == typeof(char)) {
il.Emit(OpCodes.Ldind_U2);
}
else if (propertyType == typeof(decimal)) {
il.Emit(OpCodes.Ldobj, propertyType);
}
else if (propertyType == typeof(float)) {
il.Emit(OpCodes.Ldind_R4);
}
else if (propertyType == typeof(double)) {
il.Emit(OpCodes.Ldind_R8);
}
else {
il.Emit(OpCodes.Ldobj, propertyType);
}
}
else if (propertyType.IsValueType) {
// Value type: deal with boxing
il.Emit(OpCodes.Unbox, propertyType);
il.Emit(OpCodes.Ldobj, propertyType);
}
else {
// No boxing involved: just generate a standard cast
il.Emit(OpCodes.Castclass, propertyType);
}
// Generate the assignment based on whether it's a Property or Field
if (propInfo != null)
il.EmitCall(OpCodes.Callvirt, setterMethodInfo, null);
else
il.Emit(OpCodes.Stfld, fieldInfo);
}
il.Emit(OpCodes.Ret);
// Specify that this method implements SetProperty from the inherited interface.
accessorTypeBuilder.DefineMethodOverride(method, _setPropertyMethod);
// Bake in the type.
accessorType = accessorTypeBuilder.CreateType();
return accessorType;
}
private static IWebPropertyAccessor GetPropertyAccessor(Type type, string propertyName) {
if (s_accessorGenerator == null || s_accessorCache == null) {
lock (s_lockObject) {
if (s_accessorGenerator == null || s_accessorCache == null) {
s_accessorGenerator = new FastPropertyAccessor();
s_accessorCache = new Hashtable();
}
}
}
// First, check if we have it cached
// Get a hash key based on the Type and the property name
int cacheKey = HashCodeCombiner.CombineHashCodes(
type.GetHashCode(), propertyName.GetHashCode());
IWebPropertyAccessor accessor = (IWebPropertyAccessor) s_accessorCache[cacheKey];
// It was cached, so just return it
if (accessor != null)
return accessor;
FieldInfo fieldInfo = null;
Type declaringType;
// First, try to find a property with that name. Type.GetProperty() without BindingFlags.Declared
// will throw AmbiguousMatchException if there is a hidden property with the same name and a
// different type (VSWhidbey 237437). This method finds the property with the specified name
// on the most specific type.
PropertyInfo propInfo = GetPropertyMostSpecific(type, propertyName);
if (propInfo != null) {
// Get the most base Type where the property is first declared
MethodInfo baseCheckMethodInfo = propInfo.GetGetMethod();
if (baseCheckMethodInfo == null) {
baseCheckMethodInfo = propInfo.GetSetMethod();
}
declaringType = baseCheckMethodInfo.GetBaseDefinition().DeclaringType;
// DevDiv Bug 27734
// Ignore the declaring type if it's generic
if (declaringType.IsGenericType)
declaringType = type;
// If they're different, get a new PropertyInfo
if (declaringType != type) {
// We want the propertyInfo for the property specifically declared on the declaringType.
// So pass in the correct BindingFlags to avoid an AmbiguousMatchException, which would
// be thrown if the declaringType hides a property with the same name and a different type.
// VSWhidbey 518034
propInfo = declaringType.GetProperty(propertyName, _declaredFlags);
}
}
else {
// We couldn't find a property, so try a field
// Type.GetField can not throw AmbiguousMatchException like Type.GetProperty above.
fieldInfo = type.GetField(propertyName);
// If we couldn't find a field either, give up
if (fieldInfo == null)
throw new ArgumentException();
declaringType = fieldInfo.DeclaringType;
}
// If the Type where the property/field is declared is not the same as the current
// Type, check if the declaring Type already has a cached accessor. This limits
// the number of different accessors we need to create. e.g. Every control has
// an ID property, but we'll end up only create one accessor for all of them.
int declaringTypeCacheKey = 0;
if (declaringType != type) {
// Get a hash key based on the declaring Type and the property name
declaringTypeCacheKey = HashCodeCombiner.CombineHashCodes(
declaringType.GetHashCode(), propertyName.GetHashCode());
accessor = (IWebPropertyAccessor) s_accessorCache[declaringTypeCacheKey];
// We have a cached accessor for the declaring type, so use it
if (accessor != null) {
// Cache the declaring type's accessor as ourselves
lock (s_accessorCache.SyncRoot) {
s_accessorCache[cacheKey] = accessor;
}
return accessor;
}
}
if (accessor == null) {
Type propertyAccessorType;
lock (s_accessorGenerator) {
propertyAccessorType = s_accessorGenerator.GetPropertyAccessorTypeWithAssert(
declaringType, propertyName, propInfo, fieldInfo);
}
// Create the type. This is the only place where Activator.CreateInstance is used,
// reducing the calls to it from 1 per instance to 1 per type.
accessor = (IWebPropertyAccessor) HttpRuntime.CreateNonPublicInstance(propertyAccessorType);
}
// Cache the accessor
lock (s_accessorCache.SyncRoot) {
s_accessorCache[cacheKey] = accessor;
if (declaringTypeCacheKey != 0)
s_accessorCache[declaringTypeCacheKey] = accessor;
}
return accessor;
}
internal static object GetProperty(object target, string propName) {
IWebPropertyAccessor accessor = GetPropertyAccessor(target.GetType(), propName);
return accessor.GetProperty(target);
}
// Finds the property with the specified name on the most specific type.
private static PropertyInfo GetPropertyMostSpecific(Type type, string name) {
PropertyInfo propInfo;
Type currentType = type;
while (currentType != null) {
propInfo = currentType.GetProperty(name, _declaredFlags);
if (propInfo != null) {
return propInfo;
}
else {
currentType = currentType.BaseType;
}
}
return null;
}
internal static void SetProperty(object target, string propName, object val) {
IWebPropertyAccessor accessor = GetPropertyAccessor(target.GetType(), propName);
accessor.SetProperty(target, val);
}
}
[AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
public interface IWebPropertyAccessor {
object GetProperty(object target);
void SetProperty(object target, object value);
}
}
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- StagingAreaInputItem.cs
- MarshalByValueComponent.cs
- ExtensionQuery.cs
- XamlWriter.cs
- XmlSerializerVersionAttribute.cs
- MsmqIntegrationBindingElement.cs
- AssemblyBuilderData.cs
- HostingEnvironmentWrapper.cs
- ComponentDispatcher.cs
- XsdDateTime.cs
- SchemaImporterExtensionElement.cs
- Highlights.cs
- ExceptionCollection.cs
- InvariantComparer.cs
- ISCIIEncoding.cs
- PropertyIDSet.cs
- SoapFault.cs
- ConfigurationManagerInternalFactory.cs
- PropertyGridView.cs
- DocumentPageTextView.cs
- PropertyManager.cs
- WindowsPrincipal.cs
- VirtualDirectoryMappingCollection.cs
- SessionState.cs
- AttributeCollection.cs
- RuntimeIdentifierPropertyAttribute.cs
- RepeaterItemCollection.cs
- StaticFileHandler.cs
- WebBrowserUriTypeConverter.cs
- WindowsListViewItem.cs
- SqlConnectionString.cs
- DataDocumentXPathNavigator.cs
- AuthenticatedStream.cs
- MemberJoinTreeNode.cs
- StylusButtonCollection.cs
- AdornerPresentationContext.cs
- AttachmentCollection.cs
- ProxyDataContractResolver.cs
- SplitterEvent.cs
- NextPreviousPagerField.cs
- WindowsStatusBar.cs
- XhtmlBasicCalendarAdapter.cs
- VBIdentifierTrimConverter.cs
- SigningCredentials.cs
- MarshalByValueComponent.cs
- MetaType.cs
- NotImplementedException.cs
- MultilineStringConverter.cs
- XmlSchemaComplexContentRestriction.cs
- StringFormat.cs
- WindowsComboBox.cs
- Parallel.cs
- TiffBitmapEncoder.cs
- VirtualPath.cs
- EnumerableCollectionView.cs
- Events.cs
- ToRequest.cs
- CreateUserErrorEventArgs.cs
- RequestBringIntoViewEventArgs.cs
- EdmEntityTypeAttribute.cs
- DetailsViewDeletedEventArgs.cs
- Hyperlink.cs
- MethodRental.cs
- HttpServerVarsCollection.cs
- XmlChildNodes.cs
- DataGridViewRowHeaderCell.cs
- MemoryMappedViewStream.cs
- CodeDomSerializerException.cs
- CapabilitiesSection.cs
- Coordinator.cs
- MenuItemBindingCollection.cs
- ServiceNameElement.cs
- CodeObject.cs
- TemplateBaseAction.cs
- InvalidWorkflowException.cs
- XmlDictionaryReaderQuotasElement.cs
- PathSegmentCollection.cs
- ResourceDisplayNameAttribute.cs
- RightsManagementEncryptionTransform.cs
- ServiceNameElementCollection.cs
- SqlBuffer.cs
- login.cs
- CoTaskMemUnicodeSafeHandle.cs
- TimerTable.cs
- SvcMapFile.cs
- CompoundFileStreamReference.cs
- CommandTreeTypeHelper.cs
- ClipboardData.cs
- TreePrinter.cs
- ImmComposition.cs
- CssStyleCollection.cs
- StorageMappingItemLoader.cs
- BaseAutoFormat.cs
- RegexFCD.cs
- LeftCellWrapper.cs
- OleDbSchemaGuid.cs
- FieldInfo.cs
- DefaultProxySection.cs
- GridViewHeaderRowPresenterAutomationPeer.cs
- Window.cs