diff options
Diffstat (limited to 'src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs')
-rw-r--r-- | src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs new file mode 100644 index 0000000000..3ea781252f --- /dev/null +++ b/src/mscorlib/shared/System/Diagnostics/Tracing/TraceLogging/PropertyValue.cs @@ -0,0 +1,252 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using System.Diagnostics; + +#if !ES_BUILD_AGAINST_DOTNET_V35 +using Contract = System.Diagnostics.Contracts.Contract; +#else +using Contract = Microsoft.Diagnostics.Contracts.Internal.Contract; +#endif + +namespace System.Diagnostics.Tracing +{ + /// <summary> + /// Holds property values of any type. For common value types, we have inline storage so that we don't need + /// to box the values. For all other types, we store the value in a single object reference field. + /// + /// To get the value of a property quickly, use a delegate produced by <see cref="PropertyValue.GetPropertyGetter(PropertyInfo)"/>. + /// </summary> + internal unsafe struct PropertyValue + { + /// <summary> + /// Union of well-known value types, to avoid boxing those types. + /// </summary> + [StructLayout(LayoutKind.Explicit)] + public struct Scalar + { + [FieldOffset(0)] + public Boolean AsBoolean; + [FieldOffset(0)] + public Byte AsByte; + [FieldOffset(0)] + public SByte AsSByte; + [FieldOffset(0)] + public Char AsChar; + [FieldOffset(0)] + public Int16 AsInt16; + [FieldOffset(0)] + public UInt16 AsUInt16; + [FieldOffset(0)] + public Int32 AsInt32; + [FieldOffset(0)] + public UInt32 AsUInt32; + [FieldOffset(0)] + public Int64 AsInt64; + [FieldOffset(0)] + public UInt64 AsUInt64; + [FieldOffset(0)] + public IntPtr AsIntPtr; + [FieldOffset(0)] + public UIntPtr AsUIntPtr; + [FieldOffset(0)] + public Single AsSingle; + [FieldOffset(0)] + public Double AsDouble; + [FieldOffset(0)] + public Guid AsGuid; + [FieldOffset(0)] + public DateTime AsDateTime; + [FieldOffset(0)] + public DateTimeOffset AsDateTimeOffset; + [FieldOffset(0)] + public TimeSpan AsTimeSpan; + [FieldOffset(0)] + public Decimal AsDecimal; + } + + // Anything not covered by the Scalar union gets stored in this reference. + readonly object _reference; + readonly Scalar _scalar; + readonly int _scalarLength; + + private PropertyValue(object value) + { + _reference = value; + _scalar = default(Scalar); + _scalarLength = 0; + } + + private PropertyValue(Scalar scalar, int scalarLength) + { + _reference = null; + _scalar = scalar; + _scalarLength = scalarLength; + } + + private PropertyValue(Boolean value) : this(new Scalar() { AsBoolean = value }, sizeof(Boolean)) { } + private PropertyValue(Byte value) : this(new Scalar() { AsByte = value }, sizeof(Byte)) { } + private PropertyValue(SByte value) : this(new Scalar() { AsSByte = value }, sizeof(SByte)) { } + private PropertyValue(Char value) : this(new Scalar() { AsChar = value }, sizeof(Char)) { } + private PropertyValue(Int16 value) : this(new Scalar() { AsInt16 = value }, sizeof(Int16)) { } + private PropertyValue(UInt16 value) : this(new Scalar() { AsUInt16 = value }, sizeof(UInt16)) { } + private PropertyValue(Int32 value) : this(new Scalar() { AsInt32 = value }, sizeof(Int32)) { } + private PropertyValue(UInt32 value) : this(new Scalar() { AsUInt32 = value }, sizeof(UInt32)) { } + private PropertyValue(Int64 value) : this(new Scalar() { AsInt64 = value }, sizeof(Int64)) { } + private PropertyValue(UInt64 value) : this(new Scalar() { AsUInt64 = value }, sizeof(UInt64)) { } + private PropertyValue(IntPtr value) : this(new Scalar() { AsIntPtr = value }, sizeof(IntPtr)) { } + private PropertyValue(UIntPtr value) : this(new Scalar() { AsUIntPtr = value }, sizeof(UIntPtr)) { } + private PropertyValue(Single value) : this(new Scalar() { AsSingle = value }, sizeof(Single)) { } + private PropertyValue(Double value) : this(new Scalar() { AsDouble = value }, sizeof(Double)) { } + private PropertyValue(Guid value) : this(new Scalar() { AsGuid = value }, sizeof(Guid)) { } + private PropertyValue(DateTime value) : this(new Scalar() { AsDateTime = value }, sizeof(DateTime)) { } + private PropertyValue(DateTimeOffset value) : this(new Scalar() { AsDateTimeOffset = value }, sizeof(DateTimeOffset)) { } + private PropertyValue(TimeSpan value) : this(new Scalar() { AsTimeSpan = value }, sizeof(TimeSpan)) { } + private PropertyValue(Decimal value) : this(new Scalar() { AsDecimal = value }, sizeof(Decimal)) { } + + public static Func<object, PropertyValue> GetFactory(Type type) + { + if (type == typeof(Boolean)) return value => new PropertyValue((Boolean)value); + if (type == typeof(Byte)) return value => new PropertyValue((Byte)value); + if (type == typeof(SByte)) return value => new PropertyValue((SByte)value); + if (type == typeof(Char)) return value => new PropertyValue((Char)value); + if (type == typeof(Int16)) return value => new PropertyValue((Int16)value); + if (type == typeof(UInt16)) return value => new PropertyValue((UInt16)value); + if (type == typeof(Int32)) return value => new PropertyValue((Int32)value); + if (type == typeof(UInt32)) return value => new PropertyValue((UInt32)value); + if (type == typeof(Int64)) return value => new PropertyValue((Int64)value); + if (type == typeof(UInt64)) return value => new PropertyValue((UInt64)value); + if (type == typeof(IntPtr)) return value => new PropertyValue((IntPtr)value); + if (type == typeof(UIntPtr)) return value => new PropertyValue((UIntPtr)value); + if (type == typeof(Single)) return value => new PropertyValue((Single)value); + if (type == typeof(Double)) return value => new PropertyValue((Double)value); + if (type == typeof(Guid)) return value => new PropertyValue((Guid)value); + if (type == typeof(DateTime)) return value => new PropertyValue((DateTime)value); + if (type == typeof(DateTimeOffset)) return value => new PropertyValue((DateTimeOffset)value); + if (type == typeof(TimeSpan)) return value => new PropertyValue((TimeSpan)value); + if (type == typeof(Decimal)) return value => new PropertyValue((Decimal)value); + + return value => new PropertyValue(value); + } + + + public object ReferenceValue + { + get + { + Debug.Assert(_scalarLength == 0, "This ReflectedValue refers to an unboxed value type, not a reference type or boxed value type."); + return _reference; + } + } + + public Scalar ScalarValue + { + get + { + Debug.Assert(_scalarLength > 0, "This ReflectedValue refers to a reference type or boxed value type, not an unboxed value type"); + return _scalar; + } + } + + public int ScalarLength + { + get + { + Debug.Assert(_scalarLength > 0, "This ReflectedValue refers to a reference type or boxed value type, not an unboxed value type"); + return _scalarLength; + } + } + + /// <summary> + /// Gets a delegate that gets the value of a given property. + /// </summary> + public static Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property) + { + if (property.DeclaringType.GetTypeInfo().IsValueType) + return GetBoxedValueTypePropertyGetter(property); + else + return GetReferenceTypePropertyGetter(property); + } + + /// <summary> + /// Gets a delegate that gets the value of a property of a value type. We unfortunately cannot avoid boxing the value type, + /// without making this generic over the value type. That would result in a large number of generic instantiations, and furthermore + /// does not work correctly on .Net Native (we cannot express the needed instantiations in an rd.xml file). We expect that user-defined + /// value types will be rare, and in any case the boxing only happens for events that are actually enabled. + /// </summary> + private static Func<PropertyValue, PropertyValue> GetBoxedValueTypePropertyGetter(PropertyInfo property) + { + var type = property.PropertyType; + + if (type.GetTypeInfo().IsEnum) + type = Enum.GetUnderlyingType(type); + + var factory = GetFactory(type); + + return container => factory(property.GetValue(container.ReferenceValue)); + } + + /// <summary> + /// For properties of reference types, we use a generic helper class to get the value. This enables us to use MethodInfo.CreateDelegate + /// to build a fast getter. We can get away with this on .Net Native, because we really only need one runtime instantiation of the + /// generic type, since it's only instantiated over reference types (and thus all instances are shared). + /// </summary> + /// <param name="property"></param> + /// <returns></returns> + private static Func<PropertyValue, PropertyValue> GetReferenceTypePropertyGetter(PropertyInfo property) + { + var helper = (TypeHelper)Activator.CreateInstance(typeof(ReferenceTypeHelper<>).MakeGenericType(property.DeclaringType)); + return helper.GetPropertyGetter(property); + } + + private abstract class TypeHelper + { + public abstract Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property); + + protected Delegate GetGetMethod(PropertyInfo property, Type propertyType) + { + return property.GetMethod.CreateDelegate(typeof(Func<,>).MakeGenericType(property.DeclaringType, propertyType)); + } + } + + private sealed class ReferenceTypeHelper<TContainer> : TypeHelper where TContainer : class + { + public override Func<PropertyValue, PropertyValue> GetPropertyGetter(PropertyInfo property) + { + var type = property.PropertyType; + + if (!Statics.IsValueType(type)) + { + var getter = (Func<TContainer, object>)GetGetMethod(property, type); + return container => new PropertyValue(getter((TContainer)container.ReferenceValue)); + } + else + { + if (type.GetTypeInfo().IsEnum) + type = Enum.GetUnderlyingType(type); + + if (type == typeof(Boolean)) { var f = (Func<TContainer, Boolean>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(Byte)) { var f = (Func<TContainer, Byte>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(SByte)) { var f = (Func<TContainer, SByte>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(Char)) { var f = (Func<TContainer, Char>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(Int16)) { var f = (Func<TContainer, Int16>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(UInt16)) { var f = (Func<TContainer, UInt16>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(Int32)) { var f = (Func<TContainer, Int32>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(UInt32)) { var f = (Func<TContainer, UInt32>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(Int64)) { var f = (Func<TContainer, Int64>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(UInt64)) { var f = (Func<TContainer, UInt64>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(IntPtr)) { var f = (Func<TContainer, IntPtr>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(UIntPtr)) { var f = (Func<TContainer, UIntPtr>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(Single)) { var f = (Func<TContainer, Single>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(Double)) { var f = (Func<TContainer, Double>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(Guid)) { var f = (Func<TContainer, Guid>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(DateTime)) { var f = (Func<TContainer, DateTime>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(DateTimeOffset)) { var f = (Func<TContainer, DateTimeOffset>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(TimeSpan)) { var f = (Func<TContainer, TimeSpan>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + if (type == typeof(Decimal)) { var f = (Func<TContainer, Decimal>)GetGetMethod(property, type); return container => new PropertyValue(f((TContainer)container.ReferenceValue)); } + + return container => new PropertyValue(property.GetValue(container.ReferenceValue)); + } + } + } + } +} |