|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
//
// <OWNER>Microsoft</OWNER>
// <OWNER>Microsoft</OWNER>
// <OWNER>Microsoft</OWNER>
using System;
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Security;
namespace System.Runtime.InteropServices.WindowsRuntime
{
internal class CLRIPropertyValueImpl : IPropertyValue
{
private PropertyType _type;
private Object _data;
// Numeric scalar types which participate in coersion
private static volatile Tuple<Type, PropertyType>[] s_numericScalarTypes;
internal CLRIPropertyValueImpl(PropertyType type, Object data)
{
_type = type;
_data = data;
}
private static Tuple<Type, PropertyType>[] NumericScalarTypes {
get {
if (s_numericScalarTypes == null) {
Tuple<Type, PropertyType>[] numericScalarTypes = new Tuple<Type, PropertyType>[] {
new Tuple<Type, PropertyType>(typeof(Byte), PropertyType.UInt8),
new Tuple<Type, PropertyType>(typeof(Int16), PropertyType.Int16),
new Tuple<Type, PropertyType>(typeof(UInt16), PropertyType.UInt16),
new Tuple<Type, PropertyType>(typeof(Int32), PropertyType.Int32),
new Tuple<Type, PropertyType>(typeof(UInt32), PropertyType.UInt32),
new Tuple<Type, PropertyType>(typeof(Int64), PropertyType.Int64),
new Tuple<Type, PropertyType>(typeof(UInt64), PropertyType.UInt64),
new Tuple<Type, PropertyType>(typeof(Single), PropertyType.Single),
new Tuple<Type, PropertyType>(typeof(Double), PropertyType.Double)
};
s_numericScalarTypes = numericScalarTypes;
}
return s_numericScalarTypes;
}
}
public PropertyType Type {
[Pure]
get { return _type; }
}
public bool IsNumericScalar {
[Pure]
get {
return IsNumericScalarImpl(_type, _data);
}
}
public override string ToString()
{
if (_data != null)
{
return _data.ToString();
}
else
{
return base.ToString();
}
}
[Pure]
public Byte GetUInt8()
{
return CoerceScalarValue<Byte>(PropertyType.UInt8);
}
[Pure]
public Int16 GetInt16()
{
return CoerceScalarValue<Int16>(PropertyType.Int16);
}
public UInt16 GetUInt16()
{
return CoerceScalarValue<UInt16>(PropertyType.UInt16);
}
[Pure]
public Int32 GetInt32()
{
return CoerceScalarValue<Int32>(PropertyType.Int32);
}
[Pure]
public UInt32 GetUInt32()
{
return CoerceScalarValue<UInt32>(PropertyType.UInt32);
}
[Pure]
public Int64 GetInt64()
{
return CoerceScalarValue<Int64>(PropertyType.Int64);
}
[Pure]
public UInt64 GetUInt64()
{
return CoerceScalarValue<UInt64>(PropertyType.UInt64);
}
[Pure]
public Single GetSingle()
{
return CoerceScalarValue<Single>(PropertyType.Single);
}
[Pure]
public Double GetDouble()
{
return CoerceScalarValue<Double>(PropertyType.Double);
}
[Pure]
public char GetChar16()
{
if (this.Type != PropertyType.Char16)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Char16"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (char)_data;
}
[Pure]
public Boolean GetBoolean()
{
if (this.Type != PropertyType.Boolean)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Boolean"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (bool)_data;
}
[Pure]
public String GetString()
{
return CoerceScalarValue<String>(PropertyType.String);
}
[Pure]
public Object GetInspectable()
{
if (this.Type != PropertyType.Inspectable)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Inspectable"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return _data;
}
[Pure]
public Guid GetGuid()
{
return CoerceScalarValue<Guid>(PropertyType.Guid);
}
[Pure]
public DateTimeOffset GetDateTime()
{
if (this.Type != PropertyType.DateTime)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "DateTime"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (DateTimeOffset)_data;
}
[Pure]
public TimeSpan GetTimeSpan()
{
if (this.Type != PropertyType.TimeSpan)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "TimeSpan"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (TimeSpan)_data;
}
[Pure]
[SecuritySafeCritical]
public Point GetPoint()
{
if (this.Type != PropertyType.Point)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Point"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return Unbox<Point>(IReferenceFactory.s_pointType);
}
[Pure]
[SecuritySafeCritical]
public Size GetSize()
{
if (this.Type != PropertyType.Size)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Size"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return Unbox<Size>(IReferenceFactory.s_sizeType);
}
[Pure]
[SecuritySafeCritical]
public Rect GetRect()
{
if (this.Type != PropertyType.Rect)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Rect"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return Unbox<Rect>(IReferenceFactory.s_rectType);
}
[Pure]
public Byte[] GetUInt8Array()
{
return CoerceArrayValue<Byte>(PropertyType.UInt8Array);
}
[Pure]
public Int16[] GetInt16Array()
{
return CoerceArrayValue<Int16>(PropertyType.Int16Array);
}
[Pure]
public UInt16[] GetUInt16Array()
{
return CoerceArrayValue<UInt16>(PropertyType.UInt16Array);
}
[Pure]
public Int32[] GetInt32Array()
{
return CoerceArrayValue<Int32>(PropertyType.Int32Array);
}
[Pure]
public UInt32[] GetUInt32Array()
{
return CoerceArrayValue<UInt32>(PropertyType.UInt32Array);
}
[Pure]
public Int64[] GetInt64Array()
{
return CoerceArrayValue<Int64>(PropertyType.Int64Array);
}
[Pure]
public UInt64[] GetUInt64Array()
{
return CoerceArrayValue<UInt64>(PropertyType.UInt64Array);
}
[Pure]
public Single[] GetSingleArray()
{
return CoerceArrayValue<Single>(PropertyType.SingleArray);
}
[Pure]
public Double[] GetDoubleArray()
{
return CoerceArrayValue<Double>(PropertyType.DoubleArray);
}
[Pure]
public char[] GetChar16Array()
{
if (this.Type != PropertyType.Char16Array)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Char16[]"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (char[])_data;
}
[Pure]
public Boolean[] GetBooleanArray()
{
if (this.Type != PropertyType.BooleanArray)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Boolean[]"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (bool[])_data;
}
[Pure]
public String[] GetStringArray()
{
return CoerceArrayValue<String>(PropertyType.StringArray);
}
[Pure]
public Object[] GetInspectableArray()
{
if (this.Type != PropertyType.InspectableArray)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Inspectable[]"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (Object[])_data;
}
[Pure]
public Guid[] GetGuidArray()
{
return CoerceArrayValue<Guid>(PropertyType.GuidArray);
}
[Pure]
public DateTimeOffset[] GetDateTimeArray()
{
if (this.Type != PropertyType.DateTimeArray)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "DateTimeOffset[]"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (DateTimeOffset[])_data;
}
[Pure]
public TimeSpan[] GetTimeSpanArray()
{
if (this.Type != PropertyType.TimeSpanArray)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "TimeSpan[]"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return (TimeSpan[])_data;
}
[Pure]
[SecuritySafeCritical]
public Point[] GetPointArray()
{
if (this.Type != PropertyType.PointArray)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Point[]"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return UnboxArray<Point>(IReferenceFactory.s_pointType);
}
[Pure]
[SecuritySafeCritical]
public Size[] GetSizeArray()
{
if (this.Type != PropertyType.SizeArray)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Size[]"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return UnboxArray<Size>(IReferenceFactory.s_sizeType);
}
[Pure]
[SecuritySafeCritical]
public Rect[] GetRectArray()
{
if (this.Type != PropertyType.RectArray)
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, "Rect[]"), __HResults.TYPE_E_TYPEMISMATCH);
Contract.EndContractBlock();
return UnboxArray<Rect>(IReferenceFactory.s_rectType);
}
private T[] CoerceArrayValue<T>(PropertyType unboxType) {
// If we contain the type being looked for directly, then take the fast-path
if (Type == unboxType) {
return (T[])_data;
}
// Make sure we have an array to begin with
Array dataArray = _data as Array;
if (dataArray == null) {
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", this.Type, typeof(T).MakeArrayType().Name), __HResults.TYPE_E_TYPEMISMATCH);
}
// Array types are 1024 larger than their equivilent scalar counterpart
BCLDebug.Assert((int)Type > 1024, "Unexpected array PropertyType value");
PropertyType scalarType = Type - 1024;
// If we do not have the correct array type, then we need to convert the array element-by-element
// to a new array of the requested type
T[] coercedArray = new T[dataArray.Length];
for (int i = 0; i < dataArray.Length; ++i) {
try {
coercedArray[i] = CoerceScalarValue<T>(scalarType, dataArray.GetValue(i));
} catch (InvalidCastException elementCastException) {
Exception e = new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueArrayCoersion", this.Type, typeof(T).MakeArrayType().Name, i, elementCastException.Message), elementCastException);
e.SetErrorCode(elementCastException._HResult);
throw e;
}
}
return coercedArray;
}
private T CoerceScalarValue<T>(PropertyType unboxType)
{
// If we are just a boxed version of the requested type, then take the fast path out
if (Type == unboxType) {
return (T)_data;
}
return CoerceScalarValue<T>(Type, _data);
}
private static T CoerceScalarValue<T>(PropertyType type, object value) {
// If the property type is neither one of the coercable numeric types nor IInspectable, we
// should not attempt coersion, even if the underlying value is technically convertable
if (!IsCoercable(type, value) && type != PropertyType.Inspectable) {
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", type, typeof(T).Name), __HResults.TYPE_E_TYPEMISMATCH);
}
try {
// Try to coerce:
// * String <--> Guid
// * Numeric scalars
if (type == PropertyType.String && typeof(T) == typeof(Guid)) {
return (T)(object)Guid.Parse((string)value);
}
else if (type == PropertyType.Guid && typeof(T) == typeof(String)) {
return (T)(object)((Guid)value).ToString("D", System.Globalization.CultureInfo.InvariantCulture);
}
else {
// Iterate over the numeric scalars, to see if we have a match for one of the known conversions
foreach (Tuple<Type, PropertyType> numericScalar in NumericScalarTypes) {
if (numericScalar.Item1 == typeof(T)) {
return (T)Convert.ChangeType(value, typeof(T), System.Globalization.CultureInfo.InvariantCulture);
}
}
}
}
catch (FormatException) {
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", type, typeof(T).Name), __HResults.TYPE_E_TYPEMISMATCH);
}
catch (InvalidCastException) {
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", type, typeof(T).Name), __HResults.TYPE_E_TYPEMISMATCH);
}
catch (OverflowException) {
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueCoersion", type, value, typeof(T).Name), __HResults.DISP_E_OVERFLOW);
}
// If the property type is IInspectable, and we have a nested IPropertyValue, then we need
// to pass along the request to coerce the value.
IPropertyValue ipv = value as IPropertyValue;
if (type == PropertyType.Inspectable && ipv != null) {
if (typeof(T) == typeof(Byte)) {
return (T)(object)ipv.GetUInt8();
}
else if (typeof(T) == typeof(Int16)) {
return (T)(object)ipv.GetInt16();
}
else if (typeof(T) == typeof(UInt16)) {
return (T)(object)ipv.GetUInt16();
}
else if (typeof(T) == typeof(Int32)) {
return (T)(object)ipv.GetUInt32();
}
else if (typeof(T) == typeof(UInt32)) {
return (T)(object)ipv.GetUInt32();
}
else if (typeof(T) == typeof(Int64)) {
return (T)(object)ipv.GetInt64();
}
else if (typeof(T) == typeof(UInt64)) {
return (T)(object)ipv.GetUInt64();
}
else if (typeof(T) == typeof(Single)) {
return (T)(object)ipv.GetSingle();
}
else if (typeof(T) == typeof(Double)) {
return (T)(object)ipv.GetDouble();
}
else {
BCLDebug.Assert(false, "T in coersion function wasn't understood as a type that can be coerced - make sure that CoerceScalarValue and NumericScalarTypes are in sync");
}
}
// Otherwise, this is an invalid coersion
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", type, typeof(T).Name), __HResults.TYPE_E_TYPEMISMATCH);
}
private static bool IsCoercable(PropertyType type, object data) {
// String <--> Guid is allowed
if (type == PropertyType.Guid || type == PropertyType.String) {
return true;
}
// All numeric scalars can also be coerced
return IsNumericScalarImpl(type, data);
}
private static bool IsNumericScalarImpl(PropertyType type, object data) {
if (data.GetType().IsEnum) {
return true;
}
foreach (Tuple<Type, PropertyType> numericScalar in NumericScalarTypes) {
if (numericScalar.Item2 == type) {
return true;
}
}
return false;
}
// Unbox the data stored in the property value to a structurally equivilent type
[Pure]
[SecurityCritical]
private unsafe T Unbox<T>(Type expectedBoxedType) where T : struct {
Contract.Requires(expectedBoxedType != null);
Contract.Requires(Marshal.SizeOf(expectedBoxedType) == Marshal.SizeOf(typeof(T)));
if (_data.GetType() != expectedBoxedType) {
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", _data.GetType(), expectedBoxedType.Name), __HResults.TYPE_E_TYPEMISMATCH);
}
T unboxed = new T();
fixed (byte *pData = &JitHelpers.GetPinningHelper(_data).m_data) {
byte* pUnboxed = (byte*)JitHelpers.UnsafeCastToStackPointer(ref unboxed);
Buffer.Memcpy(pUnboxed, pData, Marshal.SizeOf(unboxed));
}
return unboxed;
}
// Convert the array stored in the property value to a structurally equivilent array type
[Pure]
[SecurityCritical]
private unsafe T[] UnboxArray<T>(Type expectedArrayElementType) where T : struct {
Contract.Requires(expectedArrayElementType != null);
Contract.Requires(Marshal.SizeOf(expectedArrayElementType) == Marshal.SizeOf(typeof(T)));
Array dataArray = _data as Array;
if (dataArray == null || _data.GetType().GetElementType() != expectedArrayElementType) {
throw new InvalidCastException(Environment.GetResourceString("InvalidCast_WinRTIPropertyValueElement", _data.GetType(), expectedArrayElementType.MakeArrayType().Name), __HResults.TYPE_E_TYPEMISMATCH);
}
T[] converted = new T[dataArray.Length];
if (converted.Length > 0) {
fixed (byte * dataPin = &JitHelpers.GetPinningHelper(dataArray).m_data) {
fixed (byte * convertedPin = &JitHelpers.GetPinningHelper(converted).m_data) {
byte *pData = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(dataArray, 0);
byte *pConverted = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(converted, 0);
Buffer.Memcpy(pConverted, pData, checked(Marshal.SizeOf(typeof(T)) * converted.Length));
}
}
}
return converted;
}
}
}
|