summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs')
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs555
1 files changed, 555 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs
new file mode 100644
index 0000000000..c88f13dd0b
--- /dev/null
+++ b/src/mscorlib/src/System/Runtime/InteropServices/WindowsRuntime/CLRIPropertyValueImpl.cs
@@ -0,0 +1,555 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+
+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;
+ }
+ }
+}