diff options
author | dotnet-bot <dotnet-bot@microsoft.com> | 2015-11-09 23:23:18 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2015-11-09 23:23:18 +0300 |
commit | 44376bbb262a53a7481b02144e56ea9b9581ec67 (patch) | |
tree | 7303b2f16e442a6f45135b04c4af028f31eb99d8 /src/Runtime.Base | |
parent | 137a6cf6dd75d53ca8a45c44d05d2e16c820ed15 (diff) |
Initial population of Runtime.Base
Diffstat (limited to 'src/Runtime.Base')
53 files changed, 5546 insertions, 0 deletions
diff --git a/src/Runtime.Base/src/System/Array.cs b/src/Runtime.Base/src/System/Array.cs new file mode 100644 index 000000000..2e50ab2d3 --- /dev/null +++ b/src/Runtime.Base/src/System/Array.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System +{ + // CONTRACT with Runtime + // The Array type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type int + + public class Array + { + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 649 + // This field should be the first field in Array as the runtime/compilers depend on it + private int _numComponents; + +#pragma warning restore + + public int Length + { + get + { + // NOTE: The compiler has assumptions about the implementation of this method. + // Changing the implementation here (or even deleting this) will NOT have the desired impact + return _numComponents; + } + } + } + + // To accomodate class libraries that wish to implement generic interfaces on arrays, all class libraries + // are now required to provide an Array<T> class that derives from Array. + internal class Array<T> : Array + { + } +} diff --git a/src/Runtime.Base/src/System/Attribute.cs b/src/Runtime.Base/src/System/Attribute.cs new file mode 100644 index 000000000..e3648d497 --- /dev/null +++ b/src/Runtime.Base/src/System/Attribute.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System +{ + [AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple = false)] + internal abstract class Attribute + { + } +} + diff --git a/src/Runtime.Base/src/System/AttributeTargets.cs b/src/Runtime.Base/src/System/AttributeTargets.cs new file mode 100644 index 000000000..d09310e4c --- /dev/null +++ b/src/Runtime.Base/src/System/AttributeTargets.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System +{ + // Enum used to indicate all the elements of the + // VOS it is valid to attach this element to. + + internal enum AttributeTargets + { + Assembly = 0x0001, + Module = 0x0002, + Class = 0x0004, + Struct = 0x0008, + Enum = 0x0010, + Constructor = 0x0020, + Method = 0x0040, + Property = 0x0080, + Field = 0x0100, + Event = 0x0200, + Interface = 0x0400, + Parameter = 0x0800, + Delegate = 0x1000, + ReturnValue = 0x2000, + GenericParameter = 0x4000, + + All = Assembly | Module | Class | Struct | Enum | Constructor | + Method | Property | Field | Event | Interface | Parameter | + Delegate | ReturnValue | GenericParameter + } +} diff --git a/src/Runtime.Base/src/System/AttributeUsageAttribute.cs b/src/Runtime.Base/src/System/AttributeUsageAttribute.cs new file mode 100644 index 000000000..10b6cc8d8 --- /dev/null +++ b/src/Runtime.Base/src/System/AttributeUsageAttribute.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*============================================================ +** +** +** +** Purpose: The class denotes how to specify the usage of an attribute +** +** +===========================================================*/ + +namespace System +{ + /* By default, attributes are inherited and multiple attributes are not allowed */ + [AttributeUsage(AttributeTargets.Class, Inherited = true)] + internal sealed class AttributeUsageAttribute : Attribute + { + //Constructors + public AttributeUsageAttribute(AttributeTargets validOn) + { + } + + public AttributeUsageAttribute(AttributeTargets validOn, bool allowMultiple, bool inherited) + { + } + + //Properties. + // Allowing the set properties as it allows a more readable syntax in the specifiers (and are commonly used) + // The get properties will be needed only if these attributes are used at Runtime, however, the compiler + // is getting an internal error if the gets are not defined. + + public bool AllowMultiple + { + get { return false; } + set { } + } + + public bool Inherited + { + get { return false; } + set { } + } + } +} diff --git a/src/Runtime.Base/src/System/Delegate.cs b/src/Runtime.Base/src/System/Delegate.cs new file mode 100644 index 000000000..c3f2dc9f7 --- /dev/null +++ b/src/Runtime.Base/src/System/Delegate.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime; +using System.Runtime.InteropServices; + +namespace System +{ + [StructLayout(LayoutKind.Sequential)] + internal class Delegate + { + } +} diff --git a/src/Runtime.Base/src/System/Diagnostics/ConditionalAttribute.cs b/src/Runtime.Base/src/System/Diagnostics/ConditionalAttribute.cs new file mode 100644 index 000000000..43ecbf4c0 --- /dev/null +++ b/src/Runtime.Base/src/System/Diagnostics/ConditionalAttribute.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Diagnostics +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] + internal sealed class ConditionalAttribute : Attribute + { + public ConditionalAttribute(String conditionString) + { + _conditionString = conditionString; + } + + public String ConditionString + { + get + { + return _conditionString; + } + } + + private String _conditionString; + } +} diff --git a/src/Runtime.Base/src/System/Diagnostics/Debug.cs b/src/Runtime.Base/src/System/Diagnostics/Debug.cs new file mode 100644 index 000000000..2c0529f12 --- /dev/null +++ b/src/Runtime.Base/src/System/Diagnostics/Debug.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime; +using System.Runtime.CompilerServices; + +namespace System.Diagnostics +{ + internal static class Debug + { + [System.Diagnostics.Conditional("DEBUG")] + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void Assert(bool condition, string message) + { + if (!condition) + { + EH.FailFast(RhFailFastReason.InternalError, null); + } + } + + [System.Diagnostics.Conditional("DEBUG")] + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void TriggerGCForGCStress() + { +#if FEATURE_GC_STRESS + if(GCStress.Initialized) + InternalCalls.RhCollect(-1, InternalGCCollectionMode.Blocking); +#endif + } + } +} diff --git a/src/Runtime.Base/src/System/Exception.cs b/src/Runtime.Base/src/System/Exception.cs new file mode 100644 index 000000000..96a5dd677 --- /dev/null +++ b/src/Runtime.Base/src/System/Exception.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace System +{ + public abstract class Exception + { + private string _exceptionString; + + internal Exception() { } + + internal Exception(String str) + { + _exceptionString = str; + } + } + + internal sealed class NullReferenceException : Exception + { + public NullReferenceException() { } + } + + internal sealed class InvalidOperationException : Exception + { + public InvalidOperationException() { } + } + + internal sealed class ArgumentOutOfRangeException : Exception + { + public ArgumentOutOfRangeException() { } + } + + internal sealed class IndexOutOfRangeException : Exception + { + public IndexOutOfRangeException() { } + } + + internal sealed class ArgumentNullException : Exception + { + public ArgumentNullException() { } + } + + internal sealed class NotImplementedException : Exception + { + public NotImplementedException() { } + } + + internal sealed class NotSupportedException : Exception + { + public NotSupportedException() { } + } + + internal sealed class PlatformNotSupportedException : Exception + { + public PlatformNotSupportedException() { } + } + + internal sealed class InvalidCastException : Exception + { + public InvalidCastException() { } + } + + internal sealed class ArrayTypeMismatchException : Exception + { + public ArrayTypeMismatchException() { } + } + + internal sealed class OverflowException : Exception + { + public OverflowException() { } + } + + internal sealed class ArithmeticException : Exception + { + public ArithmeticException() { } + } + + internal sealed class DivideByZeroException : Exception + { + public DivideByZeroException() { } + } + + internal class OutOfMemoryException : Exception + { + public OutOfMemoryException() { } + } +} diff --git a/src/Runtime.Base/src/System/FlagsAttribute.cs b/src/Runtime.Base/src/System/FlagsAttribute.cs new file mode 100644 index 000000000..e0f828483 --- /dev/null +++ b/src/Runtime.Base/src/System/FlagsAttribute.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System +{ + // Custom attribute to indicate that the enum + // should be treated as a bitfield (or set of flags). + // An IDE may use this information to provide a richer + // development experience. + [AttributeUsage(AttributeTargets.Enum, Inherited = false)] + internal class FlagsAttribute : Attribute + { + public FlagsAttribute() + { + } + } +} diff --git a/src/Runtime.Base/src/System/GC.cs b/src/Runtime.Base/src/System/GC.cs new file mode 100644 index 000000000..a4b33d1d4 --- /dev/null +++ b/src/Runtime.Base/src/System/GC.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// +// Exposes features of the Garbage Collector to managed code. +// +// This is an extremely simple initial version that only exposes a small fraction of the API that the CLR +// version does. +// + +namespace System +{ + // !!!!!!!!!!!!!!!!!!!!!!! + // Make sure you change the def in gc.h if you change this! + public enum InternalGCCollectionMode + { + NonBlocking = 0x00000001, + Blocking = 0x00000002, + Optimized = 0x00000004, + } + + public enum GCLatencyMode + { + Batch = 0, + Interactive = 1, + LowLatency = 2, + SustainedLowLatency = 3 + } +} diff --git a/src/Runtime.Base/src/System/MulticastDelegate.cs b/src/Runtime.Base/src/System/MulticastDelegate.cs new file mode 100644 index 000000000..4ec72c72f --- /dev/null +++ b/src/Runtime.Base/src/System/MulticastDelegate.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime; + +namespace System +{ + internal class MulticastDelegate : Delegate + { + } +} diff --git a/src/Runtime.Base/src/System/Nullable.cs b/src/Runtime.Base/src/System/Nullable.cs new file mode 100644 index 000000000..786df8141 --- /dev/null +++ b/src/Runtime.Base/src/System/Nullable.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace System +{ + internal struct Nullable<T> where T : struct + { +#pragma warning disable 169 // The field 'blah' is never used + private readonly Boolean _hasValue; + private T _value; +#pragma warning restore 0169 + } +} diff --git a/src/Runtime.Base/src/System/Object.cs b/src/Runtime.Base/src/System/Object.cs new file mode 100644 index 000000000..fc62c40e9 --- /dev/null +++ b/src/Runtime.Base/src/System/Object.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*============================================================ +** +** +** +** Object is the root class for all CLR objects. This class +** defines only the basics. +** +** +===========================================================*/ + +using System.Runtime; +using System.Diagnostics; + +namespace System +{ + // CONTRACT with Runtime + // The Object type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type EEType* + // VTable Contract: The first vtable slot should be the finalizer for object => The first virtual method in the object class should be the Finalizer + + // The Object is the root class for all object in the CLR System. Object + // is the super class for all other CLR objects and provide a set of methods and low level + // services to subclasses. + + public unsafe class Object + { + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 649 + + private EEType* _pEEType; + // TODO: Consider making this EETypePtr instead of EEType*. + +#pragma warning restore + + // Creates a new instance of an Object. + internal Object() + { + } + + // Allow an object to free resources before the object is reclaimed by the GC. + // CONTRACT with runtime: This method's virtual slot number is hardcoded in the binder. It is an + // implementation detail where it winds up at runtime. + // **** Do not add any virtual methods in this class ahead of this **** + + ~Object() + { + } + + internal unsafe EEType* EEType + { + get + { + // NOTE: if managed code can be run when the GC has objects marked, then this method is + // unsafe. But, generically, we don't expect managed code such as this to be allowed + // to run while the GC is running. + return _pEEType; + //PREFER _pEEType.ToPointer(); + } + } + + private unsafe int GetArrayLength() + { + Debug.Assert(_pEEType->IsArray, "this is only supported on arrays"); + + // m_numComponents is an int field that is directly after _pEEType + fixed (EEType** ptr = &_pEEType) + return *(int*)(ptr + 1); + } + + internal object MemberwiseClone() + { + object objClone; +#if FEATURE_64BIT_ALIGNMENT + if (_pEEType->RequiresAlign8) + { + if (_pEEType->IsArray) + objClone = InternalCalls.RhpNewArrayAlign8(_pEEType, GetArrayLength()); + else if (_pEEType->IsFinalizable) + objClone = InternalCalls.RhpNewFinalizableAlign8(_pEEType); + else + objClone = InternalCalls.RhpNewFastAlign8(_pEEType); + } + else +#endif // FEATURE_64BIT_ALIGNMENT + { + if (_pEEType->IsArray) + objClone = InternalCalls.RhpNewArray(_pEEType, GetArrayLength()); + else if (_pEEType->IsFinalizable) + objClone = InternalCalls.RhpNewFinalizable(_pEEType); + else + objClone = InternalCalls.RhpNewFast(_pEEType); + } + + InternalCalls.RhpCopyObjectContents(objClone, this); + + return objClone; + } + } +} diff --git a/src/Runtime.Base/src/System/ParamArrayAttribute.cs b/src/Runtime.Base/src/System/ParamArrayAttribute.cs new file mode 100644 index 000000000..0edb35b93 --- /dev/null +++ b/src/Runtime.Base/src/System/ParamArrayAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System +{ + // Attribute to indicate array of arguments for variable number of args. + + [AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)] + internal class ParamArrayAttribute : Attribute + { + public ParamArrayAttribute() + { + } + } +} diff --git a/src/Runtime.Base/src/System/Primitives.cs b/src/Runtime.Base/src/System/Primitives.cs new file mode 100644 index 000000000..8c8136438 --- /dev/null +++ b/src/Runtime.Base/src/System/Primitives.cs @@ -0,0 +1,410 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This file contains the basic primitive type definitions (int etc) +// These types are well known to the compiler and the runtime and are basic interchange types that do not change + +// CONTRACT with Runtime +// Each of the data types has a data contract with the runtime. See the contract in the type definition +// + +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System +{ + // CONTRACT with Runtime + // Place holder type for type hierarchy, Compiler/Runtime requires this class + public abstract class ValueType + { + } + + // CONTRACT with Runtime, Compiler/Runtime requires this class + // Place holder type for type hierarchy + public abstract class Enum : ValueType + { + } + + /*============================================================ + ** + ** Class: Boolean + ** + ** + ** Purpose: The boolean class serves as a wrapper for the primitive + ** type boolean. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Boolean type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type bool + + public struct Boolean + { + // Disable compile warning about unused m_value field +#pragma warning disable 0169 + private bool _value; +#pragma warning restore 0169 + } + + + /*============================================================ + ** + ** Class: Char + ** + ** + ** Purpose: This is the value class representing a Unicode character + ** + ** + ===========================================================*/ + + + + // CONTRACT with Runtime + // The Char type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type char + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Char + { + private char _value; + } + + + /*============================================================ + ** + ** Class: SByte + ** + ** + ** Purpose: A representation of a 8 bit 2's complement integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The SByte type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type sbyte + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct SByte + { + private sbyte _value; + } + + + /*============================================================ + ** + ** Class: Byte + ** + ** + ** Purpose: A representation of a 8 bit integer (byte) + ** + ** + ===========================================================*/ + + + // CONTRACT with Runtime + // The Byte type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type bool + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Byte + { + private byte _value; + } + + + /*============================================================ + ** + ** Class: Int16 + ** + ** + ** Purpose: A representation of a 16 bit 2's complement integer. + ** + ** + ===========================================================*/ + + + // CONTRACT with Runtime + // The Int16 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type short + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Int16 + { + private short _value; + } + + /*============================================================ + ** + ** Class: UInt16 + ** + ** + ** Purpose: A representation of a short (unsigned 16-bit) integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Uint16 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type ushort + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct UInt16 + { + private ushort _value; + } + + /*============================================================ + ** + ** Class: Int32 + ** + ** + ** Purpose: A representation of a 32 bit 2's complement integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Int32 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type int + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Int32 + { + private int _value; + } + + + /*============================================================ + ** + ** Class: UInt32 + ** + ** + ** Purpose: A representation of a 32 bit unsigned integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Uint32 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type uint + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct UInt32 + { + private uint _value; + } + + + /*============================================================ + ** + ** Class: Int64 + ** + ** + ** Purpose: A representation of a 64 bit 2's complement integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Int64 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type long + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Int64 + { + private long _value; + } + + + /*============================================================ + ** + ** Class: UInt64 + ** + ** + ** Purpose: A representation of a 64 bit unsigned integer. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The UInt64 type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type ulong + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct UInt64 + { + private ulong _value; + } + + + /*============================================================ + ** + ** Class: Single + ** + ** + ** Purpose: A wrapper class for the primitive type float. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Single type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type float + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Single + { + private float _value; + } + + + /*============================================================ + ** + ** Class: Double + ** + ** + ** Purpose: A representation of an IEEE double precision + ** floating point number. + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The Double type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type double + // This type is LayoutKind Sequential + + [StructLayout(LayoutKind.Sequential)] + public struct Double + { + private double _value; + } + + + + /*============================================================ + ** + ** Class: IntPtr + ** + ** + ** Purpose: Platform independent integer + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The IntPtr type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type void * + + // This type implements == without overriding GetHashCode, Equals, disable compiler warning +#pragma warning disable 0660, 0661 + public struct IntPtr + { + unsafe private void* _value; // The compiler treats void* closest to uint hence explicit casts are required to preserve int behavior + + [Intrinsic] + public static readonly IntPtr Zero; + + [Intrinsic] + public unsafe IntPtr(void* value) + { + _value = value; + } + + [Intrinsic] + public static unsafe explicit operator IntPtr(void* value) + { + return new IntPtr(value); + } + + [Intrinsic] + public static unsafe explicit operator void* (IntPtr value) + { + return value._value; + } + + [Intrinsic] + public unsafe static bool operator ==(IntPtr value1, IntPtr value2) + { + return value1._value != value2._value; + } + + [Intrinsic] + public unsafe static bool operator !=(IntPtr value1, IntPtr value2) + { + return value1._value != value2._value; + } + } +#pragma warning restore 0660, 0661 +#pragma warning restore 0661 + + + /*============================================================ + ** + ** Class: UIntPtr + ** + ** + ** Purpose: Platform independent integer + ** + ** + ===========================================================*/ + + // CONTRACT with Runtime + // The UIntPtr type is one of the primitives understood by the compilers and runtime + // Data Contract: Single field of type void * + + // This type implements == without overriding GetHashCode, Equals, disable compiler warning +#pragma warning disable 0660, 0661 + public struct UIntPtr + { + unsafe private void* _value; + + [Intrinsic] + public static readonly UIntPtr Zero; + + [Intrinsic] + public unsafe UIntPtr(void* value) + { + _value = value; + } + + [Intrinsic] + public static unsafe explicit operator UIntPtr(void* value) + { + return new UIntPtr(value); + } + + [Intrinsic] + public static unsafe explicit operator void* (UIntPtr value) + { + return value._value; + } + + [Intrinsic] + public unsafe static bool operator ==(UIntPtr value1, UIntPtr value2) + { + return value1._value == value2._value; + } + + [Intrinsic] + public unsafe static bool operator !=(UIntPtr value1, UIntPtr value2) + { + return value1._value != value2._value; + } + } +#pragma warning restore 0660, 0661 +} + diff --git a/src/Runtime.Base/src/System/Runtime/BinderIntrinsics.cs b/src/Runtime.Base/src/System/Runtime/BinderIntrinsics.cs new file mode 100644 index 000000000..f4f7626a9 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/BinderIntrinsics.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.CompilerServices; + +namespace System.Runtime +{ + internal static class BinderIntrinsics + { + // NOTE: We rely on the RID of DebugBreak, so it must be kept as the first method declared in this class. + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization | MethodImplOptions.ForwardRef)] + public static void DebugBreak() + { + // Ignore body of method, it will be generated by the binder. + } + + // NOTE: We rely on the RID of GetReturnAddress, so it must be kept as the second method declared in this class. + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization | MethodImplOptions.ForwardRef)] + internal static IntPtr GetReturnAddress() + { + // Ignore body of method, it will be generated by the binder. + return IntPtr.Zero; + } + + // NOTE: We rely on the RID of TailCall_RhpThrowEx, so it must be kept as the third method declared in this class. + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization | MethodImplOptions.ForwardRef)] + internal static void TailCall_RhpThrowEx(Exception e) + { + // Ignore body of method, it will be generated by the binder. + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs b/src/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs new file mode 100644 index 000000000..ca90ea82d --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime; + +namespace System.Runtime +{ + internal unsafe static class CachedInterfaceDispatch + { + [RuntimeExport("RhpCidResolve")] + private static IntPtr RhpCidResolve(object pObject, IntPtr pCell) + { + try + { + EEType* pInterfaceType; + ushort slot; + InternalCalls.RhpGetDispatchCellInfo(pCell, &pInterfaceType, &slot); + IntPtr pTargetCode = RhResolveDispatchWorker(pObject, pInterfaceType, slot); + if (pTargetCode != IntPtr.Zero) + { + return InternalCalls.RhpUpdateDispatchCellCache(pCell, pTargetCode, pObject.EEType); + } + } + catch + { + // Exceptions are not permitted to escape from runtime->managed callbacks + EH.FailFast(RhFailFastReason.InternalError, null); + } + + // "Valid method implementation was not found." + EH.FailFast(RhFailFastReason.InternalError, null); + return IntPtr.Zero; + } + + [RuntimeExport("RhpResolveInterfaceMethod")] + private static IntPtr RhpResolveInterfaceMethod(object pObject, IntPtr pCell) + { + if (pObject == null) + { + // ProjectN Optimizer may perform code motion on dispatch such that it occurs independant of + // null check on "this" pointer. Allow for this case by returning back an invalid pointer. + return IntPtr.Zero; + } + + EEType* pInstanceType = pObject.EEType; + + // This method is used for the implementation of LOAD_VIRT_FUNCTION and in that case the mapping we want + // may already be in the cache. + IntPtr pTargetCode = InternalCalls.RhpSearchDispatchCellCache(pCell, pInstanceType); + if (pTargetCode != IntPtr.Zero) + return pTargetCode; + + // Otherwise call the version of this method that knows how to resolve the method manually. + return RhpCidResolve(pObject, pCell); + } + + [RuntimeExport("RhResolveDispatch")] + private static IntPtr RhResolveDispatch(object pObject, EETypePtr interfaceType, ushort slot) + { + return RhResolveDispatchWorker(pObject, interfaceType.ToPointer(), slot); + } + + [RuntimeExport("RhResolveDispatchOnType")] + private static IntPtr RhResolveDispatchOnType(EETypePtr instanceType, EETypePtr interfaceType, ushort slot) + { + // Type of object we're dispatching on. + EEType* pInstanceType = instanceType.ToPointer(); + + // Type of interface + EEType* pInterfaceType = interfaceType.ToPointer(); + + return DispatchResolve.FindInterfaceMethodImplementationTarget(pInstanceType, + pInterfaceType, + slot); + } + + private static IntPtr RhResolveDispatchWorker(object pObject, EEType* pInterfaceType, ushort slot) + { + // Type of object we're dispatching on. + EEType* pInstanceType = pObject.EEType; + + // Type whose DispatchMap is used. Usually the same as the above but for types which implement ICastable + // we may repeat this process with an alternate type. + EEType* pResolvingInstanceType = pInstanceType; + + IntPtr pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pResolvingInstanceType, + pInterfaceType, + slot); + + if (pTargetCode == IntPtr.Zero && pInstanceType->IsICastable) + { + // Dispatch not resolved through normal dispatch map, try using the ICastable + IntPtr pfnGetImplTypeMethod = pInstanceType->ICastableGetImplTypeMethod; + pResolvingInstanceType = (EEType*)CalliIntrinsics.Call<IntPtr>(pfnGetImplTypeMethod, pObject, new IntPtr(pInterfaceType)); + + pTargetCode = DispatchResolve.FindInterfaceMethodImplementationTarget(pResolvingInstanceType, + pInterfaceType, + slot); + } + + return pTargetCode; + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CalliIntrinsics.cs b/src/Runtime.Base/src/System/Runtime/CalliIntrinsics.cs new file mode 100644 index 000000000..b85614629 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CalliIntrinsics.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsageAttribute(AttributeTargets.Class)] + internal class McgIntrinsicsAttribute : Attribute { } +} + +namespace System.Runtime +{ + using System.Runtime.InteropServices; + + [McgIntrinsics] + internal static unsafe partial class CalliIntrinsics + { + internal static void CallVoid(IntPtr pfn) { Call<int>(pfn); } + internal static void CallVoid(IntPtr pfn, object arg0) { Call<int>(pfn, arg0); } + internal static void CallVoid(IntPtr pfn, IntPtr arg0, object arg1) { Call<int>(pfn, arg0, arg1); } + internal static void CallVoid(IntPtr pfn, RhFailFastReason arg0, object arg1, IntPtr arg2) { Call<int>(pfn, arg0, arg1, arg2); } + internal static void CallVoid(IntPtr pfn, object arg0, IntPtr arg1, int arg2) { Call<int>(pfn, arg0, arg1, arg2); } + + internal static T Call<T>(IntPtr pfn) { throw new NotImplementedException(); } + internal static T Call<T>(IntPtr pfn, object arg0) { throw new NotImplementedException(); } + internal static T Call<T>(IntPtr pfn, IntPtr arg0, object arg1) { throw new NotImplementedException(); } + internal static T Call<T>(IntPtr pfn, RhFailFastReason arg0, object arg1, IntPtr arg2) { throw new NotImplementedException(); } + internal static T Call<T>(IntPtr pfn, object arg0, IntPtr arg1, int arg2) { throw new NotImplementedException(); } + internal static T Call<T>(IntPtr pfn, object arg0, IntPtr arg1) { throw new NotImplementedException(); } + internal static T Call<T>(IntPtr pfn, ExceptionIDs arg0) { throw new NotImplementedException(); } + internal static T Call<T>(IntPtr pfn, object arg0, void* arg1, out Exception arg2) { throw new NotImplementedException(); } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs b/src/Runtime.Base/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs new file mode 100644 index 000000000..09abb19bc --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.CompilerServices +{ + internal unsafe sealed class FixedBufferAttribute : Attribute + { + public FixedBufferAttribute(Type elementType, int length) + { + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CompilerServices/ICastable.cs b/src/Runtime.Base/src/System/Runtime/CompilerServices/ICastable.cs new file mode 100644 index 000000000..4a8161eeb --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CompilerServices/ICastable.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +// +// Support for dynamic interface casting. Specifically implementing this interface on a type will allow the +// type to support interfaces (for the purposes of casting and interface dispatch) that do not appear in its +// interface map. +// + +namespace System.Runtime.CompilerServices +{ + internal interface ICastable + { + // This is called if casting this object to the given interface type would otherwise fail. Casting + // here means the IL isinst and castclass instructions in the case where they are given an interface + // type as the target type. + // + // A return value of true indicates the cast is valid. + // + // If false is returned when this is called as part of a castclass then the usual InvalidCastException + // will be thrown unless an alternate exception is assigned to the castError output parameter. This + // parameter is ignored on successful casts or during the evaluation of an isinst (which returns null + // rather than throwing on error). + // + // No exception should be thrown from this method (it will cause unpredictable effects, including the + // possibility of an immediate failfast). + // + // The results of this call are not cached, so it is advisable to provide a performant implementation. + // + // The results of this call should be invariant for the same class, interface type pair. That is + // because this is the only guard placed before an interface invocation at runtime. If a type decides + // it no longer wants to implement a given interface it has no way to synchronize with callers that + // have already cached this relationship and can invoke directly via the interface pointer. + bool IsInstanceOfInterface(EETypePtr interfaceType, out Exception castError); + + // This is called as part of the interface dispatch mechanism when the dispatcher logic cannot find + // the given interface type in the interface map of this object. + // + // It allows the implementor to return an alternate class type which does implement the interface. The + // interface lookup shall be performed again on this type (failure to find the interface this time + // resulting in a fail fast) and the corresponding implemented method on that class called instead. + // + // Naturally, since the call is dispatched to a method on a class which does not match the type of the + // this pointer, extreme care must be taken in the implementation of the interface methods of this + // surrogate type. + // + // No exception should be thrown from this method (it will cause unpredictable effects, including the + // possibility of an immediate failfast). + // + // There is no error path defined here. By construction all interface dispatches will already have + // been verified via the castclass/isinst mechanism (and thus a call to IsInstanceOfInterface above) + // so this method is expected to succeed in all cases. The contract for interface dispatch does not + // include any errors from the infrastructure, of which this is a part. + // + // The results of this lookup are cached so computation of the result is not as perf-sensitive as + // IsInstanceOfInterface. + EETypePtr GetImplType(EEType interfaceType); + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CompilerServices/IntrinsicAttribute.cs b/src/Runtime.Base/src/System/Runtime/CompilerServices/IntrinsicAttribute.cs new file mode 100644 index 000000000..e9c1940f0 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CompilerServices/IntrinsicAttribute.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.CompilerServices +{ + // This attribute is only for use in a Class Library + internal sealed class IntrinsicAttribute : Attribute + { + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CompilerServices/IsVolatile.cs b/src/Runtime.Base/src/System/Runtime/CompilerServices/IsVolatile.cs new file mode 100644 index 000000000..5cd76fd03 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CompilerServices/IsVolatile.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.CompilerServices +{ + public static class IsVolatile + { + // no instantiation, please! + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CompilerServices/ManuallyManagedAttribute.cs b/src/Runtime.Base/src/System/Runtime/CompilerServices/ManuallyManagedAttribute.cs new file mode 100644 index 000000000..eb516cc95 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CompilerServices/ManuallyManagedAttribute.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.CompilerServices +{ + // Indicates whether or not a given ManuallyManaged method will rendez-vous + // with GC if a suspension is being requested. Most ManuallyManaged methods + // will be PollPolicy.Never. The only distinction made by the code generator + // is between Always and everything else. The distinction between Sometimes + // and Never is merely for documentation purposes. + internal enum GcPollPolicy + { + Always = 1, + Sometimes = 2, + Never = 3, + } + + internal class ManuallyManagedAttribute : Attribute + { + public ManuallyManagedAttribute(GcPollPolicy poll) + { + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CompilerServices/MethodImplAttribute.cs b/src/Runtime.Base/src/System/Runtime/CompilerServices/MethodImplAttribute.cs new file mode 100644 index 000000000..3a210c963 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CompilerServices/MethodImplAttribute.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace System.Runtime.CompilerServices +{ + // This Enum matchs the miImpl flags defined in corhdr.h. It is used to specify + // certain method properties. + internal enum MethodImplOptions + { + NoInlining = 0x0008, + ForwardRef = 0x0010, + NoOptimization = 0x0040, + InternalCall = 0x1000, + } + + // Custom attribute to specify additional method properties. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + sealed internal class MethodImplAttribute : Attribute + { + internal MethodImplOptions _val; + + public MethodImplAttribute(MethodImplOptions methodImplOptions) + { + _val = methodImplOptions; + } + + public MethodImplAttribute(short value) + { + _val = (MethodImplOptions)value; + } + + public MethodImplAttribute() + { + } + + public MethodImplOptions Value { get { return _val; } } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CompilerServices/RuntimeHelpers.cs b/src/Runtime.Base/src/System/Runtime/CompilerServices/RuntimeHelpers.cs new file mode 100644 index 000000000..298bb9d07 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CompilerServices/RuntimeHelpers.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.CompilerServices +{ + internal static class RuntimeHelpers + { + public static int OffsetToStringData + { + get + { + // Number of bytes from the address pointed to by a reference to + // a String to the first 16-bit character in the String. + // This property allows C#'s fixed statement to work on Strings. + return String.FIRST_CHAR_OFFSET; + } + } + + [Intrinsic] + public static extern void InitializeArray(Array array, RuntimeFieldHandle fldHandle); + } +} diff --git a/src/Runtime.Base/src/System/Runtime/CompilerServices/StackOnlyAttribute.cs b/src/Runtime.Base/src/System/Runtime/CompilerServices/StackOnlyAttribute.cs new file mode 100644 index 000000000..d18d3ccd7 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CompilerServices/StackOnlyAttribute.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Struct)] + internal sealed class StackOnlyAttribute : Attribute { } +} diff --git a/src/Runtime.Base/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs b/src/Runtime.Base/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs new file mode 100644 index 000000000..cd1f422a3 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.CompilerServices +{ + internal class UnsafeValueTypeAttribute : Attribute { } +} diff --git a/src/Runtime.Base/src/System/Runtime/DispatchResolve.cs b/src/Runtime.Base/src/System/Runtime/DispatchResolve.cs new file mode 100644 index 000000000..39b9b29b1 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/DispatchResolve.cs @@ -0,0 +1,229 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics; + +namespace System.Runtime +{ + internal unsafe static class DispatchResolve + { + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 649 + public struct DispatchMapEntry + { + public ushort _usInterfaceIndex; + public ushort _usInterfaceMethodSlot; + public ushort _usImplMethodSlot; + } + + public struct DispatchMap + { + public uint _entryCount; + public DispatchMapEntry _dispatchMap; // Actually a variable length array + } +#pragma warning restore + + public static IntPtr FindInterfaceMethodImplementationTarget(EEType* pTgtType, + EEType* pItfType, + ushort itfSlotNumber) + { + // Start at the current type and work up the inheritance chain + EEType* pCur = pTgtType; + UInt32 iCurInheritanceChainDelta = 0; + + if (pItfType->IsCloned) + pItfType = pItfType->CanonicalEEType; + + while (pCur != null) + { + UInt16 implSlotNumber; + if (FindImplSlotForCurrentType( + pCur, pItfType, itfSlotNumber, &implSlotNumber)) + { + IntPtr targetMethod; + if (implSlotNumber < pCur->NumVTableSlots) + { + // true virtual - need to get the slot from the target type in case it got overridden + targetMethod = pTgtType->GetVTableStartAddress()[implSlotNumber]; + } + else + { + // sealed virtual - need to get the slot form the implementing type, because + // it's not present on the target type + targetMethod = pCur->GetSealedVirtualSlot((ushort)(implSlotNumber - pCur->NumVTableSlots)); + } + return targetMethod; + } + if (pCur->IsArray) + pCur = pCur->ArrayBaseType; + else + pCur = pCur->NonArrayBaseType; + iCurInheritanceChainDelta++; + } + return IntPtr.Zero; + } + + + private static bool FindImplSlotForCurrentType(EEType* pTgtType, + EEType* pItfType, + UInt16 itfSlotNumber, + UInt16* pImplSlotNumber) + { + bool fRes = false; + + // If making a call and doing virtual resolution don't look into the dispatch map, + // take the slot number directly. + if (!pItfType->IsInterface) + { + *pImplSlotNumber = itfSlotNumber; + + // Only notice matches if the target type and search types are the same + // This will make dispatch to sealed slots work correctly + return pTgtType == pItfType; + } + + if (pTgtType->HasDispatchMap) + { + // For variant interface dispatch, the algorithm is to walk the parent hierarchy, and at each level + // attempt to dispatch exactly first, and then if that fails attempt to dispatch variantly. This can + // result in interesting behavior such as a derived type only overriding one particular instantiation + // and funneling all the dispatches to it, but its the algorithm. + + bool fDoVariantLookup = false; // do not check variance for first scan of dispatch map + + fRes = FindImplSlotInSimpleMap( + pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, fDoVariantLookup); + + if (!fRes) + { + fDoVariantLookup = true; // check variance for second scan of dispatch map + fRes = FindImplSlotInSimpleMap( + pTgtType, pItfType, itfSlotNumber, pImplSlotNumber, fDoVariantLookup); + } + } + + return fRes; + } + + private static bool FindImplSlotInSimpleMap(EEType* pTgtType, + EEType* pItfType, + UInt32 itfSlotNumber, + UInt16* pImplSlotNumber, + bool actuallyCheckVariance) + { + Debug.Assert(pTgtType->HasDispatchMap, "Missing dispatch map"); + + EEType* pItfOpenGenericType = null; + EETypeRef* pItfInstantiation = null; + int itfArity = 0; + GenericVariance* pItfVarianceInfo = null; + + bool fCheckVariance = false; + bool fArrayCovariance = false; + + if (actuallyCheckVariance) + { + fCheckVariance = pItfType->HasGenericVariance; + fArrayCovariance = pTgtType->IsArray; + + // Non-arrays can follow array variance rules iff + // 1. They have one generic parameter + // 2. That generic parameter is array covariant. + // + // This special case is to allow array enumerators to work + if (!fArrayCovariance && pTgtType->HasGenericVariance) + { + EETypeRef* pTgtInstantiation; + int tgtEntryArity; + GenericVariance* pTgtVarianceInfo; + EEType* pTgtEntryGenericType = InternalCalls.RhGetGenericInstantiation(pTgtType, + &tgtEntryArity, + &pTgtInstantiation, + &pTgtVarianceInfo); + + if ((tgtEntryArity == 1) && pTgtVarianceInfo[0] == GenericVariance.ArrayCovariant) + { + fArrayCovariance = true; + } + } + + // Arrays are covariant even though you can both get and set elements (type safety is maintained by + // runtime type checks during set operations). This extends to generic interfaces implemented on those + // arrays. We handle this by forcing all generic interfaces on arrays to behave as though they were + // covariant (over their one type parameter corresponding to the array element type). + if (fArrayCovariance && pItfType->IsGeneric) + fCheckVariance = true; + + // If there is no variance checking, there is no operation to perform. (The non-variance check loop + // has already completed) + if (!fCheckVariance) + { + return false; + } + } + + DispatchMap* pMap = pTgtType->DispatchMap; + DispatchMapEntry* i = &pMap->_dispatchMap; + DispatchMapEntry* iEnd = (&pMap->_dispatchMap) + pMap->_entryCount; + for (; i != iEnd; ++i) + { + if (i->_usInterfaceMethodSlot == itfSlotNumber) + { + EEType* pCurEntryType = + pTgtType->InterfaceMap[i->_usInterfaceIndex].InterfaceType; + + if (pCurEntryType->IsCloned) + pCurEntryType = pCurEntryType->CanonicalEEType; + + if (pCurEntryType == pItfType) + { + *pImplSlotNumber = i->_usImplMethodSlot; + return true; + } + else if (fCheckVariance && ((fArrayCovariance && pCurEntryType->IsGeneric) || pCurEntryType->HasGenericVariance)) + { + // Interface types don't match exactly but both the target interface and the current interface + // in the map are marked as being generic with at least one co- or contra- variant type + // parameter. So we might still have a compatible match. + + // Retrieve the unified generic instance for the callsite interface if we haven't already (we + // lazily get this then cache the result since the lookup isn't necessarily cheap). + if (pItfOpenGenericType == null) + { + pItfOpenGenericType = InternalCalls.RhGetGenericInstantiation(pItfType, + &itfArity, + &pItfInstantiation, + &pItfVarianceInfo); + } + + // Retrieve the unified generic instance for the interface we're looking at in the map. + // Grab instantiation details for the candidate interface. + EETypeRef* pCurEntryInstantiation; + int curEntryArity; + GenericVariance* pCurEntryVarianceInfo; + EEType* pCurEntryGenericType = InternalCalls.RhGetGenericInstantiation(pCurEntryType, + &curEntryArity, + &pCurEntryInstantiation, + &pCurEntryVarianceInfo); + + // If the generic types aren't the same then the types aren't compatible. + if (pItfOpenGenericType != pCurEntryGenericType) + continue; + + // The types represent different instantiations of the same generic type. The + // arity of both had better be the same. + Debug.Assert(itfArity == curEntryArity, "arity mismatch betweeen generic instantiations"); + + if (TypeCast.TypeParametersAreCompatible(itfArity, pCurEntryInstantiation, pItfInstantiation, pItfVarianceInfo, fArrayCovariance)) + { + *pImplSlotNumber = i->_usImplMethodSlot; + return true; + } + } + } + } + + return false; + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/EEType.cs b/src/Runtime.Base/src/System/Runtime/EEType.cs new file mode 100644 index 000000000..56110a83b --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/EEType.cs @@ -0,0 +1,732 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Runtime +{ + // Fundamental runtime type representation + internal unsafe struct EEType + { + [StructLayout(LayoutKind.Explicit)] + private unsafe struct RelatedTypeUnion + { + // Kinds.CanonicalEEType + [FieldOffset(0)] + public EEType* _pBaseType; + [FieldOffset(0)] + public EEType** _ppBaseTypeViaIAT; + + // Kinds.ClonedEEType + [FieldOffset(0)] + public EEType** _ppCanonicalTypeViaIAT; + + // Kinds.ArrayEEType + [FieldOffset(0)] + public EEType* _pRelatedParameterType; + [FieldOffset(0)] + public EEType** _ppRelatedParameterTypeViaIAT; + } + + // CS0169: The private field '{blah}' is never used + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 169, 649 + + private UInt16 _usComponentSize; + private UInt16 _usFlags; + private UInt32 _uBaseSize; + private RelatedTypeUnion _relatedType; + private UInt16 _usNumVtableSlots; + private UInt16 _usNumInterfaces; + private UInt32 _uHashCode; + + // vtable follows + +#pragma warning restore + + private enum Flags : ushort + { + // There are three kinds of EETypes, the fourth state is currently unused and disallowed. + EETypeKindMask = 0x0003, + + // This flag is set when m_RelatedType is in a different module. In that case, _pRelatedType + // actually points to an IAT slot in this module, which then points to the desired EEType in the + // other module. In other words, there is an extra indirection through m_RelatedType to get to + // the related type in the other module. When this flag is set, it is expected that you use the + // "_ppXxxxViaIAT" member of the RelatedTypeUnion for the particular related type you're + // accessing. + RelatedTypeViaIATFlag = 0x0004, + + // This EEType represents a value type + ValueTypeFlag = 0x0008, + + // This EEType represents a type which requires finalization + HasFinalizerFlag = 0x0010, + + // This type contain gc pointers + HasPointersFlag = 0x0020, + + // This type instance was allocated at runtime (rather than being embedded in a module image) + RuntimeAllocatedFlag = 0x0040, + + // This type is generic and one or more of it's type parameters is co- or contra-variant. This + // only applies to interface and delegate types. + GenericVarianceFlag = 0x0080, + + // This type has optional fields present. + OptionalFieldsFlag = 0x0100, + + // This EEType represents an interface. + IsInterfaceFlag = 0x0200, + + // This type is generic. + IsGenericFlag = 0x0400, + + // We are storing a CorElementType in the upper bits for unboxing enums + CorElementTypeMask = 0xf800, + CorElementTypeShift = 11, + + // Single mark to check TypeKind and two flags. When non-zero, casting is more complicated + ComplexCastingMask = EETypeKindMask | RelatedTypeViaIATFlag | GenericVarianceFlag + }; + + // These are flag values that are rarely set for types. If any of them are set then an optional field will + // be associated with the EEType to represent them. + private enum RareFlags + { + // This type requires 8-byte alignment for its fields on certain platforms (only ARM currently). + RequiresAlign8Flag = 0x00000001, + + // Type implements ICastable to allow dynamic resolution of interface casts. + ICastableFlag = 0x00000002, + + // Type is an instantiation of Nullable<T>. + IsNullableFlag = 0x00000004, + + // Nullable target type stashed in the EEType is indirected via the IAT. + NullableTypeViaIATFlag = 0x00000008, + + // This EEType was created by generic instantiation loader + IsDynamicTypeFlag = 0x00000010, + + // This EEType has a Class Constructor + HasCctorFlag = 0x0000020, + + // This EEType has sealed vtable entries (note that this flag is only used for + // dynamically created types because they always have an optional field (hence the + // very explicit flag name). + IsDynamicTypeWithSealedVTableEntriesFlag = 0x00000040, + + // This EEType was constructed from a universal canonical template, and has + // its own dynamically created DispatchMap (does not use the DispatchMap of its template type) + HasDynamicallyAllocatedDispatchMapFlag = 0x00000080, + + // This EEType represents a structure that is an HFA + IsHFAFlag = 0x00000100, + } + + private enum Kinds : ushort + { + CanonicalEEType = 0x0000, + ClonedEEType = 0x0001, + ParameterizedEEType = 0x0002, // parameter eetype, currently either pointer or single dimensional array type + GenericTypeDefEEType = 0x0003, // Generic type definition EEType + } + + private Kinds Kind + { + get + { + return (Kinds)(_usFlags & (ushort)Flags.EETypeKindMask); + } + } + + internal UInt16 FlagBits + { + get + { + return _usFlags; + } + } + + internal UInt32 BaseSize + { + get + { + return _uBaseSize; + } + } + + internal UInt16 ComponentSize + { + get + { + return _usComponentSize; + } + } + + internal UInt16 NumVTableSlots + { + get + { + return _usNumVtableSlots; + } + } + + internal bool IsFinalizable + { + get + { + return ((_usFlags & (ushort)Flags.HasFinalizerFlag) != 0); + } + } + + internal bool IsInterface + { + get + { + return ((_usFlags & (ushort)Flags.IsInterfaceFlag) != 0); + } + } + + internal bool IsCanonical + { + get + { + return Kind == Kinds.CanonicalEEType; + } + } + + internal bool IsCloned + { + get + { + return Kind == Kinds.ClonedEEType; + } + } + + internal bool HasReferenceFields + { + get + { + return ((_usFlags & (UInt16)Flags.HasPointersFlag) != 0); + } + } + + internal bool HasOptionalFields + { + get + { + return ((_usFlags & (UInt16)Flags.OptionalFieldsFlag) != 0); + } + } + + // RH only natively supports single-dimensional arrays, so this check implicitly means + // "is single dimensional array" + internal bool IsArray + { + get + { + return IsParameterizedType && ParameterizedTypeShape != 0; // See comment above ParameterizedTypeShape for details. + } + } + + internal bool IsParameterizedType + { + get + { + return Kind == Kinds.ParameterizedEEType; + } + } + + // The parameterized type shape defines the particular form of parameterized type that + // is being represented. + // Currently, the meaning is a shape of 0 indicates that this is a Pointer + // and non-zero indicates that this is an array. + // Two types are not equivalent if their shapes do not exactly match. + internal UInt32 ParameterizedTypeShape + { + get + { + return _uBaseSize; + } + } + + internal bool IsRelatedTypeViaIAT + { + get + { + return ((_usFlags & (UInt16)Flags.RelatedTypeViaIATFlag) != 0); + } + } + + internal bool IsValueType + { + get + { + return ((_usFlags & (UInt16)Flags.ValueTypeFlag) != 0); + } + } + + internal bool IsReferenceType + { + get + { + return !IsValueType; + } + } + + internal bool IsRuntimeAllocated + { + get + { + return ((_usFlags & (UInt16)Flags.RuntimeAllocatedFlag) != 0); + } + } + + internal bool IsGeneric + { + get + { + return (_usFlags & (UInt16)Flags.IsGenericFlag) != 0; + } + } + + // Mark or determine that a type is generic and one or more of it's type parameters is co- or + // contra-variant. This only applies to interface and delegate types. + internal bool HasGenericVariance + { + get + { + return ((_usFlags & (UInt16)Flags.GenericVarianceFlag) != 0); + } + } + + // Mark or determine that a type requires 8-byte alignment for its fields (required only on certain + // platforms, only ARM so far). + internal unsafe bool RequiresAlign8 + { + get + { +#if FEATURE_64BIT_ALIGNMENT + // For now this access to RareFlags is the only managed access required to optional fields. So + // do the flags lookup via a co-op call into the runtime rather than duplicate all of the + // logic to locate and decompress optional fields in managed code. + fixed (EEType* pThis = &this) + return (InternalCalls.RhpGetEETypeRareFlags(pThis) & (UInt32)RareFlags.RequiresAlign8Flag) != 0; +#else + return false; +#endif + } + } + + // Determine whether a type supports ICastable. + internal unsafe bool IsICastable + { + get + { + fixed (EEType* pThis = &this) + return (InternalCalls.RhpGetEETypeRareFlags(pThis) & (UInt32)RareFlags.ICastableFlag) != 0; + } + } + + // For an ICastable type return a pointer to code that implements ICastable.IsInstanceOfInterface. + internal unsafe IntPtr ICastableIsInstanceOfInterfaceMethod + { + get + { + fixed (EEType* pThis = &this) + return InternalCalls.RhpGetICastableIsInstanceOfInterfaceMethod(pThis); + } + } + + // For an ICastable type return a pointer to code that implements ICastable.GetImplTypeMethod. + internal unsafe IntPtr ICastableGetImplTypeMethod + { + get + { + fixed (EEType* pThis = &this) + return InternalCalls.RhpGetICastableGetImplTypeMethod(pThis); + } + } + + // Determine whether a type is an instantiation of Nullable<T>. + internal unsafe bool IsNullable + { + get + { + fixed (EEType* pThis = &this) + return (InternalCalls.RhpGetEETypeRareFlags(pThis) & (UInt32)RareFlags.IsNullableFlag) != 0; + } + } + + // Retrieve the value type T from a Nullable<T>. + internal unsafe EEType* GetNullableType() + { + fixed (EEType* pThis = &this) + return InternalCalls.RhpGetNullableEEType(pThis); + } + + // Retrieve the offset of the value embedded in a Nullable<T>. + internal unsafe byte GetNullableValueOffset() + { + fixed (EEType* pThis = &this) + return InternalCalls.RhpGetNullableEETypeValueOffset(pThis); + } + + internal unsafe bool IsDynamicType + { + get + { + fixed (EEType* pThis = &this) + return (InternalCalls.RhpGetEETypeRareFlags(pThis) & (UInt32)RareFlags.IsDynamicTypeFlag) != 0; + } + } + + internal EEType* CanonicalEEType + { + get + { + // cloned EETypes must always refer to types in other modules + Debug.Assert(IsCloned, "only cloned EETypes have canonical equivalents"); + Debug.Assert(IsRelatedTypeViaIAT, "cloned types should point to their master via IAT"); + + return *_relatedType._ppCanonicalTypeViaIAT; + } + } + + internal EEType* NonArrayBaseType + { + get + { + Debug.Assert(!IsArray, "array type not supported in BaseType"); + + if (IsCloned) + { + // Assuming that since this is not an Array, the CanonicalEEType is also not an array + return CanonicalEEType->NonArrayBaseType; + } + + Debug.Assert(IsCanonical, "we expect canonical types here"); + + if (IsRelatedTypeViaIAT) + { + return *_relatedType._ppBaseTypeViaIAT; + } + + return _relatedType._pBaseType; + } + } + + internal EEType* ArrayBaseType + { + get + { + fixed (EEType* pThis = &this) + return InternalCalls.RhpGetArrayBaseType(pThis); + } + } + + internal EEType* NonClonedNonArrayBaseType + { + get + { + Debug.Assert(!IsArray, "array type not supported in NonArrayBaseType"); + Debug.Assert(!IsCloned, "cloned type not supported in NonClonedNonArrayBaseType"); + Debug.Assert(IsCanonical || IsGenericTypeDefinition, "we expect canonical types here"); + + if (IsRelatedTypeViaIAT) + { + return *_relatedType._ppBaseTypeViaIAT; + } + + return _relatedType._pBaseType; + } + } + + internal EEType* BaseType + { + get + { + Debug.Assert(!IsParameterizedType, "array type not supported in NonArrayBaseType"); + Debug.Assert(!IsCloned, "cloned type not supported in NonClonedNonArrayBaseType"); + Debug.Assert(IsCanonical, "we expect canonical types here"); + Debug.Assert(!IsRelatedTypeViaIAT, "Non IAT"); + + return _relatedType._pBaseType; + } + set + { + Debug.Assert(!IsParameterizedType, "array type not supported in NonArrayBaseType"); + Debug.Assert(!IsCloned, "cloned type not supported in NonClonedNonArrayBaseType"); + Debug.Assert(IsCanonical, "we expect canonical types here"); + _usFlags &= (ushort)~Flags.RelatedTypeViaIATFlag; + _relatedType._pBaseType = value; + } + } + + internal EEType* RelatedParameterType + { + get + { + Debug.Assert(IsParameterizedType, "RelatedParameterType can only be used on array or pointer EETypees"); + Debug.Assert(!IsCloned, "cloned array types are not allowed"); + + if (IsRelatedTypeViaIAT) + { + return *_relatedType._ppRelatedParameterTypeViaIAT; + } + + return _relatedType._pRelatedParameterType; + } + } + + internal int NumInterfaces + { + get + { + return _usNumInterfaces; + } + } + + internal uint HashCode + { + get + { + return _uHashCode; + } + } + + + internal EEInterfaceInfo* InterfaceMap + { + get + { + fixed (EEType* start = &this) + { + // interface info table starts after the vtable and has m_usNumInterfaces entries + return (EEInterfaceInfo*)((byte*)start + sizeof(EEType) + sizeof(void*) * _usNumVtableSlots); + } + } + } + + internal bool HasDispatchMap + { + get + { + fixed (EEType* pThis = &this) + return InternalCalls.RhpHasDispatchMap(pThis); + } + } + + internal DispatchResolve.DispatchMap* DispatchMap + { + get + { + fixed (EEType* pThis = &this) + return InternalCalls.RhpGetDispatchMap(pThis); + } + } + + internal TypeCast.CorElementType CorElementType + { + get + { + return (TypeCast.CorElementType)((_usFlags & (ushort)Flags.CorElementTypeMask) >> (ushort)Flags.CorElementTypeShift); + } + } + + internal unsafe IntPtr* GetVTableStartAddress() + { + byte* pResult; + + // EETypes are always in unmanaged memory, so 'leaking' the 'fixed pointer' is safe. + fixed (EEType* pThis = &this) + pResult = (byte*)pThis; + + pResult += sizeof(EEType); + return (IntPtr*)pResult; + } + + internal IntPtr GetSealedVirtualSlot(ushort index) + { + fixed (EEType* pThis = &this) + return InternalCalls.RhpGetSealedVirtualSlot(pThis, index); + } + + // Returns an address in the module most closely associated with this EEType that can be handed to + // EH.GetClasslibException and use to locate the compute the correct exception type. In most cases + // this is just the EEType pointer itself, but when this type represents a generic that has been + // unified at runtime (and thus the EEType pointer resides in the process heap rather than a specific + // module) we need to do some work. + internal unsafe IntPtr GetAssociatedModuleAddress() + { + fixed (EEType* pThis = &this) + { + if (!IsRuntimeAllocated && !IsDynamicType) + return (IntPtr)pThis; + + // There are currently three types of runtime allocated EETypes, arrays, pointers, and generic types. + // Arrays/Pointers can be handled by looking at their element type. + if (IsParameterizedType) + return pThis->RelatedParameterType->GetAssociatedModuleAddress(); + + // Generic types are trickier. Often we could look at the parent type (since eventually it + // would derive from the class library's System.Object which is definitely not runtime + // allocated). But this breaks down for generic interfaces. Instead we fetch the generic + // instantiation information and use the generic type definition, which will always be module + // local. We know this lookup will succeed since we're dealing with a unified generic type + // and the unification process requires this metadata. + EETypeRef* pInstantiation; + int arity; + GenericVariance* pVarianceInfo; + EEType* pGenericType = InternalCalls.RhGetGenericInstantiation(pThis, + &arity, + &pInstantiation, + &pVarianceInfo); + + Debug.Assert(pGenericType != null, "Generic type expected"); + + return (IntPtr)pGenericType; + } + } + + internal bool IsGenericTypeDefinition + { + get + { + return Kind == Kinds.GenericTypeDefEEType; + } + } + + internal bool IsPointerTypeDefinition + { + get + { + if (Kind != Kinds.ParameterizedEEType) + return false; + return ParameterizedTypeShape == 0; // See comment above ParameterizedTypeShape for details. + } + } + + // Get the address of the finalizer method for finalizable types. + internal IntPtr FinalizerCode + { + get + { + Debug.Assert(IsFinalizable, "Can't get finalizer for non-finalizeable type"); + fixed (EEType* start = &this) + { + // Finalizer code address is stored after the vtable and interface map. + return *(IntPtr*)((byte*)start + + sizeof(EEType) + + (sizeof(void*) * _usNumVtableSlots) + + (sizeof(EEInterfaceInfo) * NumInterfaces)); + } + } + } + + /// <summary> + /// Return true if type is good for simple casting : canonical, no related type via IAT, no generic variance + /// </summary> + internal bool SimpleCasting() + { + return (_usFlags & (ushort)Flags.ComplexCastingMask) == (ushort)Kinds.CanonicalEEType; + } + + /// <summary> + /// Does this type have a class constructor. + /// </summary> + internal bool HasCctor + { + get + { + fixed (EEType* pThis = &this) + return (InternalCalls.RhpGetEETypeRareFlags(pThis) & (UInt32)RareFlags.HasCctorFlag) != 0; + } + } + + /// <summary> + /// Return true if both types are good for simple casting: canonical, no related type via IAT, no generic variance + /// </summary> + static internal bool BothSimpleCasting(EEType* pThis, EEType* pOther) + { + return ((pThis->_usFlags | pOther->_usFlags) & (ushort)Flags.ComplexCastingMask) == (ushort)Kinds.CanonicalEEType; + } + } + + // CS0169: The private field '{blah}' is never used + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 169, 649 + + // Wrapper around EEType pointers that may be indirected through the IAT if their low bit is set. + internal unsafe struct EETypeRef + { + private byte* _value; + + public EEType* Value + { + get + { + if (((int)_value & 1) == 0) + return (EEType*)_value; + return *(EEType**)(_value - 1); + } + } + } + + internal unsafe struct EEInterfaceInfo + { + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InterfaceTypeUnion + { + [FieldOffset(0)] + public EEType* _pInterfaceEEType; + [FieldOffset(0)] + public EEType** _ppInterfaceEETypeViaIAT; + } + + private InterfaceTypeUnion _interfaceType; + + internal EEType* InterfaceType + { + get + { + if ((unchecked((uint)_interfaceType._pInterfaceEEType) & 1u) != 0) + { +#if WIN32 + EEType** ppInterfaceEETypeViaIAT = (EEType**)(((uint)_interfaceType._ppInterfaceEETypeViaIAT) & ~1u); +#else + EEType** ppInterfaceEETypeViaIAT = (EEType**)(((ulong)_interfaceType._ppInterfaceEETypeViaIAT) & ~1ul); +#endif + return *ppInterfaceEETypeViaIAT; + } + + return _interfaceType._pInterfaceEEType; + } + set { _interfaceType._pInterfaceEEType = value; } + } + +#pragma warning restore + }; + + internal static class WellKnownEETypes + { + // Returns true if the passed in EEType is the EEType for System.Object + // This is recognized by the fact that System.Object and interfaces are the only ones without a base type + internal static unsafe bool IsSystemObject(EEType* pEEType) + { + if (pEEType->IsArray) + return false; + return (pEEType->NonArrayBaseType == null) && !pEEType->IsInterface; + } + + // Returns true if the passed in EEType is the EEType for System.Array. + // The binder sets a special CorElementType for this well known type + internal static unsafe bool IsSystemArray(EEType* pEEType) + { + return (pEEType->CorElementType == TypeCast.CorElementType.ELEMENT_TYPE_ARRAY); + } + } +} // System.Runtime diff --git a/src/Runtime.Base/src/System/Runtime/EETypePtr.cs b/src/Runtime.Base/src/System/Runtime/EETypePtr.cs new file mode 100644 index 000000000..af8a61779 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/EETypePtr.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.InteropServices; +/*============================================================ +** +** Class: EETypePtr +** +** +** Purpose: Pointer Type to a EEType in the runtime. +** +** +===========================================================*/ + +namespace System +{ + // This type does not implement GetHashCode but implements Equals +#pragma warning disable 0659 + + [StructLayout(LayoutKind.Sequential)] + public struct EETypePtr + { + private IntPtr _value; + + internal EETypePtr(IntPtr value) + { + _value = value; + } + + internal bool Equals(EETypePtr p) + { + return (_value == p._value); + } + + internal unsafe System.Runtime.EEType* ToPointer() + { + return (System.Runtime.EEType*)(void*)_value; + } + } +} + + diff --git a/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs b/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs new file mode 100644 index 000000000..c97d1defc --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs @@ -0,0 +1,958 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#define FEATURE_CLR_EH + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Runtime +{ + public enum RhFailFastReason + { + Unknown = 0, + InternalError = 1, // "Runtime internal error" + UnhandledException_ExceptionDispatchNotAllowed = 2, // "Unhandled exception: no handler found before escaping a finally clause or other fail-fast scope." + UnhandledException_CallerDidNotHandle = 3, // "Unhandled exception: no handler found in calling method." + ClassLibDidNotTranslateExceptionID = 4, // "Unable to translate failure into a classlib-specific exception object." + IllegalNativeCallableEntry = 5, // "Invalid Program: attempted to call a NativeCallable method from runtime-typesafe code." + + PN_UnhandledException = 6, // ProjectN: "unhandled exception" + PN_UnhandledExceptionFromPInvoke = 7, // ProjectN: "Unhandled exception: an unmanaged exception was thrown out of a managed-to-native transition." + Max + } + + // Keep this synchronized with the duplicate definition in DebugEventSource.cpp + [Flags] + internal enum ExceptionEventKind + { + Thrown = 1, + CatchHandlerFound = 2, + Unhandled = 4, + FirstPassFrameEntered = 8 + } + + internal unsafe static class DebuggerNotify + { + // We cache the events a debugger is interested on the C# side to avoid p/invokes when the + // debugger isn't attached. + // + // Ideally we would like the managed debugger to start toggling this directly so that + // it stays perfectly up-to-date. However as a reasonable approximation we fetch + // the value from native code at the beginning of each exception dispatch. If the debugger + // attempts to enroll in more events mid-exception handling we aren't going to see it. + private static ExceptionEventKind s_cachedEventMask; + + internal static void BeginFirstPass(Exception e, byte* faultingIP, UIntPtr faultingFrameSP) + { +#if FEATURE_CLR_EH + s_cachedEventMask = InternalCalls.RhpGetRequestedExceptionEvents(); + + if ((s_cachedEventMask & ExceptionEventKind.Thrown) == 0) + return; + + InternalCalls.RhpSendExceptionEventToDebugger(ExceptionEventKind.Thrown, faultingIP, faultingFrameSP); +#endif // FEATURE_CLR_EH + } + + internal static void FirstPassFrameEntered(Exception e, byte* enteredFrameIP, UIntPtr enteredFrameSP) + { +#if FEATURE_CLR_EH + s_cachedEventMask = InternalCalls.RhpGetRequestedExceptionEvents(); + + if ((s_cachedEventMask & ExceptionEventKind.FirstPassFrameEntered) == 0) + return; + + InternalCalls.RhpSendExceptionEventToDebugger(ExceptionEventKind.FirstPassFrameEntered, enteredFrameIP, enteredFrameSP); +#endif // FEATURE_CLR_EH + } + + internal static void EndFirstPass(Exception e, byte* handlerIP, UIntPtr handlingFrameSP) + { +#if FEATURE_CLR_EH + if (handlerIP == null) + { + if ((s_cachedEventMask & ExceptionEventKind.Unhandled) == 0) + return; + InternalCalls.RhpSendExceptionEventToDebugger(ExceptionEventKind.Unhandled, null, UIntPtr.Zero); + } + else + { + if ((s_cachedEventMask & ExceptionEventKind.CatchHandlerFound) == 0) + return; + InternalCalls.RhpSendExceptionEventToDebugger(ExceptionEventKind.CatchHandlerFound, handlerIP, handlingFrameSP); + } +#endif // FEATURE_CLR_EH + } + + internal static void BeginSecondPass() + { + //desktop debugging has an unwind begin event, however it appears that is unneeded for now, and possibly + // will never be needed? + } + } + + public unsafe static class EH + { + internal static UIntPtr c_maxSP = new UIntPtr(unchecked((void*)(ulong)-1L)); + + private enum RhEHClauseKind + { + RH_EH_CLAUSE_TYPED = 0, + RH_EH_CLAUSE_FAULT = 1, + + #region CLR Exceptions + RH_EH_CLAUSE_FILTER = 2, + RH_EH_CLAUSE_UNUSED = 3, + #endregion + } + + private struct RhEHClause + { + internal RhEHClauseKind _clauseKind; + internal uint _tryStartOffset; + internal uint _tryEndOffset; + internal uint _filterOffset; + internal uint _handlerOffset; + internal void* _pTargetType; + + ///<summary> + /// We expect the stackwalker to adjust return addresses to point at 'return address - 1' so that we + /// can use an interval here that is closed at the start and open at the end. When a hardware fault + /// occurs, the IP is pointing at the start of the instruction and will not be adjusted by the + /// stackwalker. Therefore, it will naturally work with an interval that has a closed start and open + /// end. + ///</summary> + public bool ContainsCodeOffset(uint codeOffset) + { + return ((codeOffset >= _tryStartOffset) && + (codeOffset < _tryEndOffset)); + } + } + [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__EHEnum)] + + private struct EHEnum + { + [FieldOffset(0)] + private IntPtr _dummy; // For alignment + } + + // This is a fail-fast function exported by the runtime that will terminate the process + // with as little effort as possible. No guarantee is made about Watson behavior, whether a + // "debug break" is used, etc. + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void FailFast(RhFailFastReason reason, Exception unhandledException) + { + BinderIntrinsics.DebugBreak(); + } + + // Constants used with RhpGetClasslibFunction, to indicate which classlib function + // we are interested in. + // Note: make sure you change the def in EHHelpers.cpp if you change this! + internal enum ClassLibFunctionId + { + GetRuntimeException = 0, + FailFast = 1, + // UnhandledExceptionHandler = 2, // unused + AppendExceptionStackFrame = 3, + } + + // Given an address pointing somewhere into a managed module, get the classlib-defined fail-fast + // function and invoke it. Any failure to find and invoke the function, or if it returns, results in + // Rtm-define fail-fast behavior. + internal unsafe static void FailFastViaClasslib(RhFailFastReason reason, Exception unhandledException, + IntPtr classlibAddress) + { + // Find the classlib function that will fail fast. This is a RuntimeExport function from the + // classlib module, and is therefore managed-callable. + IntPtr pFailFastFunction = (IntPtr)InternalCalls.RhpGetClasslibFunction(classlibAddress, + ClassLibFunctionId.FailFast); + + if (pFailFastFunction == IntPtr.Zero) + { + // The classlib didn't provide a function, so we fail our way... + FailFast(reason, unhandledException); + } + + try + { + // Invoke the classlib fail fast function. + CalliIntrinsics.CallVoid(pFailFastFunction, reason, unhandledException, IntPtr.Zero); + } + catch + { + // Unfortunately, this catch turns into "catch (System.Object)", which will not catch + // exceptions thrown from the class library because their objects do not derive from our + // System.Object. + // + // @TODO: Use a filtered catch whose filter always returns 'true'. + } + + // The classlib's funciton should never return and should not throw. If it does, then we fail our way... + FailFast(reason, unhandledException); + } + +#if AMD64 + [StructLayout(LayoutKind.Explicit, Size = 0x4d0)] +#elif ARM + [StructLayout(LayoutKind.Explicit, Size=0x1a0)] +#elif X86 + [StructLayout(LayoutKind.Explicit, Size=0x2cc)] +#else + [StructLayout(LayoutKind.Explicit, Size = 0x10)] // this is small enough that it should trip an assert in RhpCopyContextFromExInfo +#endif + private struct OSCONTEXT + { + } + +#if FEATURE_CLR_EH +#if ARM + const int c_IPAdjustForHardwareFault = 2; +#else + private const int c_IPAdjustForHardwareFault = 1; +#endif + + internal unsafe static void* PointerAlign(void* ptr, int alignmentInBytes) + { + int alignMask = alignmentInBytes - 1; +#if BIT64 + return (void*)((((long)ptr) + alignMask) & ~alignMask); +#else + return (void*)((((int)ptr) + alignMask) & ~alignMask); +#endif + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + internal unsafe static void UnhandledExceptionFailFastViaClasslib( + RhFailFastReason reason, Exception unhandledException, ref ExInfo exInfo) + { + IntPtr pFailFastFunction = (IntPtr)InternalCalls.RhpGetClasslibFunction(exInfo._pExContext->IP, + ClassLibFunctionId.FailFast); + + if (pFailFastFunction == IntPtr.Zero) + { + FailFastViaClasslib( + reason, + unhandledException, + exInfo._pExContext->IP); + } + + // 16-byte align the context. This is overkill on x86 and ARM, but simplifies things slightly. + const int contextAlignment = 16; + byte* pbBuffer = stackalloc byte[sizeof(OSCONTEXT) + contextAlignment]; + void* pContext = PointerAlign(pbBuffer, contextAlignment); + + // We 'normalized' the faulting IP of hardware faults to behave like return addresses. Undo this + // normalization here so that we report the correct thing in the exception context record. + if ((exInfo._kind & ExKind.KindMask) == ExKind.HardwareFault) + { + exInfo._pExContext->IP = (IntPtr)(((byte*)exInfo._pExContext->IP) - c_IPAdjustForHardwareFault); + } + + InternalCalls.RhpCopyContextFromExInfo(pContext, sizeof(OSCONTEXT), exInfo._pExContext); + + try + { + CalliIntrinsics.CallVoid(pFailFastFunction, reason, unhandledException, (IntPtr)pContext); + } + catch + { + // Unfortunately, this catch turns into "catch (System.Object)", which will not catch + // exceptions thrown from the class library because their objects do not derive from our + // System.Object. + // + // @TODO: Use a filtered catch whose filter always returns 'true'. + } + + // The classlib's funciton should never return and should not throw. If it does, then we fail our way... + FailFast(reason, unhandledException); + } +#endif // FEATURE_CLR_EH + + private enum RhEHFrameType + { + RH_EH_FIRST_FRAME = 1, + RH_EH_FIRST_RETHROW_FRAME = 2, + } + + internal unsafe static void AppendExceptionStackFrameViaClasslib( + Exception exception, IntPtr IP, bool isFirstRethrowFrame, IntPtr classlibAddress, bool isFirstFrame) + { + IntPtr pAppendStackFrame = (IntPtr)InternalCalls.RhpGetClasslibFunction(classlibAddress, + ClassLibFunctionId.AppendExceptionStackFrame); + int flags = (isFirstFrame ? (int)RhEHFrameType.RH_EH_FIRST_FRAME : 0) | + (isFirstRethrowFrame ? (int)RhEHFrameType.RH_EH_FIRST_RETHROW_FRAME : 0); + + if (pAppendStackFrame != IntPtr.Zero) + { + try + { + CalliIntrinsics.CallVoid(pAppendStackFrame, exception, IP, flags); + } + catch + { + // Unfortunately, this catch turns into "catch (System.Object)", which will not catch + // exceptions thrown from the class library because their objects do not derive from our + // System.Object. + // + // @TODO: Use a filtered catch whose filter always returns 'true'. + } + } + } + + // Given an ExceptionID and an address pointing somewhere into a managed module, get + // an exception object of a type that the module contianing the given address will understand. + // This finds the classlib-defined GetRuntimeException function and asks it for the exception object. + internal static Exception GetClasslibException(ExceptionIDs id, IntPtr address) + { + unsafe + { + // Find the classlib function that will give us the exception object we want to throw. This + // is a RuntimeExport function from the classlib module, and is therefore managed-callable. + void* pGetRuntimeExceptionFunction = + InternalCalls.RhpGetClasslibFunction(address, ClassLibFunctionId.GetRuntimeException); + + // Return the exception object we get from the classlib. + Exception e = null; + try + { + e = CalliIntrinsics.Call<Exception>((IntPtr)pGetRuntimeExceptionFunction, id); + } + catch + { + // Unfortunately, this catch turns into "catch (System.Object)", which will not catch + // exceptions thrown from the class library because their objects do not derive from our + // System.Object. + // + // @TODO: Use a filtered catch whose filter always returns 'true'. + } + + // If the helper fails to yield an object, then we fail-fast. + if (e == null) + { + FailFastViaClasslib( + RhFailFastReason.ClassLibDidNotTranslateExceptionID, + null, + address); + } + + return e; + } + } + + #region ItWouldBeNiceToRemoveThese + // These four functions are used to implement the special THROW_* MDIL instructions. + [MethodImpl(MethodImplOptions.NoInlining)] + [RuntimeExport("RhExceptionHandling_ThrowClasslibOverflowException")] + public static void ThrowClasslibOverflowException() + { + // Throw the overflow exception defined by the classlib, using the return address from this helper + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.Overflow; + + IntPtr returnAddr = BinderIntrinsics.GetReturnAddress(); + Exception e = GetClasslibException(exID, returnAddr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + throw e; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + [RuntimeExport("RhExceptionHandling_ThrowClasslibDivideByZeroException")] + public static void ThrowClasslibDivideByZeroException() + { + // Throw the divide by zero exception defined by the classlib, using the return address from this + // helper to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.DivideByZero; + + IntPtr returnAddr = BinderIntrinsics.GetReturnAddress(); + Exception e = GetClasslibException(exID, returnAddr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + throw e; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + [RuntimeExport("RhExceptionHandling_ThrowClasslibArithmeticException")] + public static void ThrowClasslibArithmeticException() + { + // Throw the arithmetic exception defined by the classlib, using the return address from this + // helper to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.Arithmetic; + + IntPtr returnAddr = BinderIntrinsics.GetReturnAddress(); + Exception e = GetClasslibException(exID, returnAddr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + throw e; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + [RuntimeExport("RhExceptionHandling_ThrowClasslibIndexOutOfRangeException")] + public static void ThrowClasslibIndexOutOfRangeException() + { + // Throw the index out of range exception defined by the classlib, using the return address from + // this helper to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.IndexOutOfRange; + + IntPtr returnAddr = BinderIntrinsics.GetReturnAddress(); + Exception e = GetClasslibException(exID, returnAddr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + throw e; + } + #endregion // ItWouldBeNiceToRemoveThese + + + // This function is used to throw exceptions out of our fast allocation helpers, implemented in asm. We tail-call + // from the allocation helpers to this function, which performs the throw. The tail-call is important: it ensures + // that the stack is crawlable from within this function. Throwing from a sub-function is important, since this + // is how we enforce our locality requirements and ensure that the method that invokes "new" is the method that + // catches the exception. + [MethodImpl(MethodImplOptions.NoInlining)] + [RuntimeExport("RhExceptionHandling_FailedAllocation")] + public static void FailedAllocation(bool fIsOverflow) + { + // Throw the out of memory or overflow exception defined by the classlib, using the return address from this helper + // to find the correct classlib. + + ExceptionIDs exID = fIsOverflow ? ExceptionIDs.Overflow : ExceptionIDs.OutOfMemory; + + IntPtr returnAddr = BinderIntrinsics.GetReturnAddress(); + Exception e = GetClasslibException(exID, returnAddr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + } + + private static OutOfMemoryException s_theOOMException = new OutOfMemoryException(); + + // Rtm exports GetRuntimeException for the few cases where we have a helper that throws an exception + // and may be called by either slr100 or other classlibs and that helper needs to throw an exception. + // There are only a few cases where this happens now (the fast allocation helpers), so we limit the + // exception types that Rtm will return. + [RuntimeExport("GetRuntimeException")] + public static Exception GetRuntimeException(ExceptionIDs id) + { + switch (id) + { + case ExceptionIDs.OutOfMemory: + // Throw a preallocated exception to avoid infinite recursion. + return s_theOOMException; + + case ExceptionIDs.Overflow: + try + { + return new OverflowException(); + } + catch (OutOfMemoryException) + { + throw; + } + + default: + Debug.Assert(false, "unexpected ExceptionID"); + FailFast(RhFailFastReason.InternalError, null); + return null; + } + } + +#if FEATURE_CLR_EH + private enum HwExceptionCode : uint + { + STATUS_REDHAWK_NULL_REFERENCE = 0x00000000u, + + STATUS_DATATYPE_MISALIGNMENT = 0x80000002u, + STATUS_ACCESS_VIOLATION = 0xC0000005u, + STATUS_INTEGER_DIVIDE_BY_ZERO = 0xC0000094u, + } + + [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__PAL_LIMITED_CONTEXT)] + public struct PAL_LIMITED_CONTEXT + { + [FieldOffset(AsmOffsets.OFFSETOF__PAL_LIMITED_CONTEXT__IP)] + internal IntPtr IP; + // the rest of the struct is left unspecified. + } + + internal struct StackRange + { + } + + // N.B. -- These values are burned into the throw helper assembly code and are also known the the + // StackFrameIterator code. + [Flags] + internal enum ExKind : byte + { + None = 0, + Throw = 1, + HardwareFault = 2, + // unused: 3 + KindMask = 3, + + RethrowFlag = 4, + Rethrow = 5, // RethrowFlag | Throw + RethrowFault = 6, // RethrowFlag | HardwareFault + } + + [StackOnly] + [StructLayout(LayoutKind.Explicit)] + public struct ExInfo + { + internal void Init(Exception exceptionObj) + { + // _pPrevExInfo -- set by asm helper + // _pExContext -- set by asm helper + // _passNumber -- set by asm helper + // _kind -- set by asm helper + // _idxCurClause -- set by asm helper + // _frameIter -- initialized explicitly during dispatch + + _exception = exceptionObj; + _notifyDebuggerSP = UIntPtr.Zero; + } + + internal void Init(Exception exceptionObj, ref ExInfo rethrownExInfo) + { + // _pPrevExInfo -- set by asm helper + // _pExContext -- set by asm helper + // _passNumber -- set by asm helper + // _idxCurClause -- set by asm helper + // _frameIter -- initialized explicitly during dispatch + + _exception = exceptionObj; + _kind = rethrownExInfo._kind | ExKind.RethrowFlag; + _notifyDebuggerSP = UIntPtr.Zero; + } + + internal Exception ThrownException + { + get + { + return _exception; + } + } + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pPrevExInfo)] + internal void* _pPrevExInfo; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_pExContext)] + internal PAL_LIMITED_CONTEXT* _pExContext; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_exception)] + private Exception _exception; // actual object reference, specially reported by GcScanRootsWorker + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_kind)] + internal ExKind _kind; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_passNumber)] + internal byte _passNumber; + + // BEWARE: This field is used by the stackwalker to know if the dispatch code has reached the + // point at which a handler is called. In other words, it serves as an "is a handler + // active" state where '_idxCurClause == MaxTryRegionIdx' means 'no'. + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_idxCurClause)] + internal uint _idxCurClause; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_frameIter)] + internal StackFrameIterator _frameIter; + + [FieldOffset(AsmOffsets.OFFSETOF__ExInfo__m_notifyDebuggerSP)] + volatile internal UIntPtr _notifyDebuggerSP; + } + + // + // Called by RhpThrowHwEx + // + [RuntimeExport("RhThrowHwEx")] + public static void RhThrowHwEx(uint exceptionCode, ref ExInfo exInfo) + { + // trigger a GC (only if gcstress) to ensure we can stackwalk at this point + Debug.TriggerGCForGCStress(); + + InternalCalls.RhpValidateExInfoStack(); + + IntPtr faultingCodeAddress = exInfo._pExContext->IP; + + ExceptionIDs exceptionId; + switch (exceptionCode) + { + case (uint)HwExceptionCode.STATUS_REDHAWK_NULL_REFERENCE: + exceptionId = ExceptionIDs.NullReference; + break; + + case (uint)HwExceptionCode.STATUS_DATATYPE_MISALIGNMENT: + exceptionId = ExceptionIDs.DataMisaligned; + break; + + // N.B. -- AVs that have a read/write address lower than 64k are already transformed to + // HwExceptionCode.REDHAWK_NULL_REFERENCE prior to calling this routine. + case (uint)HwExceptionCode.STATUS_ACCESS_VIOLATION: + exceptionId = ExceptionIDs.AccessViolation; + break; + + case (uint)HwExceptionCode.STATUS_INTEGER_DIVIDE_BY_ZERO: + exceptionId = ExceptionIDs.DivideByZero; + break; + + default: + // We don't wrap SEH exceptions from foreign code like CLR does, so we believe that we + // know the complete set of HW faults generated by managed code and do not need to handle + // this case. + FailFastViaClasslib(RhFailFastReason.InternalError, null, faultingCodeAddress); + exceptionId = ExceptionIDs.NullReference; + break; + } + + Exception exceptionToThrow = GetClasslibException(exceptionId, faultingCodeAddress); + + exInfo.Init(exceptionToThrow); + DispatchEx(ref exInfo._frameIter, ref exInfo, MaxTryRegionIdx); + BinderIntrinsics.DebugBreak(); + } + + private const uint MaxTryRegionIdx = 0xFFFFFFFFu; + + [RuntimeExport("RhThrowEx")] + public static void RhThrowEx(Exception exceptionObj, ref ExInfo exInfo) + { + // trigger a GC (only if gcstress) to ensure we can stackwalk at this point + Debug.TriggerGCForGCStress(); + + InternalCalls.RhpValidateExInfoStack(); + + // Transform attempted throws of null to a throw of NullReferenceException. + if (exceptionObj == null) + { + IntPtr faultingCodeAddress = exInfo._pExContext->IP; + exceptionObj = GetClasslibException(ExceptionIDs.NullReference, faultingCodeAddress); + } + + exInfo.Init(exceptionObj); + DispatchEx(ref exInfo._frameIter, ref exInfo, MaxTryRegionIdx); + BinderIntrinsics.DebugBreak(); + } + + [RuntimeExport("RhRethrow")] + public static void RhRethrow(ref ExInfo activeExInfo, ref ExInfo exInfo) + { + // trigger a GC (only if gcstress) to ensure we can stackwalk at this point + Debug.TriggerGCForGCStress(); + + InternalCalls.RhpValidateExInfoStack(); + + // We need to copy the Exception object to this stack location because collided unwinds will cause + // the original stack location to go dead. + Exception rethrownException = activeExInfo.ThrownException; + + exInfo.Init(rethrownException, ref activeExInfo); + DispatchEx(ref exInfo._frameIter, ref exInfo, activeExInfo._idxCurClause); + BinderIntrinsics.DebugBreak(); + } + + private static void DispatchEx(ref StackFrameIterator frameIter, ref ExInfo exInfo, uint startIdx) + { + Debug.Assert(exInfo._passNumber == 1, "expected asm throw routine to set the pass"); + Exception exceptionObj = exInfo.ThrownException; + + // ------------------------------------------------ + // + // First pass + // + // ------------------------------------------------ + UIntPtr handlingFrameSP = c_maxSP; + byte* pCatchHandler = null; + uint catchingTryRegionIdx = MaxTryRegionIdx; + + bool isFirstRethrowFrame = (startIdx != MaxTryRegionIdx); + bool isFirstFrame = true; + + UIntPtr prevFramePtr = UIntPtr.Zero; + bool unwoundReversePInvoke = false; + + bool isValid = frameIter.Init(exInfo._pExContext); + Debug.Assert(isValid, "RhThrowEx called with an unexpected context"); + DebuggerNotify.BeginFirstPass(exceptionObj, frameIter.ControlPC, frameIter.SP); + for (; isValid; isValid = frameIter.Next(out startIdx, out unwoundReversePInvoke)) + { + // For GC stackwalking, we'll happily walk across native code blocks, but for EH dispatch, we + // disallow dispatching exceptions across native code. + if (unwoundReversePInvoke) + break; + + DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); + + // A debugger can subscribe to get callbacks at a specific frame of exception dispatch + // exInfo._notifyDebuggerSP can be populated by the debugger from out of process + // at any time. + if (exInfo._notifyDebuggerSP == frameIter.SP) + DebuggerNotify.FirstPassFrameEntered(exceptionObj, frameIter.ControlPC, frameIter.SP); + + UpdateStackTrace(exceptionObj, ref exInfo, ref isFirstRethrowFrame, ref prevFramePtr, ref isFirstFrame); + + byte* pHandler; + if (FindFirstPassHandler(exceptionObj, startIdx, ref frameIter, + out catchingTryRegionIdx, out pHandler)) + { + handlingFrameSP = frameIter.SP; + pCatchHandler = pHandler; + + DebugVerifyHandlingFrame(handlingFrameSP); + break; + } + } + DebuggerNotify.EndFirstPass(exceptionObj, pCatchHandler, handlingFrameSP); + + if (pCatchHandler == null) + { + UnhandledExceptionFailFastViaClasslib( + RhFailFastReason.PN_UnhandledException, + exceptionObj, + ref exInfo); + } + + // We FailFast above if the exception goes unhandled. Therefore, we cannot run the second pass + // without a catch handler. + Debug.Assert(pCatchHandler != null, "We should have a handler if we're starting the second pass"); + + DebuggerNotify.BeginSecondPass(); + // ------------------------------------------------ + // + // Second pass + // + // ------------------------------------------------ + + // Due to the stackwalker logic, we cannot tolerate triggering a GC from the dispatch code once we + // are in the 2nd pass. This is because the stackwalker applies a particular unwind semantic to + // 'collapse' funclets which gets confused when we walk out of the dispatch code and encounter the + // 'main body' without first encountering the funclet. The thunks used to invoke 2nd-pass + // funclets will always toggle this mode off before invoking them. + InternalCalls.RhpSetThreadDoNotTriggerGC(); + + exInfo._passNumber = 2; + startIdx = MaxTryRegionIdx; + isValid = frameIter.Init(exInfo._pExContext); + for (; isValid && ((byte*)frameIter.SP <= (byte*)handlingFrameSP); isValid = frameIter.Next(out startIdx)) + { + Debug.Assert(isValid, "second-pass EH unwind failed unexpectedly"); + DebugScanCallFrame(exInfo._passNumber, frameIter.ControlPC, frameIter.SP); + + if (frameIter.SP == handlingFrameSP) + { + // invoke only a partial second-pass here... + InvokeSecondPass(ref exInfo, startIdx, catchingTryRegionIdx); + break; + } + + InvokeSecondPass(ref exInfo, startIdx); + } + + // ------------------------------------------------ + // + // Call the handler and resume execution + // + // ------------------------------------------------ + exInfo._idxCurClause = catchingTryRegionIdx; + InternalCalls.RhpCallCatchFunclet( + exceptionObj, pCatchHandler, frameIter.RegisterSet, ref exInfo); + // currently, RhpCallCatchFunclet will resume after the catch + Debug.Assert(false, "unreachable"); + BinderIntrinsics.DebugBreak(); + } + + [System.Diagnostics.Conditional("DEBUG")] + private static void DebugScanCallFrame(int passNumber, byte* ip, UIntPtr sp) + { + if (ip == null) { Debug.Assert(false, "false"); } + } + + [System.Diagnostics.Conditional("DEBUG")] + private static void DebugVerifyHandlingFrame(UIntPtr handlingFrameSP) + { + Debug.Assert(handlingFrameSP != c_maxSP, "Handling frame must have an SP value"); + Debug.Assert(((UIntPtr*)handlingFrameSP) > &handlingFrameSP, + "Handling frame must have a valid stack frame pointer"); + } + + private static void UpdateStackTrace(Exception exceptionObj, ref ExInfo exInfo, ref bool isFirstRethrowFrame, ref UIntPtr prevFramePtr, ref bool isFirstFrame) + { + // We use the fact that all funclet stack frames belonging to the same logical method activation + // will have the same FramePointer value. Additionally, the stackwalker will return a sequence of + // callbacks for all the funclet stack frames, one right after the other. The classlib doesn't + // want to know about funclets, so we strip them out by only reporting the first frame of a + // sequence of funclets. This is correct because the leafmost funclet is first in the sequence + // and corresponds to the current 'IP state' of the method. + UIntPtr curFramePtr = exInfo._frameIter.FramePointer; + if ((prevFramePtr == UIntPtr.Zero) || (curFramePtr != prevFramePtr)) + { + AppendExceptionStackFrameViaClasslib(exceptionObj, (IntPtr)exInfo._frameIter.ControlPC, + isFirstRethrowFrame, exInfo._pExContext->IP, isFirstFrame); + } + prevFramePtr = curFramePtr; + isFirstRethrowFrame = false; + isFirstFrame = false; + } + + private static bool FindFirstPassHandler(Exception exception, uint idxStart, + ref StackFrameIterator frameIter, + out uint tryRegionIdx, out byte* pHandler) + { + pHandler = null; + tryRegionIdx = MaxTryRegionIdx; + + EHEnum ehEnum; + byte* pbMethodStartAddress; + if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref frameIter, &pbMethodStartAddress, &ehEnum)) + return false; + + byte* pbControlPC = frameIter.ControlPC; + + uint codeOffset = (uint)(pbControlPC - pbMethodStartAddress); + + uint lastTryStart = 0, lastTryEnd = 0; + + // Search the clauses for one that contains the current offset. + RhEHClause ehClause; + for (uint curIdx = 0; InternalCalls.RhpEHEnumNext(&ehEnum, &ehClause); curIdx++) + { + // + // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where + // the previous dispatch left off. + // + if (idxStart != MaxTryRegionIdx) + { + if (curIdx <= idxStart) + { + lastTryStart = ehClause._tryStartOffset; lastTryEnd = ehClause._tryEndOffset; + continue; + } + + // Now, we continue skipping while the try region is identical to the one that invoked the + // previous dispatch. + if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) + continue; + } + + RhEHClauseKind clauseKind = ehClause._clauseKind; + + if (((clauseKind != RhEHClauseKind.RH_EH_CLAUSE_TYPED) && + (clauseKind != RhEHClauseKind.RH_EH_CLAUSE_FILTER)) + || !ehClause.ContainsCodeOffset(codeOffset)) + { + continue; + } + + // Found a containing clause. Because of the order of the clauses, we know this is the + // most containing. + if (clauseKind == RhEHClauseKind.RH_EH_CLAUSE_TYPED) + { + if (System.Runtime.TypeCast.IsInstanceOfClass(exception, ehClause._pTargetType) != null) + { + pHandler = (pbMethodStartAddress + ehClause._handlerOffset); + tryRegionIdx = curIdx; + return true; + } + } + else + { + byte* pFilterFunclet = (pbMethodStartAddress + ehClause._filterOffset); + bool shouldInvokeHandler = + InternalCalls.RhpCallFilterFunclet(exception, pFilterFunclet, frameIter.RegisterSet); + + if (shouldInvokeHandler) + { + pHandler = (pbMethodStartAddress + ehClause._handlerOffset); + tryRegionIdx = curIdx; + return true; + } + } + } + + return false; + } + + private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart) + { + InvokeSecondPass(ref exInfo, idxStart, MaxTryRegionIdx); + } + private static void InvokeSecondPass(ref ExInfo exInfo, uint idxStart, uint idxLimit) + { + EHEnum ehEnum; + byte* pbMethodStartAddress; + if (!InternalCalls.RhpEHEnumInitFromStackFrameIterator(ref exInfo._frameIter, &pbMethodStartAddress, &ehEnum)) + return; + + byte* pbControlPC = exInfo._frameIter.ControlPC; + + uint codeOffset = (uint)(pbControlPC - pbMethodStartAddress); + + uint lastTryStart = 0, lastTryEnd = 0; + + // Search the clauses for one that contains the current offset. + RhEHClause ehClause; + for (uint curIdx = 0; InternalCalls.RhpEHEnumNext(&ehEnum, &ehClause) && curIdx < idxLimit; curIdx++) + { + // + // Skip to the starting try region. This is used by collided unwinds and rethrows to pickup where + // the previous dispatch left off. + // + if (idxStart != MaxTryRegionIdx) + { + if (curIdx <= idxStart) + { + lastTryStart = ehClause._tryStartOffset; lastTryEnd = ehClause._tryEndOffset; + continue; + } + + // Now, we continue skipping while the try region is identical to the one that invoked the + // previous dispatch. + if ((ehClause._tryStartOffset == lastTryStart) && (ehClause._tryEndOffset == lastTryEnd)) + continue; + } + + RhEHClauseKind clauseKind = ehClause._clauseKind; + + if ((clauseKind != RhEHClauseKind.RH_EH_CLAUSE_FAULT) + || !ehClause.ContainsCodeOffset(codeOffset)) + { + continue; + } + + // Found a containing clause. Because of the order of the clauses, we know this is the + // most containing. + + // N.B. -- We need to suppress GC "in-between" calls to finallys in this loop because we do + // not have the correct next-execution point live on the stack and, therefore, may cause a GC + // hole if we allow a GC between invocation of finally funclets (i.e. after one has returned + // here to the dispatcher, but before the next one is invoked). Once they are running, it's + // fine for them to trigger a GC, obviously. + // + // As a result, RhpCallFinallyFunclet will set this state in the runtime upon return from the + // funclet, and we need to reset it if/when we fall out of the loop and we know that the + // method will no longer get any more GC callbacks. + + byte* pFinallyHandler = (pbMethodStartAddress + ehClause._handlerOffset); + exInfo._idxCurClause = curIdx; + InternalCalls.RhpCallFinallyFunclet(pFinallyHandler, exInfo._frameIter.RegisterSet); + exInfo._idxCurClause = MaxTryRegionIdx; + } + } + + [NativeCallable(EntryPoint = "RhpFailFastForPInvokeExceptionPreemp", CallingConvention = CallingConvention.FastCall)] + static public void RhpFailFastForPInvokeExceptionPreemp(IntPtr PInvokeCallsiteReturnAddr, void* pExceptionRecord, void* pContextRecord) + { + FailFastViaClasslib(RhFailFastReason.PN_UnhandledExceptionFromPInvoke, null, PInvokeCallsiteReturnAddr); + } + [RuntimeExport("RhpFailFastForPInvokeExceptionCoop")] + static public void RhpFailFastForPInvokeExceptionCoop(IntPtr PInvokeCallsiteReturnAddr, void* pExceptionRecord, void* pContextRecord) + { + FailFastViaClasslib(RhFailFastReason.PN_UnhandledExceptionFromPInvoke, null, PInvokeCallsiteReturnAddr); + } +#endif // FEATURE_CLR_EH + } // static class EH +} diff --git a/src/Runtime.Base/src/System/Runtime/ExceptionIDs.cs b/src/Runtime.Base/src/System/Runtime/ExceptionIDs.cs new file mode 100644 index 000000000..101afa0d7 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/ExceptionIDs.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime +{ + public enum ExceptionIDs + { + OutOfMemory = 1, + Arithmetic = 2, + ArrayTypeMismatch = 3, + DivideByZero = 4, + IndexOutOfRange = 5, + InvalidCast = 6, + Overflow = 7, + NullReference = 8, + AccessViolation = 9, + DataMisaligned = 10, + } +} diff --git a/src/Runtime.Base/src/System/Runtime/GCStress.cs b/src/Runtime.Base/src/System/Runtime/GCStress.cs new file mode 100644 index 000000000..a65d4fb1d --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/GCStress.cs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime +{ + public /*internal*/ class GCStress + { + [RuntimeExport("RhGcStress_Initialize")] + public /*internal*/ static void Initialize() + { +#if FEATURE_GC_STRESS + // This method is called via binder-injected code in a module's DllMain. The OS guarantees that + // only one thread at a time is in any DllMain, so we should be thread-safe as a result. + if (Initialized) + return; + + Initialized = true; + + Head = new GCStress(); + Tail = Head; + + int size = 10; + for (int i = 0; i < size; i++) + { + Tail.Next = new GCStress(); + Tail = Tail.Next; + } + + // drop the first element + Head = Head.Next; + + // notify redhawku.dll + InternalCalls.RhpInitializeGcStress(); +#endif // FEATURE_GC_STRESS + } + + ~GCStress() + { +#if FEATURE_GC_STRESS + // drop the first element + Head = Head.Next; + + // create and link a new element at the end of the list + Tail.Next = new GCStress(); + Tail = Tail.Next; +#endif // FEATURE_GC_STRESS + } + +#if FEATURE_GC_STRESS + static internal bool Initialized { get; private set; } + static private GCStress Head; + static private GCStress Tail; + + private GCStress Next; +#endif // FEATURE_GC_STRESS + + private GCStress() + { + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InternalCalls.cs b/src/Runtime.Base/src/System/Runtime/InternalCalls.cs new file mode 100644 index 000000000..c106935a2 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InternalCalls.cs @@ -0,0 +1,317 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// +// This is where we group together all the internal calls. +// + +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +namespace System.Runtime +{ + internal static class InternalCalls + { + // + // internalcalls for System.GC. + // + + // Force a garbage collection. + [RuntimeImport(Redhawk.BaseName, "RhCollect")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Always)] + internal static extern void RhCollect(int generation, InternalGCCollectionMode mode); + + // + // internalcalls for System.Runtime.__Finalizer. + // + + // Fetch next object which needs finalization or return null if we've reached the end of the list. + [RuntimeImport(Redhawk.BaseName, "RhpGetNextFinalizableObject")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal static extern Object RhpGetNextFinalizableObject(); + + // + // internalcalls for System.Runtime.InteropServices.GCHandle. + // + + // Allocate handle. + [RuntimeImport(Redhawk.BaseName, "RhpHandleAlloc")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal static extern IntPtr RhpHandleAlloc(Object value, GCHandleType type); + + // Allocate dependent handle. + [RuntimeImport(Redhawk.BaseName, "RhpHandleAllocDependent")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal static extern IntPtr RhpHandleAllocDependent(Object primary, Object secondary); + + // Allocate variable handle. + [RuntimeImport(Redhawk.BaseName, "RhpHandleAllocVariable")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal static extern IntPtr RhpHandleAllocVariable(Object value, uint type); + + // + // internal calls for allocation + // + [RuntimeImport(Redhawk.BaseName, "RhpNewFast")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Sometimes)] + internal unsafe extern static object RhpNewFast(EEType* pEEType); // BEWARE: not for finalizable objects! + + [RuntimeImport(Redhawk.BaseName, "RhpNewFinalizable")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Sometimes)] + internal unsafe extern static object RhpNewFinalizable(EEType* pEEType); + + [RuntimeImport(Redhawk.BaseName, "RhpNewArray")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Sometimes)] + internal unsafe extern static object RhpNewArray(EEType* pEEType, int length); + +#if FEATURE_64BIT_ALIGNMENT + [RuntimeImport(Redhawk.BaseName, "RhpNewFastAlign8")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Sometimes)] + internal unsafe extern static object RhpNewFastAlign8(EEType * pEEType); // BEWARE: not for finalizable objects! + + [RuntimeImport(Redhawk.BaseName, "RhpNewFinalizableAlign8")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Sometimes)] + internal unsafe extern static object RhpNewFinalizableAlign8(EEType* pEEType); + + [RuntimeImport(Redhawk.BaseName, "RhpNewArrayAlign8")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Sometimes)] + internal unsafe extern static object RhpNewArrayAlign8(EEType* pEEType, int length); + + [RuntimeImport(Redhawk.BaseName, "RhpNewFastMisalign")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Sometimes)] + internal unsafe extern static object RhpNewFastMisalign(EEType * pEEType); +#endif // FEATURE_64BIT_ALIGNMENT + + [RuntimeImport(Redhawk.BaseName, "RhpBox")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Sometimes)] + internal unsafe extern static void RhpBox(object obj, void* pData); // NOTE: returns null on allocation failure + + [RuntimeImport(Redhawk.BaseName, "RhUnbox")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Sometimes)] + internal unsafe extern static void RhUnbox(object obj, void* pData, EEType* pUnboxToEEType); + + [RuntimeImport(Redhawk.BaseName, "RhpCopyObjectContents")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static void RhpCopyObjectContents(object objDest, object objSrc); + +#if FEATURE_GC_STRESS + // + // internal calls for GC stress + // + [RuntimeImport(Redhawk.BaseName, "RhpInitializeGcStress")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static void RhpInitializeGcStress(); +#endif // FEATURE_GC_STRESS + + [RuntimeImport(Redhawk.BaseName, "RhpEHEnumInitFromStackFrameIterator")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static bool RhpEHEnumInitFromStackFrameIterator(ref StackFrameIterator pFrameIter, byte** pMethodStartAddress, void* pEHEnum); + + [RuntimeImport(Redhawk.BaseName, "RhpEHEnumNext")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static bool RhpEHEnumNext(void* pEHEnum, void* pEHClause); + + [RuntimeImport(Redhawk.BaseName, "RhpGetUnhijackedReturnAddress")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static void* RhpGetUnhijackedReturnAddress(void** ppvReturnAddressLocation); + + [RuntimeImport(Redhawk.BaseName, "RhpGetArrayBaseType")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static EEType* RhpGetArrayBaseType(EEType* pEEType); + + [RuntimeImport(Redhawk.BaseName, "RhpHasDispatchMap")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static bool RhpHasDispatchMap(EEType* pEETypen); + + [RuntimeImport(Redhawk.BaseName, "RhpGetDispatchMap")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static DispatchResolve.DispatchMap* RhpGetDispatchMap(EEType* pEEType); + + [RuntimeImport(Redhawk.BaseName, "RhpGetSealedVirtualSlot")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static IntPtr RhpGetSealedVirtualSlot(EEType* pEEType, ushort slot); + + [RuntimeImport(Redhawk.BaseName, "RhpGetDispatchCellInfo")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static void RhpGetDispatchCellInfo(IntPtr pCell, EEType** pInterfaceType, ushort* slot); + + [RuntimeImport(Redhawk.BaseName, "RhpSearchDispatchCellCache")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static IntPtr RhpSearchDispatchCellCache(IntPtr pCell, EEType* pInstanceType); + + [RuntimeImport(Redhawk.BaseName, "RhpUpdateDispatchCellCache")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static IntPtr RhpUpdateDispatchCellCache(IntPtr pCell, IntPtr pTargetCode, EEType* pInstanceType); + + [RuntimeImport(Redhawk.BaseName, "RhpGetClasslibFunction")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal unsafe extern static void* RhpGetClasslibFunction(IntPtr address, EH.ClassLibFunctionId id); + + // Given the EEType* for a generic type, retrieve instantiation information (generic type definition + // EEType, arity, type arguments and variance info for each type parameter). If the EEType is not + // generic, null will be returned. + [RuntimeImport(Redhawk.BaseName, "RhGetGenericInstantiation")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe EEType* RhGetGenericInstantiation(EEType* pEEType, + int* pArity, + EETypeRef** ppInstantiation, + GenericVariance** ppVarianceInfo); + +#if FEATURE_CLR_EH + // + // StackFrameIterator + // + + [RuntimeImport(Redhawk.BaseName, "RhpSfiInit")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal static extern unsafe bool RhpSfiInit(ref StackFrameIterator pThis, void* pStackwalkCtx); + + [RuntimeImport(Redhawk.BaseName, "RhpSfiNext")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal static extern bool RhpSfiNext(ref StackFrameIterator pThis, out uint uExCollideClauseIdx, out bool fUnwoundReversePInvoke); + + // + // DebugEventSource + // + + [RuntimeImport(Redhawk.BaseName, "RhpGetRequestedExceptionEvents")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal static extern ExceptionEventKind RhpGetRequestedExceptionEvents(); + + [DllImport(Redhawk.BaseName)] + internal static extern unsafe void RhpSendExceptionEventToDebugger(ExceptionEventKind eventKind, byte* ip, UIntPtr sp); +#endif // FEATURE_CLR_EH + + // + // Miscellaneous helpers. + // + + // Get the rarely used (optional) flags of an EEType. If they're not present 0 will be returned. + [RuntimeImport(Redhawk.BaseName, "RhpGetEETypeRareFlags")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe UInt32 RhpGetEETypeRareFlags(EEType* pEEType); + + // Retrieve the offset of the value embedded in a Nullable<T>. + [RuntimeImport(Redhawk.BaseName, "RhpGetNullableEETypeValueOffset")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe byte RhpGetNullableEETypeValueOffset(EEType* pEEType); + + // Retrieve the target type T in a Nullable<T>. + [RuntimeImport(Redhawk.BaseName, "RhpGetNullableEEType")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe EEType* RhpGetNullableEEType(EEType* pEEType); + + // For an ICastable type return a pointer to code that implements ICastable.IsInstanceOfInterface. + [RuntimeImport(Redhawk.BaseName, "RhpGetICastableIsInstanceOfInterfaceMethod")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe IntPtr RhpGetICastableIsInstanceOfInterfaceMethod(EEType* pEEType); + + // For an ICastable type return a pointer to code that implements ICastable.GetImplType. + [RuntimeImport(Redhawk.BaseName, "RhpGetICastableGetImplTypeMethod")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe IntPtr RhpGetICastableGetImplTypeMethod(EEType* pEEType); + + [RuntimeImport(Redhawk.BaseName, "RhpGetNextFinalizerInitCallback")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe IntPtr RhpGetNextFinalizerInitCallback(); + +#if FEATURE_CLR_EH + [RuntimeImport(Redhawk.BaseName, "RhpCallCatchFunclet")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe IntPtr RhpCallCatchFunclet( + object exceptionObj, byte* pHandlerIP, void* pvRegDisplay, ref EH.ExInfo exInfo); + + [RuntimeImport(Redhawk.BaseName, "RhpCallFinallyFunclet")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe void RhpCallFinallyFunclet(byte* pHandlerIP, void* pvRegDisplay); + + [RuntimeImport(Redhawk.BaseName, "RhpCallFilterFunclet")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe bool RhpCallFilterFunclet( + object exceptionObj, byte* pFilterIP, void* pvRegDisplay); + + [RuntimeImport(Redhawk.BaseName, "RhpSetThreadDoNotTriggerGC")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static void RhpSetThreadDoNotTriggerGC(); + + [System.Diagnostics.Conditional("DEBUG")] + [RuntimeImport(Redhawk.BaseName, "RhpValidateExInfoStack")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static void RhpValidateExInfoStack(); + + [RuntimeImport(Redhawk.BaseName, "RhpCopyContextFromExInfo")] + [MethodImpl(MethodImplOptions.InternalCall)] + [ManuallyManaged(GcPollPolicy.Never)] + internal extern static unsafe void RhpCopyContextFromExInfo(void* pOSContext, int cbOSContext, EH.PAL_LIMITED_CONTEXT* pPalContext); +#endif // FEATURE_CLR_EH + + + //------------------------------------------------------------------------------------------------------------ + // PInvoke-based internal calls + // + // These either do not need to be called in cooperative mode or, in some cases, MUST be called in preemptive + // mode. Note that they must use the Cdecl calling convention due to a limitation in our .obj file linking + // support. + //------------------------------------------------------------------------------------------------------------ + + // Block the current thread until at least one object needs to be finalized (returns true) or + // memory is low (returns false and the finalizer thread should initiate a garbage collection). + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern UInt32 RhpWaitForFinalizerRequest(); + + // Indicate that the current round of finalizations is complete. + [DllImport(Redhawk.BaseName, CallingConvention = CallingConvention.Cdecl)] + internal static extern void RhpSignalFinalizationComplete(); + } + + // Keep this synchronized with GenericVarianceType in rhbinder.h. + public enum GenericVariance : byte + { + NonVariant = 0, + Covariant = 1, + Contravariant = 2, + ArrayCovariant = 0x20, + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs new file mode 100644 index 000000000..87f10f222 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices +{ + // Used for the CallingConvention named argument to the DllImport and NativeCallable attribute + internal enum CallingConvention + { + Winapi = 1, + Cdecl = 2, + StdCall = 3, + ThisCall = 4, + FastCall = 5, + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs new file mode 100644 index 000000000..40df2c336 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices +{ + // Use this in P/Direct function prototypes to specify + // which character set to use when marshalling Strings. + // Using Ansi will marshal the strings as 1 byte char*'s. + // Using Unicode will marshal the strings as 2 byte wchar*'s. + // Generally you probably want to use Auto, which does the + // right thing 99% of the time. + + internal enum CharSet + { + None = 1, // User didn't specify how to marshal strings. + Ansi = 2, // Strings should be marshalled as ANSI 1 byte chars. + Unicode = 3, // Strings should be marshalled as Unicode 2 byte chars. + Auto = 4, // Marshal Strings in the right way for the target system. + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs new file mode 100644 index 000000000..039fbd05e --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Method)] + internal sealed class DllImportAttribute : Attribute + { + public CallingConvention CallingConvention; + + public DllImportAttribute(string dllName) + { + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs new file mode 100644 index 000000000..60fffbe39 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Field, Inherited = false)] + internal sealed class FieldOffsetAttribute : Attribute + { + public FieldOffsetAttribute(int offset) + { + } + public int Value { get { return 0; } } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/GCHandle.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/GCHandle.cs new file mode 100644 index 000000000..6f63c3079 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InteropServices/GCHandle.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace System.Runtime.InteropServices +{ + // These are the types of handles used by the EE. + // IMPORTANT: These must match the definitions in ObjectHandle.h in the EE. + public enum GCHandleType + { + Weak = 0, + WeakTrackResurrection = 1, + Normal = 2, + Pinned = 3 + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/LayoutKind.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/LayoutKind.cs new file mode 100644 index 000000000..97bbc307a --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InteropServices/LayoutKind.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices +{ + // Used in the StructLayoutAttribute class + internal enum LayoutKind + { + Sequential = 0, // 0x00000008, + Explicit = 2, // 0x00000010, + Auto = 3, // 0x00000000, + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/NativeCallableAttribute.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/NativeCallableAttribute.cs new file mode 100644 index 000000000..c5652029e --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InteropServices/NativeCallableAttribute.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Method)] + internal sealed class NativeCallableAttribute : Attribute + { +#pragma warning disable 649 // field never assigned to + // Optional. If omitted, then the method is native callable, but no EAT is emitted. + public string EntryPoint; + + // Optional. If omitted a default will be chosen by the compiler. + public CallingConvention CallingConvention; + + public NativeCallableAttribute() + { + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/OutAttribute.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/OutAttribute.cs new file mode 100644 index 000000000..b142aa058 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InteropServices/OutAttribute.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices +{ + // Not used in Redhawk. Only here as C# compiler requires it + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class OutAttribute : Attribute + { + public OutAttribute() + { + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/StructLayoutAttribute.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/StructLayoutAttribute.cs new file mode 100644 index 000000000..7ea2f12f3 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/InteropServices/StructLayoutAttribute.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime.InteropServices +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)] + internal sealed class StructLayoutAttribute : Attribute + { + public StructLayoutAttribute(LayoutKind layoutKind) + { + } + + // These fields are expected by C# compiler, + // so just disable the 'unused' warning +#pragma warning disable 649 + public LayoutKind Value; + public int Pack; + public int Size; + public CharSet CharSet; + } +} diff --git a/src/Runtime.Base/src/System/Runtime/RuntimeExportAttribute.cs b/src/Runtime.Base/src/System/Runtime/RuntimeExportAttribute.cs new file mode 100644 index 000000000..3ceec6547 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/RuntimeExportAttribute.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime +{ + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class RuntimeExportAttribute : Attribute + { + public string EntryPoint; + + public RuntimeExportAttribute(string entry) + { + EntryPoint = entry; + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/RuntimeExports.cs b/src/Runtime.Base/src/System/Runtime/RuntimeExports.cs new file mode 100644 index 000000000..9378e1c10 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/RuntimeExports.cs @@ -0,0 +1,547 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// +// This is where we group together all the runtime export calls. +// + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Runtime +{ + public static class RuntimeExports + { + // + // internalcalls for System.Runtime.InteropServices.GCHandle. + // + + // Allocate handle. + [RuntimeExport("RhHandleAlloc")] + public static IntPtr RhHandleAlloc(object value, GCHandleType type) + { + IntPtr h = InternalCalls.RhpHandleAlloc(value, type); + + if (h == IntPtr.Zero) + { + // Throw the out of memory exception defined by the classlib, using the return address of this method + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.OutOfMemory; + + IntPtr returnAddr = BinderIntrinsics.GetReturnAddress(); + Exception e = EH.GetClasslibException(exID, returnAddr); + throw e; + } + + return h; + } + + // Allocate dependent handle. + [RuntimeExport("RhHandleAllocDependent")] + public static IntPtr RhHandleAllocDependent(object primary, object secondary) + { + IntPtr h = InternalCalls.RhpHandleAllocDependent(primary, secondary); + + if (h == IntPtr.Zero) + { + // Throw the out of memory exception defined by the classlib, using the return address of this method + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.OutOfMemory; + + IntPtr returnAddr = BinderIntrinsics.GetReturnAddress(); + Exception e = EH.GetClasslibException(exID, returnAddr); + throw e; + } + + return h; + } + + // Allocate variable handle. + [RuntimeExport("RhHandleAllocVariable")] + public static IntPtr RhHandleAllocVariable(object value, uint type) + { + IntPtr h = InternalCalls.RhpHandleAllocVariable(value, type); + + if (h == IntPtr.Zero) + { + // Throw the out of memory exception defined by the classlib, using the return address of this method + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.OutOfMemory; + + IntPtr returnAddr = BinderIntrinsics.GetReturnAddress(); + Exception e = EH.GetClasslibException(exID, returnAddr); + throw e; + } + + return h; + } + + // + // internal calls for allocation + // + [RuntimeExport("RhNewObject")] + public unsafe static object RhNewObject(EETypePtr pEEType) + { + try + { + EEType* ptrEEType = (EEType*)pEEType.ToPointer(); +#if FEATURE_64BIT_ALIGNMENT + if (ptrEEType->RequiresAlign8) + { + if (ptrEEType->IsValueType) + return InternalCalls.RhpNewFastMisalign(ptrEEType); + if (ptrEEType->IsFinalizable) + return InternalCalls.RhpNewFinalizableAlign8(ptrEEType); + return InternalCalls.RhpNewFastAlign8(ptrEEType); + } + else +#endif // FEATURE_64BIT_ALIGNMENT + { + if (ptrEEType->IsFinalizable) + return InternalCalls.RhpNewFinalizable(ptrEEType); + return InternalCalls.RhpNewFast(ptrEEType); + } + } + catch (OutOfMemoryException) + { + // Throw the out of memory exception defined by the classlib, using the input EEType* + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.OutOfMemory; + + IntPtr addr = pEEType.ToPointer()->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + throw e; + } + } + + [RuntimeExport("RhNewArray")] + public unsafe static object RhNewArray(EETypePtr pEEType, int length) + { + EEType* ptrEEType = (EEType*)pEEType.ToPointer(); + try + { +#if FEATURE_64BIT_ALIGNMENT + if (ptrEEType->RequiresAlign8) + { + return InternalCalls.RhpNewArrayAlign8(ptrEEType, length); + } + else +#endif // FEATURE_64BIT_ALIGNMENT + { + return InternalCalls.RhpNewArray(ptrEEType, length); + } + } + catch (OutOfMemoryException) + { + // Throw the out of memory exception defined by the classlib, using the input EEType* + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.OutOfMemory; + + IntPtr addr = pEEType.ToPointer()->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + throw e; + } + catch (OverflowException) + { + // Throw the overflow exception defined by the classlib, using the input EEType* + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.Overflow; + + IntPtr addr = pEEType.ToPointer()->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + throw e; + } + } + + [RuntimeExport("RhBox")] + public unsafe static object RhBox(EETypePtr pEEType, void* pData) + { + EEType* ptrEEType = (EEType*)pEEType.ToPointer(); + object result; + + // If we're boxing a Nullable<T> then either box the underlying T or return null (if the + // nullable's value is empty). + if (ptrEEType->IsNullable) + { + // The boolean which indicates whether the value is null comes first in the Nullable struct. + if (!*(bool*)pData) + return null; + + // Switch type we're going to box to the Nullable<T> target type and advance the data pointer + // to the value embedded within the nullable. + pData = (byte*)pData + ptrEEType->GetNullableValueOffset(); + ptrEEType = ptrEEType->GetNullableType(); + } + +#if FEATURE_64BIT_ALIGNMENT + if (ptrEEType->RequiresAlign8) + { + result = InternalCalls.RhpNewFastMisalign(ptrEEType); + } + else +#endif // FEATURE_64BIT_ALIGNMENT + { + result = InternalCalls.RhpNewFast(ptrEEType); + } + if (result == null) + { + // Throw the out of memory exception defined by the classlib, using the input EEType* + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.OutOfMemory; + + IntPtr addr = pEEType.ToPointer()->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + throw e; + } + InternalCalls.RhpBox(result, pData); + return result; + } + + // this serves as a kind of union where: + // - the field o is used if the struct wraps a reference type + // - the field p is used together with pointer arithmetic if the struct is a valuetype + public struct Hack_o_p + { + internal Object o; + internal IntPtr p; + } + + [RuntimeExport("RhBoxAny")] + public unsafe static object RhBoxAny(ref Hack_o_p data, EETypePtr pEEType) + { + EEType* ptrEEType = (EEType*)pEEType.ToPointer(); + if (ptrEEType->IsValueType) + { + // HACK: we would really want to take the address of o here, + // but the rules of the C# language don't let us do that, + // so we arrive at the same result by taking the address of p + // and going back one pointer-sized unit + fixed (IntPtr* pData = &data.p) + return RhBox(pEEType, pData - 1); + } + else + return data.o; + } + + [RuntimeExport("RhUnboxAny")] + public unsafe static void RhUnboxAny(object o, ref Hack_o_p data, EETypePtr pUnboxToEEType) + { + EEType* ptrUnboxToEEType = (EEType*)pUnboxToEEType.ToPointer(); + if (ptrUnboxToEEType->IsValueType) + { + // HACK: we would really want to take the address of o here, + // but the rules of the C# language don't let us do that, + // so we arrive at the same result by taking the address of p + // and going back one pointer-sized unit + fixed (IntPtr* pData = &data.p) + { + bool isValid = false; + + if (ptrUnboxToEEType->IsNullable) + isValid = (o == null) || (o.EEType == ptrUnboxToEEType->GetNullableType()); + else + isValid = (o != null && o.EEType->CorElementType == ptrUnboxToEEType->CorElementType && TypeCast.IsInstanceOfClass(o, ptrUnboxToEEType) != null); + + if (!isValid) + { + // Throw the invalid cast exception defined by the classlib, using the input unbox EEType* + // to find the correct classlib. + + ExceptionIDs exID = o == null ? ExceptionIDs.NullReference : ExceptionIDs.InvalidCast; + + IntPtr addr = ptrUnboxToEEType->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + } + InternalCalls.RhUnbox(o, pData - 1, ptrUnboxToEEType); + } + } + else + data.o = o; + } + + [RuntimeExport("RhArrayStoreCheckAny")] + static public /*internal*/ unsafe void RhArrayStoreCheckAny(object array, ref Hack_o_p data) + { + if (array == null) + { + return; + } + + Debug.Assert(array.EEType->IsArray, "first argument must be an array"); + + EEType* arrayElemType = array.EEType->RelatedParameterType; + if (arrayElemType->IsValueType) + { + return; + } + + TypeCast.CheckArrayStore(array, data.o); + } + + [RuntimeExport("RhBoxAndNullCheck")] + static public /*internal*/ unsafe bool RhBoxAndNullCheck(ref Hack_o_p data, EETypePtr pEEType) + { + EEType* ptrEEType = (EEType*)pEEType.ToPointer(); + if (ptrEEType->IsValueType) + return true; + else + return data.o != null; + } + +#pragma warning disable 169 // The field 'System.Runtime.RuntimeExports.Wrapper.o' is never used. + private class Wrapper + { + private Object _o; + } +#pragma warning restore 169 + + [RuntimeExport("RhAllocLocal")] + public unsafe static object RhAllocLocal(EETypePtr pEEType) + { + EEType* ptrEEType = (EEType*)pEEType.ToPointer(); + if (ptrEEType->IsValueType) + return RhNewObject(pEEType); + else + return new Wrapper(); + } + + [RuntimeExport("RhpReversePInvokeBadTransition")] + public static void RhpReversePInvokeBadTransition() + { + IntPtr returnAddress = BinderIntrinsics.GetReturnAddress(); + if (returnAddress != IntPtr.Zero) + { + EH.FailFastViaClasslib( + RhFailFastReason.IllegalNativeCallableEntry, + null, + returnAddress); + } + else + { + // @HACKHACK: we need to force the method to have an EBP frame so that we can use the + // GetReturnAddress() intrinsic above. This seems to be the smallest way to do this. + EH.FailFast(RhFailFastReason.InternalError, null); + throw EH.GetClasslibException(ExceptionIDs.Arithmetic, returnAddress); + } + } + + [RuntimeExport("RhMemberwiseClone")] + public static object RhMemberwiseClone(object src) + { + return src.MemberwiseClone(); + } + + // EEType interrogation methods. + + [RuntimeExport("RhGetRelatedParameterType")] + public static unsafe EETypePtr RhGetRelatedParameterType(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return new EETypePtr((IntPtr)pEEType->RelatedParameterType); + } + + [RuntimeExport("RhGetNonArrayBaseType")] + public static unsafe EETypePtr RhGetNonArrayBaseType(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return new EETypePtr((IntPtr)pEEType->NonArrayBaseType); + } + + [RuntimeExport("RhGetComponentSize")] + public static unsafe ushort RhGetComponentSize(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return pEEType->ComponentSize; + } + + [RuntimeExport("RhGetNumInterfaces")] + public static unsafe uint RhGetNumInterfaces(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return (uint)pEEType->NumInterfaces; + } + + [RuntimeExport("RhGetInterface")] + public static unsafe EETypePtr RhGetInterface(EETypePtr ptrEEType, uint index) + { + EEType* pEEType = ptrEEType.ToPointer(); + + // The convoluted pointer arithmetic into the interface map below (rather than a simply array + // dereference) is because C# will generate a 64-bit multiply for the lookup by default. This + // causes us a problem on x86 because it uses a helper that's mapped directly into the CRT via + // import magic and that technique doesn't work with the way we link this code into the runtime + // image. Since we don't need a 64-bit multiply here (the classlib is trusted code) we manually + // perform the calculation. + EEInterfaceInfo* pInfo = (EEInterfaceInfo*)((byte*)pEEType->InterfaceMap + (index * (uint)sizeof(EEInterfaceInfo))); + + return new EETypePtr((IntPtr)pInfo->InterfaceType); + } + + [RuntimeExport("RhSetInterface")] + public static unsafe void RhSetInterface(EETypePtr ptrEEType, int index, EETypePtr ptrInterfaceEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + EEType* pInterfaceEEType = ptrInterfaceEEType.ToPointer(); + pEEType->InterfaceMap[index].InterfaceType = pInterfaceEEType; + } + + /// FUNCTION IS OBSOLETE AND NOT EXPECTED TO BE USED IN NEW CODE + [RuntimeExport("RhSetNonArrayBaseType")] + public static unsafe void RhSetNonArrayBaseType(EETypePtr ptrEEType, EETypePtr ptrBaseEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + EEType* pBaseEEType = ptrBaseEEType.ToPointer(); + pEEType->BaseType = pBaseEEType; + } + + [RuntimeExport("RhIsDynamicType")] + public static unsafe bool RhIsDynamicType(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return pEEType->IsDynamicType; + } + + [RuntimeExport("RhHasCctor")] + public static unsafe bool RhHasCctor(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return pEEType->HasCctor; + } + + [RuntimeExport("RhIsValueType")] + public static unsafe bool RhIsValueType(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return pEEType->IsValueType; + } + + [RuntimeExport("RhIsInterface")] + public static unsafe bool RhIsInterface(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return pEEType->IsInterface; + } + + [RuntimeExport("RhIsArray")] + public static unsafe bool RhIsArray(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return pEEType->IsArray; + } + + [RuntimeExport("RhIsString")] + public static unsafe bool RhIsString(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + // String is currently the only non-array type with a non-zero component size. + return (pEEType->ComponentSize == sizeof(char)) && !pEEType->IsArray; + } + + [RuntimeExport("RhIsNullable")] + public static unsafe bool RhIsNullable(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return pEEType->IsNullable; + } + + [RuntimeExport("RhGetNullableType")] + public static unsafe EETypePtr RhGetNullableType(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return new EETypePtr((IntPtr)pEEType->GetNullableType()); + } + + [RuntimeExport("RhHasReferenceFields")] + public static unsafe bool RhHasReferenceFields(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return pEEType->HasReferenceFields; + } + + [RuntimeExport("RhGetCorElementType")] + public static unsafe byte RhGetCorElementType(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return (byte)pEEType->CorElementType; + } + + public enum RhEETypeClassification + { + Regular, // Object, String, Int32 + Array, // String[] + Generic, // List<Int32> + GenericTypeDefinition, // List<T> + UnmanagedPointer, // void* + } + + [RuntimeExport("RhGetEETypeClassification")] + public static unsafe RhEETypeClassification RhGetEETypeClassification(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + + if (pEEType->IsArray) + return RhEETypeClassification.Array; + + if (pEEType->IsGeneric) + return RhEETypeClassification.Generic; + + if (pEEType->IsGenericTypeDefinition) + return RhEETypeClassification.GenericTypeDefinition; + + if (pEEType->IsPointerTypeDefinition) + return RhEETypeClassification.UnmanagedPointer; + + return RhEETypeClassification.Regular; + } + + [RuntimeExport("RhGetEETypeHash")] + public static unsafe uint RhGetEETypeHash(EETypePtr ptrEEType) + { + EEType* pEEType = ptrEEType.ToPointer(); + return pEEType->HashCode; + } + + // Worker for RhGetCurrentThreadStackTrace. RhGetCurrentThreadStackTrace just allocates a transition + // frame that will be used to seed the stack trace and this method does all the real work. + // + // Input: outputBuffer may be null or non-null + // Return value: positive: number of entries written to outputBuffer + // negative: number of required entries in outputBuffer in case it's too small (or null) + // Output: outputBuffer is filled in with return address IPs, starting with placing the this + // method's return address into index 0 + // + // NOTE: We don't want to allocate the array on behalf of the caller because we don't know which class + // library's objects the caller understands (we support multiple class libraries with multiple root + // System.Object types). + [RuntimeExport("RhpCalculateStackTraceWorker")] + public static unsafe int RhpCalculateStackTraceWorker(IntPtr[] outputBuffer) + { + int nFrames = 0; + bool success = (outputBuffer != null); + + StackFrameIterator frameIter = new StackFrameIterator(); + bool isValid = frameIter.Init(null); + for (; isValid; isValid = frameIter.Next()) + { + if (outputBuffer != null) + { + if (nFrames < outputBuffer.Length) + outputBuffer[nFrames] = new IntPtr(frameIter.ControlPC); + else + success = false; + } + nFrames++; + } + return success ? nFrames : -nFrames; + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/RuntimeImportAttribute.cs b/src/Runtime.Base/src/System/Runtime/RuntimeImportAttribute.cs new file mode 100644 index 000000000..5ee507013 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/RuntimeImportAttribute.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System.Runtime +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false)] + internal sealed class RuntimeImportAttribute : Attribute + { + public string DllName; + public string EntryPoint; + + public RuntimeImportAttribute(string entry) + { + EntryPoint = entry; + } + + public RuntimeImportAttribute(string dllName, string entry) + { + EntryPoint = entry; + DllName = dllName; + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/StackFrameIterator.cs b/src/Runtime.Base/src/System/Runtime/StackFrameIterator.cs new file mode 100644 index 000000000..d535e0618 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/StackFrameIterator.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#define FEATURE_CLR_EH +using System.Runtime.InteropServices; + +#if FEATURE_CLR_EH + +namespace System.Runtime +{ + [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__REGDISPLAY)] + internal unsafe struct REGDISPLAY + { + [FieldOffset(AsmOffsets.OFFSETOF__REGDISPLAY__SP)] + internal UIntPtr SP; + } + + [StructLayout(LayoutKind.Explicit, Size = AsmOffsets.SIZEOF__StackFrameIterator)] + internal unsafe struct StackFrameIterator + { + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_FramePointer)] + private UIntPtr _framePointer; + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_ControlPC)] + private IntPtr _controlPC; + [FieldOffset(AsmOffsets.OFFSETOF__StackFrameIterator__m_RegDisplay)] + private REGDISPLAY _regDisplay; + + internal byte* ControlPC { get { return (byte*)_controlPC; } } + internal void* RegisterSet { get { fixed (void* pRegDisplay = &_regDisplay) { return pRegDisplay; } } } + internal UIntPtr SP { get { return _regDisplay.SP; } } + internal UIntPtr FramePointer { get { return _framePointer; } } + + internal bool Init(EH.PAL_LIMITED_CONTEXT* pStackwalkCtx) + { + return InternalCalls.RhpSfiInit(ref this, pStackwalkCtx); + } + + internal bool Next() + { + uint uExCollideClauseIdx; + bool fUnwoundReversePInvoke; + return Next(out uExCollideClauseIdx, out fUnwoundReversePInvoke); + } + + internal bool Next(out uint uExCollideClauseIdx) + { + bool fUnwoundReversePInvoke; + return Next(out uExCollideClauseIdx, out fUnwoundReversePInvoke); + } + + internal bool Next(out uint uExCollideClauseIdx, out bool fUnwoundReversePInvoke) + { + return InternalCalls.RhpSfiNext(ref this, out uExCollideClauseIdx, out fUnwoundReversePInvoke); + } + } +} + +#endif // FEATURE_CLR_EH diff --git a/src/Runtime.Base/src/System/Runtime/TypeCast.cs b/src/Runtime.Base/src/System/Runtime/TypeCast.cs new file mode 100644 index 000000000..c2bc25ef7 --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/TypeCast.cs @@ -0,0 +1,934 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics; + +namespace System.Runtime +{ + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // + // **** WARNING **** + // + // A large portion of the logic present in this file is duplicated + // in src\System.Private.Reflection.Execution\Internal\Reflection\Execution\TypeLoader\TypeCast.cs + // (for dynamic type builder). If you make changes here make sure they are reflected there. + // + // **** WARNING **** + // + ///////////////////////////////////////////////////////////////////////////////////////////////////// + + public /*internal*/ static class TypeCast + { + [RuntimeExport("RhTypeCast_IsInstanceOfClass")] + static public /*internal*/ unsafe object IsInstanceOfClass(object obj, void* pvTargetType) + { + if (obj == null) + { + return null; + } + + EEType* pTargetType = (EEType*)pvTargetType; + EEType* pObjType = obj.EEType; + + Debug.Assert(!pTargetType->IsParameterizedType, "IsInstanceOfClass called with parameterized EEType"); + Debug.Assert(!pTargetType->IsInterface, "IsInstanceOfClass called with interface EEType"); + + // if the EETypes pointers match, we're done + if (pObjType == pTargetType) + { + return obj; + } + + // Quick check if both types are good for simple casting: canonical, no related type via IAT, no generic variance + if (System.Runtime.EEType.BothSimpleCasting(pObjType, pTargetType)) + { + // walk the type hierarchy looking for a match + do + { + pObjType = pObjType->BaseType; + + if (pObjType == null) + { + return null; + } + + if (pObjType == pTargetType) + { + return obj; + } + } + while (pObjType->SimpleCasting()); + } + + if (pTargetType->IsCloned) + { + pTargetType = pTargetType->CanonicalEEType; + } + + if (pObjType->IsCloned) + { + pObjType = pObjType->CanonicalEEType; + } + + // if the EETypes pointers match, we're done + if (pObjType == pTargetType) + { + return obj; + } + + if (pTargetType->HasGenericVariance && pObjType->HasGenericVariance) + { + // Only generic interfaces and delegates can have generic variance and we shouldn't see + // interfaces for either input here. So if the canonical types are marked as having variance + // we know we've hit the delegate case. We've dealt with the identical case just above. And + // the regular path below will handle casting to Object, Delegate and MulticastDelegate. Since + // we don't support deriving from user delegate classes any further all we have to check here + // is that the uninstantiated generic delegate definitions are the same and the type + // parameters are compatible. + return TypesAreCompatibleViaGenericVariance(pObjType, pTargetType) ? obj : null; + } + + if (pObjType->IsArray) + { + // arrays can be cast to System.Object + if (WellKnownEETypes.IsSystemObject(pTargetType)) + { + return obj; + } + + // arrays can be cast to System.Array + if (WellKnownEETypes.IsSystemArray(pTargetType)) + { + return obj; + } + + return null; + } + + + // walk the type hierarchy looking for a match + while (true) + { + pObjType = pObjType->NonClonedNonArrayBaseType; + if (pObjType == null) + { + return null; + } + + if (pObjType->IsCloned) + pObjType = pObjType->CanonicalEEType; + + if (pObjType == pTargetType) + { + return obj; + } + } + } + + [RuntimeExport("RhTypeCast_CheckCastClass")] + static public /*internal*/ unsafe object CheckCastClass(Object obj, void* pvTargetEEType) + { + // a null value can be cast to anything + if (obj == null) + return null; + + object result = IsInstanceOfClass(obj, pvTargetEEType); + + if (result == null) + { + // Throw the invalid cast exception defined by the classlib, using the input EEType* + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.InvalidCast; + + IntPtr addr = ((EEType*)pvTargetEEType)->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + } + + return result; + } + + [RuntimeExport("RhTypeCast_CheckUnbox")] + static public /*internal*/ unsafe void CheckUnbox(Object obj, byte expectedCorElementType) + { + if (obj == null) + { + return; + } + + if (obj.EEType->CorElementType == (CorElementType)expectedCorElementType) + return; + + // Throw the invalid cast exception defined by the classlib, using the input object's EEType* + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.InvalidCast; + + IntPtr addr = obj.EEType->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + } + + [RuntimeExport("RhTypeCast_IsInstanceOfArray")] + static public unsafe object IsInstanceOfArray(object obj, void* pvTargetType) + { + if (obj == null) + { + return null; + } + + EEType* pTargetType = (EEType*)pvTargetType; + EEType* pObjType = obj.EEType; + + Debug.Assert(pTargetType->IsArray, "IsInstanceOfArray called with non-array EEType"); + Debug.Assert(!pTargetType->IsCloned, "cloned array types are disallowed"); + + // if the types match, we are done + if (pObjType == pTargetType) + { + return obj; + } + + // if the object is not an array, we're done + if (!pObjType->IsArray) + { + return null; + } + + Debug.Assert(!pObjType->IsCloned, "cloned array types are disallowed"); + + // compare the array types structurally + + if (AreTypesAssignableInternal(pObjType->RelatedParameterType, pTargetType->RelatedParameterType, false, true)) + return obj; + + return null; + } + + [RuntimeExport("RhTypeCast_CheckCastArray")] + static public /*internal*/ unsafe object CheckCastArray(Object obj, void* pvTargetEEType) + { + // a null value can be cast to anything + if (obj == null) + return null; + + object result = IsInstanceOfArray(obj, pvTargetEEType); + + if (result == null) + { + // Throw the invalid cast exception defined by the classlib, using the input EEType* + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.InvalidCast; + + IntPtr addr = ((EEType*)pvTargetEEType)->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + } + + return result; + } + + [RuntimeExport("RhTypeCast_IsInstanceOfInterface")] + static public unsafe object IsInstanceOfInterface(object obj, void* pvTargetType) + { + if (obj == null) + { + return null; + } + + EEType* pTargetType = (EEType*)pvTargetType; + EEType* pObjType = obj.EEType; + + if (ImplementsInterface(pObjType, pTargetType)) + return obj; + + // If object type implements ICastable then there's one more way to check whether it implements + // the interface. + if (pObjType->IsICastable) + { + // Call the ICastable.IsInstanceOfInterface method directly rather than via an interface + // dispatch since we know the method address statically. We ignore any cast error exception + // object passed back on failure (result == false) since IsInstanceOfInterface never throws. + IntPtr pfnIsInstanceOfInterface = pObjType->ICastableIsInstanceOfInterfaceMethod; + Exception castError = null; + if (CalliIntrinsics.Call<bool>(pfnIsInstanceOfInterface, obj, pTargetType, out castError)) + return obj; + } + + return null; + } + + static internal unsafe bool ImplementsInterface(EEType* pObjType, EEType* pTargetType) + { + Debug.Assert(!pTargetType->IsParameterizedType, "did not expect paramterized type"); + Debug.Assert(pTargetType->IsInterface, "IsInstanceOfInterface called with non-interface EEType"); + + // This can happen with generic interface types + // Debug.Assert(!pTargetType->IsCloned, "cloned interface types are disallowed"); + + // canonicalize target type + if (pTargetType->IsCloned) + pTargetType = pTargetType->CanonicalEEType; + + int numInterfaces = pObjType->NumInterfaces; + EEInterfaceInfo* interfaceMap = pObjType->InterfaceMap; + for (int i = 0; i < numInterfaces; i++) + { + EEType* pInterfaceType = interfaceMap[i].InterfaceType; + + // canonicalize the interface type + if (pInterfaceType->IsCloned) + pInterfaceType = pInterfaceType->CanonicalEEType; + + if (pInterfaceType == pTargetType) + { + return true; + } + } + + // We did not find the interface type in the list of supported interfaces. There's still one + // chance left: if the target interface is generic and one or more of its type parameters is co or + // contra variant then the object can still match if it implements a different instantiation of + // the interface with type compatible generic arguments. + // + // An additional edge case occurs because of array covariance. This forces us to treat any generic + // interfaces implemented by arrays as covariant over their one type parameter. + bool fArrayCovariance = pObjType->IsArray; + if (pTargetType->HasGenericVariance || (fArrayCovariance && pTargetType->IsGeneric)) + { + // Grab details about the instantiation of the target generic interface. + EETypeRef* pTargetInstantiation; + int targetArity; + GenericVariance* pTargetVarianceInfo; + EEType* pTargetGenericType = InternalCalls.RhGetGenericInstantiation(pTargetType, + &targetArity, + &pTargetInstantiation, + &pTargetVarianceInfo); + + Debug.Assert(pTargetVarianceInfo != null, "did not expect empty variance info"); + + + for (int i = 0; i < numInterfaces; i++) + { + EEType* pInterfaceType = interfaceMap[i].InterfaceType; + + // We can ignore interfaces which are not also marked as having generic variance + // unless we're dealing with array covariance. + if (pInterfaceType->HasGenericVariance || (fArrayCovariance && pInterfaceType->IsGeneric)) + { + // Grab instantiation details for the candidate interface. + EETypeRef* pInterfaceInstantiation; + int interfaceArity; + GenericVariance* pInterfaceVarianceInfo; + EEType* pInterfaceGenericType = InternalCalls.RhGetGenericInstantiation(pInterfaceType, + &interfaceArity, + &pInterfaceInstantiation, + &pInterfaceVarianceInfo); + + Debug.Assert(pInterfaceVarianceInfo != null, "did not expect empty variance info"); + + // If the generic types aren't the same then the types aren't compatible. + if (pInterfaceGenericType != pTargetGenericType) + continue; + + // The types represent different instantiations of the same generic type. The + // arity of both had better be the same. + Debug.Assert(targetArity == interfaceArity, "arity mismatch betweeen generic instantiations"); + + // Compare the instantiations to see if they're compatible taking variance into account. + if (TypeParametersAreCompatible(targetArity, + pInterfaceInstantiation, + pTargetInstantiation, + pTargetVarianceInfo, + fArrayCovariance)) + return true; + } + } + } + + return false; + } + + // Compare two types to see if they are compatible via generic variance. + static private unsafe bool TypesAreCompatibleViaGenericVariance(EEType* pSourceType, EEType* pTargetType) + { + // Get generic instantiation metadata for both types. + + EETypeRef* pTargetInstantiation; + int targetArity; + GenericVariance* pTargetVarianceInfo; + EEType* pTargetGenericType = InternalCalls.RhGetGenericInstantiation(pTargetType, + &targetArity, + &pTargetInstantiation, + &pTargetVarianceInfo); + Debug.Assert(pTargetVarianceInfo != null, "did not expect empty variance info"); + + EETypeRef* pSourceInstantiation; + int sourceArity; + GenericVariance* pSourceVarianceInfo; + EEType* pSourceGenericType = InternalCalls.RhGetGenericInstantiation(pSourceType, + &sourceArity, + &pSourceInstantiation, + &pSourceVarianceInfo); + Debug.Assert(pSourceVarianceInfo != null, "did not expect empty variance info"); + + // If the generic types aren't the same then the types aren't compatible. + if (pSourceGenericType == pTargetGenericType) + { + // The types represent different instantiations of the same generic type. The + // arity of both had better be the same. + Debug.Assert(targetArity == sourceArity, "arity mismatch betweeen generic instantiations"); + + // Compare the instantiations to see if they're compatible taking variance into account. + if (TypeParametersAreCompatible(targetArity, + pSourceInstantiation, + pTargetInstantiation, + pTargetVarianceInfo, + false)) + { + return true; + } + } + + return false; + } + + // Compare two sets of generic type parameters to see if they're assignment compatible taking generic + // variance into account. It's assumed they've already had their type definition matched (which + // implies their arities are the same as well). The fForceCovariance argument tells the method to + // override the defined variance of each parameter and instead assume it is covariant. This is used to + // implement covariant array interfaces. + static internal unsafe bool TypeParametersAreCompatible(int arity, + EETypeRef* pSourceInstantiation, + EETypeRef* pTargetInstantiation, + GenericVariance* pVarianceInfo, + bool fForceCovariance) + { + // Walk through the instantiations comparing the cast compatibility of each pair + // of type args. + for (int i = 0; i < arity; i++) + { + EEType* pTargetArgType = pTargetInstantiation[i].Value; + EEType* pSourceArgType = pSourceInstantiation[i].Value; + + GenericVariance varType; + if (fForceCovariance) + varType = GenericVariance.ArrayCovariant; + else + varType = pVarianceInfo[i]; + + switch (varType) + { + case GenericVariance.NonVariant: + // Non-variant type params need to be identical. + + if (!AreTypesEquivalentInternal(pSourceArgType, pTargetArgType)) + return false; + + break; + + case GenericVariance.Covariant: + // For covariance (or out type params in C#) the object must implement an + // interface with a more derived type arg than the target interface. Or + // the object interface can have a type arg that is an interface + // implemented by the target type arg. + // For instance: + // class Foo : ICovariant<String> is ICovariant<Object> + // class Foo : ICovariant<Bar> is ICovariant<IBar> + // class Foo : ICovariant<IBar> is ICovariant<Object> + + if (!AreTypesAssignableInternal(pSourceArgType, pTargetArgType, false, false)) + return false; + + break; + + case GenericVariance.ArrayCovariant: + // For array covariance the object must be an array with a type arg + // that is more derived than that the target interface, or be a primitive + // (or enum) with the same size. + // For instance: + // string[,,] is object[,,] + // int[,,] is uint[,,] + + // This call is just like the call for Covariance above except true is passed + // to the fAllowSizeEquivalence parameter to allow the int/uint matching to work + if (!AreTypesAssignableInternal(pSourceArgType, pTargetArgType, false, true)) + return false; + + break; + + case GenericVariance.Contravariant: + // For contravariance (or in type params in C#) the object must implement + // an interface with a less derived type arg than the target interface. Or + // the object interface can have a type arg that is a class implementing + // the interface that is the target type arg. + // For instance: + // class Foo : IContravariant<Object> is IContravariant<String> + // class Foo : IContravariant<IBar> is IContravariant<Bar> + // class Foo : IContravariant<Object> is IContravariant<IBar> + + if (!AreTypesAssignableInternal(pTargetArgType, pSourceArgType, false, false)) + return false; + + break; + + default: + Debug.Assert(false, "unknown generic variance type"); + break; + } + } + + return true; + } + + // + // Determines if a value of the source type can be assigned to a location of the target type. + // It does not handle ICastable, and cannot since we do not have an actual object instance here. + // This routine assumes that the source type is boxed, i.e. a value type source is presumed to be + // compatible with Object and ValueType and an enum source is additionally compatible with Enum. + // + [RuntimeExport("RhTypeCast_AreTypesAssignable")] + static public unsafe bool AreTypesAssignable(void* pvSourceType, void* pvTargetType) + { + EEType* pSourceType = (EEType*)pvSourceType; + EEType* pTargetType = (EEType*)pvTargetType; + + // Special case: Generic Type definitions are not assignable in a mrt sense + // in any way. Assignability of those types is handled by reflection logic. + // Call this case out first and here so that these only somewhat filled in + // types do not leak into the rest of the type casting logic. + if (pTargetType->IsGenericTypeDefinition || pSourceType->IsGenericTypeDefinition) + { + return false; + } + + // Special case: T can be cast to Nullable<T> (where T is a value type). Call this case out here + // since this is only applicable if T is boxed, which is not true for any other callers of + // AreTypesAssignableInternal, so no sense making all the other paths pay the cost of the check. + if (pTargetType->IsNullable && pSourceType->IsValueType && !pSourceType->IsNullable) + { + EEType* pNullableType = pTargetType->GetNullableType(); + + return AreTypesEquivalentInternal(pSourceType, pNullableType); + } + + return AreTypesAssignableInternal(pSourceType, pTargetType, true, false); + } + + // Internally callable version of the export method above. Has two additional parameters: + // fBoxedSource : assume the source type is boxed so that value types and enums are + // compatible with Object, ValueType and Enum (if applicable) + // fAllowSizeEquivalence : allow identically sized integral types and enums to be considered + // equivalent (currently used only for array element types) + static internal unsafe bool AreTypesAssignableInternal(EEType* pSourceType, EEType* pTargetType, bool fBoxedSource, bool fAllowSizeEquivalence) + { + // + // Are the types identical? + // + if (AreTypesEquivalentInternal(pSourceType, pTargetType)) + return true; + + // + // Handle cast to interface cases. + // + if (pTargetType->IsInterface) + { + // Value types can only be cast to interfaces if they're boxed. + if (!fBoxedSource && pSourceType->IsValueType) + return false; + + if (ImplementsInterface(pSourceType, pTargetType)) + return true; + + // Are the types compatible due to generic variance? + if (pTargetType->HasGenericVariance && pSourceType->HasGenericVariance) + return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType); + + return false; + } + if (pSourceType->IsInterface) + { + // The only non-interface type an interface can be cast to is Object. + return WellKnownEETypes.IsSystemObject(pTargetType); + } + + // + // Handle cast to array or pointer cases. + // + if (pTargetType->IsParameterizedType) + { + if (pSourceType->IsParameterizedType && (pTargetType->ParameterizedTypeShape == pSourceType->ParameterizedTypeShape)) + { + // Source type is also a parameterized type. Are the parameter types compatible? Note that using + // AreTypesAssignableInternal here handles array covariance as well as IFoo[] -> Foo[] + // etc. Pass false for fBoxedSource since int[] is not assignable to object[]. + if (pSourceType->RelatedParameterType->IsPointerTypeDefinition) + { + // If the parameter types are pointers, then only exact matches are correct. + // As we've already called AreTypesEquivalent at the start of this function, + // return false as the exact match case has already been handled. + // int** is not compatible with uint**, nor is int*[] oompatible with uint*[]. + return false; + } + else + { + return AreTypesAssignableInternal(pSourceType->RelatedParameterType, pTargetType->RelatedParameterType, false, true); + } + } + + // Can't cast a non-parameter type to a parameter type or a parameter type of different shape to a parameter type + return false; + } + if (pSourceType->IsArray) + { + // Target type is not an array. But we can still cast arrays to Object or System.Array. + return WellKnownEETypes.IsSystemObject(pTargetType) || WellKnownEETypes.IsSystemArray(pTargetType); + } + else if (pSourceType->IsParameterizedType) + { + return false; + } + + // + // Handle cast to other (non-interface, non-array) cases. + // + + if (pSourceType->IsValueType) + { + // Certain value types of the same size are treated as equivalent when the comparison is + // between array element types (indicated by fAllowSizeEquivalence). These are integer types + // of the same size (e.g. int and uint) and the base type of enums vs all integer types of the + // same size. + if (fAllowSizeEquivalence && pTargetType->IsValueType) + { + if (ArePrimitveTypesEquivalentSize(pSourceType, pTargetType)) + return true; + + // Non-identical value types aren't equivalent in any other case (since value types are + // sealed). + return false; + } + + // If the source type is a value type but it's not boxed then we've run out of options: the types + // are not identical, the target type isn't an interface and we're not allowed to check whether + // the target type is a parent of this one since value types are sealed and thus the only matches + // would be against Object, ValueType or Enum, all of which are reference types and not compatible + // with non-boxed value types. + if (!fBoxedSource) + return false; + } + + // Sub case of casting between two instantiations of the same delegate type where one or more of + // the type parameters have variance. Only interfaces and delegate types can have variance over + // their type parameters and we know that neither type is an interface due to checks above. + if (pTargetType->HasGenericVariance && pSourceType->HasGenericVariance) + { + // We've dealt with the identical case at the start of this method. And the regular path below + // will handle casting to Object, Delegate and MulticastDelegate. Since we don't support + // deriving from user delegate classes any further all we have to check here is that the + // uninstantiated generic delegate definitions are the same and the type parameters are + // compatible. + return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType); + } + + // Is the source type derived from the target type? + if (IsDerived(pSourceType, pTargetType)) + return true; + + return false; + } + + [RuntimeExport("RhTypeCast_CheckCastInterface")] + static public /*internal*/ unsafe object CheckCastInterface(Object obj, void* pvTargetEEType) + { + // a null value can be cast to anything + if (obj == null) + { + return null; + } + + EEType* pTargetType = (EEType*)pvTargetEEType; + EEType* pObjType = obj.EEType; + + if (ImplementsInterface(pObjType, pTargetType)) + return obj; + + Exception castError = null; + + // If object type implements ICastable then there's one more way to check whether it implements + // the interface. + if (pObjType->IsICastable) + { + // Call the ICastable.IsInstanceOfInterface method directly rather than via an interface + // dispatch since we know the method address statically. + IntPtr pfnIsInstanceOfInterface = pObjType->ICastableIsInstanceOfInterfaceMethod; + if (CalliIntrinsics.Call<bool>(pfnIsInstanceOfInterface, obj, pTargetType, out castError)) + return obj; + } + + // Throw the invalid cast exception defined by the classlib, using the input EEType* to find the + // correct classlib unless ICastable.IsInstanceOfInterface returned a more specific exception for + // us to use. + + IntPtr addr = ((EEType*)pvTargetEEType)->GetAssociatedModuleAddress(); + if (castError == null) + castError = EH.GetClasslibException(ExceptionIDs.InvalidCast, addr); + + BinderIntrinsics.TailCall_RhpThrowEx(castError); + throw castError; + } + + [RuntimeExport("RhTypeCast_CheckArrayStore")] + static public /*internal*/ unsafe void CheckArrayStore(object array, object obj) + { + if (array == null || obj == null) + { + return; + } + + Debug.Assert(array.EEType->IsArray, "first argument must be an array"); + + EEType* arrayElemType = array.EEType->RelatedParameterType; + bool compatible; + if (arrayElemType->IsInterface) + { + compatible = IsInstanceOfInterface(obj, arrayElemType) != null; + } + else if (arrayElemType->IsArray) + { + compatible = IsInstanceOfArray(obj, arrayElemType) != null; + } + else + { + compatible = IsInstanceOfClass(obj, arrayElemType) != null; + } + + if (!compatible) + { + // Throw the array type mismatch exception defined by the classlib, using the input array's EEType* + // to find the correct classlib. + + ExceptionIDs exID = ExceptionIDs.ArrayTypeMismatch; + + IntPtr addr = array.EEType->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + } + } + + + [RuntimeExport("RhTypeCast_CheckVectorElemAddr")] + static public /*internal*/ unsafe void CheckVectorElemAddr(void* pvElemType, object array) + { + if (array == null) + { + return; + } + + Debug.Assert(array.EEType->IsArray, "second argument must be an array"); + + EEType* elemType = (EEType*)pvElemType; + EEType* arrayElemType = array.EEType->RelatedParameterType; + + if (!AreTypesEquivalentInternal(elemType, arrayElemType)) + { + // Throw the array type mismatch exception defined by the classlib, using the input array's EEType* + // to find the correct classlib. + ExceptionIDs exID = ExceptionIDs.ArrayTypeMismatch; + + IntPtr addr = array.EEType->GetAssociatedModuleAddress(); + Exception e = EH.GetClasslibException(exID, addr); + + BinderIntrinsics.TailCall_RhpThrowEx(e); + } + } + + + static internal unsafe bool IsDerived(EEType* pDerivedType, EEType* pBaseType) + { + Debug.Assert(!pDerivedType->IsArray, "did not expect array type"); + Debug.Assert(!pDerivedType->IsParameterizedType, "did not expect parameterType"); + Debug.Assert(!pBaseType->IsArray, "did not expect array type"); + Debug.Assert(!pBaseType->IsInterface, "did not expect interface type"); + Debug.Assert(!pBaseType->IsParameterizedType, "did not expect parameterType"); + Debug.Assert(pBaseType->IsCanonical || pBaseType->IsCloned || pBaseType->IsGenericTypeDefinition, "unexpected eetype"); + Debug.Assert(pDerivedType->IsCanonical || pDerivedType->IsCloned || pDerivedType->IsGenericTypeDefinition, "unexpected eetype"); + + // If a generic type definition reaches this function, then the function should return false unless the types are equivalent. + // This works as the NonClonedNonArrayBaseType of a GenericTypeDefinition is always null. + + if (pBaseType->IsCloned) + pBaseType = pBaseType->CanonicalEEType; + + do + { + if (pDerivedType->IsCloned) + pDerivedType = pDerivedType->CanonicalEEType; + + if (pDerivedType == pBaseType) + return true; + + pDerivedType = pDerivedType->NonClonedNonArrayBaseType; + } + while (pDerivedType != null); + + return false; + } + + + [RuntimeExport("RhTypeCast_AreTypesEquivalent")] + static unsafe public bool AreTypesEquivalent(EETypePtr pType1, EETypePtr pType2) + { + return (AreTypesEquivalentInternal(pType1.ToPointer(), pType2.ToPointer())); + } + + // Method to compare two types pointers for type equality + // We cannot just compare the pointers as there can be duplicate type instances + // for cloned and constructed types. + // There are three separate cases here + // 1. The pointers are Equal => true + // 2. Either one or both the types are CLONED, follow to the canonical EEType and check + // 3. For Arrays/Pointers, we have to further check for rank and element type equality + static private unsafe bool AreTypesEquivalentInternal(EEType* pType1, EEType* pType2) + { + if (pType1 == pType2) + return true; + + if (pType1->IsCloned) + pType1 = pType1->CanonicalEEType; + + if (pType2->IsCloned) + pType2 = pType2->CanonicalEEType; + + if (pType1 == pType2) + return true; + + if (pType1->IsParameterizedType && pType2->IsParameterizedType) + return AreTypesEquivalentInternal(pType1->RelatedParameterType, pType2->RelatedParameterType) && pType1->ParameterizedTypeShape == pType2->ParameterizedTypeShape; + + return false; + } + +#if FEATURE_SHARED_GENERICS + // this is necessary for shared generic code - Foo<T> may be executing + // for T being an interface, an array or a class + [RuntimeExport("RhTypeCast_IsInstanceOf")] + static public /*internal*/ unsafe object IsInstanceOf(object obj, void* pvTargetType) + { + EEType* pTargetType = (EEType*)pvTargetType; + if (pTargetType->IsArray) + return IsInstanceOfArray(obj, pvTargetType); + else if (pTargetType->IsInterface) + return IsInstanceOfInterface(obj, pvTargetType); + else + return IsInstanceOfClass(obj, pvTargetType); + } + + [RuntimeExport("RhTypeCast_CheckCast")] + static public /*internal*/ unsafe object CheckCast(Object obj, void* pvTargetType) + { + EEType* pTargetType = (EEType*)pvTargetType; + if (pTargetType->IsArray) + return CheckCastArray(obj, pvTargetType); + else if (pTargetType->IsInterface) + return CheckCastInterface(obj, pvTargetType); + else + return CheckCastClass(obj, pvTargetType); + } +#endif + + // Returns true of the two types are equivalent primitive types. Used by array casts. + static private unsafe bool ArePrimitveTypesEquivalentSize(EEType* pType1, EEType* pType2) + { + CorElementType sourceCorType = pType1->CorElementType; + int sourcePrimitiveTypeEquivalenceSize = s_CorElementTypeIntegralSizeCompareArray[(int)sourceCorType]; + + // Quick check to see if the first type is even primitive. + if (sourcePrimitiveTypeEquivalenceSize == 0) + return false; + CorElementType targetCorType = pType2->CorElementType; + int targetPrimitiveTypeEquivalenceSize = s_CorElementTypeIntegralSizeCompareArray[(int)targetCorType]; + + return sourcePrimitiveTypeEquivalenceSize == targetPrimitiveTypeEquivalenceSize; + } + + private unsafe static int[] ComputeCorElementTypeIntegralSizeMatchArray() + { + int[] result = new int[(int)CorElementType.ELEMENT_TYPE_MAX]; + result[(int)CorElementType.ELEMENT_TYPE_I1] = 1; + result[(int)CorElementType.ELEMENT_TYPE_U1] = 1; + result[(int)CorElementType.ELEMENT_TYPE_I2] = 2; + result[(int)CorElementType.ELEMENT_TYPE_U2] = 2; + result[(int)CorElementType.ELEMENT_TYPE_I4] = 4; + result[(int)CorElementType.ELEMENT_TYPE_U4] = 4; + result[(int)CorElementType.ELEMENT_TYPE_I8] = 8; + result[(int)CorElementType.ELEMENT_TYPE_U8] = 8; + result[(int)CorElementType.ELEMENT_TYPE_I] = sizeof(IntPtr); + result[(int)CorElementType.ELEMENT_TYPE_U] = sizeof(IntPtr); + return result; + } + + private static int[] s_CorElementTypeIntegralSizeCompareArray = ComputeCorElementTypeIntegralSizeMatchArray(); + + // copied from CorHdr.h + internal enum CorElementType : byte + { + ELEMENT_TYPE_END = 0x0, + ELEMENT_TYPE_VOID = 0x1, + ELEMENT_TYPE_BOOLEAN = 0x2, + ELEMENT_TYPE_CHAR = 0x3, + ELEMENT_TYPE_I1 = 0x4, + ELEMENT_TYPE_U1 = 0x5, + ELEMENT_TYPE_I2 = 0x6, + ELEMENT_TYPE_U2 = 0x7, + ELEMENT_TYPE_I4 = 0x8, + ELEMENT_TYPE_U4 = 0x9, + ELEMENT_TYPE_I8 = 0xa, + ELEMENT_TYPE_U8 = 0xb, + ELEMENT_TYPE_R4 = 0xc, + ELEMENT_TYPE_R8 = 0xd, + ELEMENT_TYPE_STRING = 0xe, + + // every type above PTR will be simple type + ELEMENT_TYPE_PTR = 0xf, // PTR <type> + ELEMENT_TYPE_BYREF = 0x10, // BYREF <type> + + // Please use ELEMENT_TYPE_VALUETYPE. ELEMENT_TYPE_VALUECLASS is deprecated. + ELEMENT_TYPE_VALUETYPE = 0x11, // VALUETYPE <class Token> + ELEMENT_TYPE_CLASS = 0x12, // CLASS <class Token> + ELEMENT_TYPE_VAR = 0x13, // a class type variable VAR <U1> + ELEMENT_TYPE_ARRAY = 0x14, // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ... + ELEMENT_TYPE_GENERICINST = 0x15, // GENERICINST <generic type> <argCnt> <arg1> ... <argn> + ELEMENT_TYPE_TYPEDBYREF = 0x16, // TYPEDREF (it takes no args) a typed referece to some other type + + ELEMENT_TYPE_I = 0x18, // native integer size + ELEMENT_TYPE_U = 0x19, // native unsigned integer size + ELEMENT_TYPE_FNPTR = 0x1B, // FNPTR <complete sig for the function including calling convention> + ELEMENT_TYPE_OBJECT = 0x1C, // Shortcut for System.Object + ELEMENT_TYPE_SZARRAY = 0x1D, // Shortcut for single dimension zero lower bound array + // SZARRAY <type> + ELEMENT_TYPE_MVAR = 0x1e, // a method type variable MVAR <U1> + + // This is only for binding + ELEMENT_TYPE_CMOD_REQD = 0x1F, // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef> + ELEMENT_TYPE_CMOD_OPT = 0x20, // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef> + + // This is for signatures generated internally (which will not be persisted in any way). + ELEMENT_TYPE_INTERNAL = 0x21, // INTERNAL <typehandle> + + // Note that this is the max of base type excluding modifiers + ELEMENT_TYPE_MAX = 0x22, // first invalid element type + + + ELEMENT_TYPE_MODIFIER = 0x40, + ELEMENT_TYPE_SENTINEL = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs + ELEMENT_TYPE_PINNED = 0x05 | ELEMENT_TYPE_MODIFIER, + } + } +} diff --git a/src/Runtime.Base/src/System/Runtime/__Finalizer.cs b/src/Runtime.Base/src/System/Runtime/__Finalizer.cs new file mode 100644 index 000000000..4ae10a72a --- /dev/null +++ b/src/Runtime.Base/src/System/Runtime/__Finalizer.cs @@ -0,0 +1,84 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.InteropServices; +// +// Implements the single finalizer thread for a Redhawk instance. Essentially waits for an event to fire +// indicating finalization is necessary then drains the queue of pending finalizable objects, calling the +// finalize method for each one. +// + +namespace System.Runtime +{ + // We choose this name to avoid clashing with any future public class with the name Finalizer. + public static class __Finalizer + { + private static bool s_fHaveNewClasslibs = false; + + [NativeCallable(EntryPoint = "ProcessFinalizers", CallingConvention = CallingConvention.Cdecl)] + public static void ProcessFinalizers() + { + while (true) + { + // Wait until there's some work to be done. If true is returned we should finalize objects, + // otherwise memory is low and we should initiate a collection. + if (InternalCalls.RhpWaitForFinalizerRequest() != 0) + { + if (s_fHaveNewClasslibs) + { + s_fHaveNewClasslibs = false; + MakeFinalizerInitCallbacks(); + } + + // Drain the queue of finalizable objects. + Object target = InternalCalls.RhpGetNextFinalizableObject(); + while (target != null) + { + // Call the finalizer on the current target object. If the finalizer throws we'll fail + // fast via normal Redhawk exception semantics (since we don't attempt to catch + // anything). + unsafe + { + CalliIntrinsics.CallVoid(target.EEType->FinalizerCode, target); + } + + target = InternalCalls.RhpGetNextFinalizableObject(); + } + + // Tell anybody that's interested that the finalization pass is complete (there is a race condition here + // where we might immediately signal a new request as complete, but this is acceptable). + InternalCalls.RhpSignalFinalizationComplete(); + } + else + { + // RhpWaitForFinalizerRequest() returned false and indicated that memory is low. We help + // out by initiating a garbage collection and then go back to waiting for another request. + InternalCalls.RhCollect(-1, InternalGCCollectionMode.Blocking); + } + } + } + + // Each class library can sign up for a callback to run code on the finalizer thread before any + // objects derived from that class library's System.Object are finalized. This is where we make those + // callbacks. When a class library is loaded, we set the s_fHasNewClasslibs flag and then the next + // time the finalizer runs, we call this function to make any outstanding callbacks. + private unsafe static void MakeFinalizerInitCallbacks() + { + while (true) + { + IntPtr pfnFinalizerInitCallback = InternalCalls.RhpGetNextFinalizerInitCallback(); + if (pfnFinalizerInitCallback == IntPtr.Zero) + break; + + CalliIntrinsics.CallVoid(pfnFinalizerInitCallback); + } + } + + [RuntimeExport("RhpSetHaveNewClasslibs")] + public static void RhpSetHaveNewClasslibs() + { + s_fHaveNewClasslibs = true; + } + } +} diff --git a/src/Runtime.Base/src/System/RuntimeHandles.cs b/src/Runtime.Base/src/System/RuntimeHandles.cs new file mode 100644 index 000000000..deaf0b40b --- /dev/null +++ b/src/Runtime.Base/src/System/RuntimeHandles.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// +// The following type is present only because the C# compiler assumes this type always exists +// + +namespace System +{ + // This type will be used only for static fields. It will simply be the address of the static field in + // the loaded PE image. + internal unsafe struct RuntimeFieldHandle + { +#pragma warning disable 649 // Field 'blah' is never assigned to, and will always have its default value + internal byte* m_pbStaticFieldData; +#pragma warning restore + } +} diff --git a/src/Runtime.Base/src/System/RuntimeTypeHandle.cs b/src/Runtime.Base/src/System/RuntimeTypeHandle.cs new file mode 100644 index 000000000..f8dd20f85 --- /dev/null +++ b/src/Runtime.Base/src/System/RuntimeTypeHandle.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// System.Type and System.RuntimeTypeHandle are defined here as the C# compiler requires them +// In the redhawk runtime these are not used. In the class library there is an implementation that support typeof + +using System; +using System.Runtime.InteropServices; + +namespace System +{ + internal class Type + { + public RuntimeTypeHandle TypeHandle { get { return default(RuntimeTypeHandle); } } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RuntimeTypeHandle + { + private EETypePtr _pEEType; + } +} diff --git a/src/Runtime.Base/src/System/String.cs b/src/Runtime.Base/src/System/String.cs new file mode 100644 index 000000000..61492eec0 --- /dev/null +++ b/src/Runtime.Base/src/System/String.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Runtime.InteropServices; + +namespace System +{ + // CONTRACT with Runtime + // The String type is one of the primitives understood by the compilers and runtime + // Data Contract: One int field, m_stringLength, the number of 2-byte wide chars that are valid + + // STRING LAYOUT + // ------------- + // Strings are null-terminated for easy interop with native, but the value returned by String.Length + // does NOT include this null character in its count. As a result, there's some trickiness here in the + // layout and allocation of strings that needs explanation... + // + // String is allocated like any other array, using the RhNewArray API. It is essentially a very special + // char[] object. In order to be an array, the String EEType must have an 'array element size' of 2, + // which is setup by a special case in the binder. Strings must also have a typical array instance + // layout, which means that the first field after the m_pEEType field is the 'number of array elements' + // field. However, here, it is called m_stringLength because it contains the number of characters in the + // string (NOT including the terminating null element) and, thus, directly represents both the array + // length and String.Length. + // + // As with all arrays, the GC calculates the size of an object using the following formula: + // + // obj_size = align(base_size + (num_elements * element_size), sizeof(void*)) + // + // The values 'base_size' and 'element_size' are both stored in the EEType for String and 'num_elements' + // is m_stringLength. + // + // Our base_size is the size of the fixed portion of the string defined below. It, therefore, contains + // the size of the m_firstChar field in it. This means that, since our string data actually starts + // inside the fixed 'base_size' area, and our num_elements is equal to String.Length, we end up with one + // extra character at the end. This is how we get our extra null terminator which allows us to pass a + // pinned string out to native code as a null-terminated string. This is also why we don't increment the + // requested string length by one before passing it to RhNewArray. There is no need to allocate an extra + // array element, it is already allocated here in the fixed layout of the String. + // + // Typically, the base_size of an array type is aligned up to the nearest pointer size multiple (so that + // array elements start out aligned in case they need alignment themselves), but we don't want to do that + // with String because we are allocating String.Length components with RhNewArray and the overall object + // size will then need another alignment, resulting in wasted space. So the binder specially shrinks the + // base_size of String, leaving it unaligned in order to allow the use of that otherwise wasted space. + // + // One more note on base_size -- on 64-bit, the base_size ends up being 22 bytes, which is less than the + // min_obj_size of (3 * sizeof(void*)). This is OK because our array allocator will still align up the + // overall object size, so a 0-length string will end up with an object size of 24 bytes, which meets the + // min_obj_size requirement. + // + + // This type does not override GetHashCode, Equals +#pragma warning disable 0661, 0660 + [StructLayout(LayoutKind.Sequential)] + public class String + { +#if BIT64 + private const int POINTER_SIZE = 8; +#else + private const int POINTER_SIZE = 4; +#endif + // m_pEEType + m_stringLength + internal const int FIRST_CHAR_OFFSET = POINTER_SIZE + sizeof(int); + + // CS0169: The private field '{blah}' is never used + // CS0649: Field '{blah}' is never assigned to, and will always have its default value +#pragma warning disable 169, 649 + private int _stringLength; + private char _firstChar; + +#pragma warning restore + + internal int Length + { + get + { + return _stringLength; + } + } + } +} diff --git a/src/Runtime.Base/src/System/Void.cs b/src/Runtime.Base/src/System/Void.cs new file mode 100644 index 000000000..b0e3c87b1 --- /dev/null +++ b/src/Runtime.Base/src/System/Void.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace System +{ + // This class represents the void return type + // For RH, this type wont be available at runtime + // typeof(void) would fail. + public struct Void + { + } +} |