Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-11-09 23:23:18 +0300
committerJan Kotas <jkotas@microsoft.com>2015-11-09 23:23:18 +0300
commit44376bbb262a53a7481b02144e56ea9b9581ec67 (patch)
tree7303b2f16e442a6f45135b04c4af028f31eb99d8 /src/Runtime.Base
parent137a6cf6dd75d53ca8a45c44d05d2e16c820ed15 (diff)
Initial population of Runtime.Base
Diffstat (limited to 'src/Runtime.Base')
-rw-r--r--src/Runtime.Base/src/System/Array.cs35
-rw-r--r--src/Runtime.Base/src/System/Attribute.cs11
-rw-r--r--src/Runtime.Base/src/System/AttributeTargets.cs31
-rw-r--r--src/Runtime.Base/src/System/AttributeUsageAttribute.cs45
-rw-r--r--src/Runtime.Base/src/System/Delegate.cs13
-rw-r--r--src/Runtime.Base/src/System/Diagnostics/ConditionalAttribute.cs24
-rw-r--r--src/Runtime.Base/src/System/Diagnostics/Debug.cs31
-rw-r--r--src/Runtime.Base/src/System/Exception.cs89
-rw-r--r--src/Runtime.Base/src/System/FlagsAttribute.cs17
-rw-r--r--src/Runtime.Base/src/System/GC.cs29
-rw-r--r--src/Runtime.Base/src/System/MulticastDelegate.cs11
-rw-r--r--src/Runtime.Base/src/System/Nullable.cs15
-rw-r--r--src/Runtime.Base/src/System/Object.cs102
-rw-r--r--src/Runtime.Base/src/System/ParamArrayAttribute.cs15
-rw-r--r--src/Runtime.Base/src/System/Primitives.cs410
-rw-r--r--src/Runtime.Base/src/System/Runtime/BinderIntrinsics.cs32
-rw-r--r--src/Runtime.Base/src/System/Runtime/CachedInterfaceDispatch.cs105
-rw-r--r--src/Runtime.Base/src/System/Runtime/CalliIntrinsics.cs32
-rw-r--r--src/Runtime.Base/src/System/Runtime/CompilerServices/FixedBufferAttribute.cs12
-rw-r--r--src/Runtime.Base/src/System/Runtime/CompilerServices/ICastable.cs60
-rw-r--r--src/Runtime.Base/src/System/Runtime/CompilerServices/IntrinsicAttribute.cs10
-rw-r--r--src/Runtime.Base/src/System/Runtime/CompilerServices/IsVolatile.cs10
-rw-r--r--src/Runtime.Base/src/System/Runtime/CompilerServices/ManuallyManagedAttribute.cs24
-rw-r--r--src/Runtime.Base/src/System/Runtime/CompilerServices/MethodImplAttribute.cs40
-rw-r--r--src/Runtime.Base/src/System/Runtime/CompilerServices/RuntimeHelpers.cs22
-rw-r--r--src/Runtime.Base/src/System/Runtime/CompilerServices/StackOnlyAttribute.cs10
-rw-r--r--src/Runtime.Base/src/System/Runtime/CompilerServices/UnsafeValueTypeAttribute.cs7
-rw-r--r--src/Runtime.Base/src/System/Runtime/DispatchResolve.cs229
-rw-r--r--src/Runtime.Base/src/System/Runtime/EEType.cs732
-rw-r--r--src/Runtime.Base/src/System/Runtime/EETypePtr.cs43
-rw-r--r--src/Runtime.Base/src/System/Runtime/ExceptionHandling.cs958
-rw-r--r--src/Runtime.Base/src/System/Runtime/ExceptionIDs.cs19
-rw-r--r--src/Runtime.Base/src/System/Runtime/GCStress.cs61
-rw-r--r--src/Runtime.Base/src/System/Runtime/InternalCalls.cs317
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/CallingConvention.cs15
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs20
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs15
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/FieldOffsetAttribute.cs14
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/GCHandle.cs17
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/LayoutKind.cs13
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/NativeCallableAttribute.cs20
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/OutAttribute.cs14
-rw-r--r--src/Runtime.Base/src/System/Runtime/InteropServices/StructLayoutAttribute.cs21
-rw-r--r--src/Runtime.Base/src/System/Runtime/RuntimeExportAttribute.cs16
-rw-r--r--src/Runtime.Base/src/System/Runtime/RuntimeExports.cs547
-rw-r--r--src/Runtime.Base/src/System/Runtime/RuntimeImportAttribute.cs23
-rw-r--r--src/Runtime.Base/src/System/Runtime/StackFrameIterator.cs58
-rw-r--r--src/Runtime.Base/src/System/Runtime/TypeCast.cs934
-rw-r--r--src/Runtime.Base/src/System/Runtime/__Finalizer.cs84
-rw-r--r--src/Runtime.Base/src/System/RuntimeHandles.cs18
-rw-r--r--src/Runtime.Base/src/System/RuntimeTypeHandle.cs22
-rw-r--r--src/Runtime.Base/src/System/String.cs82
-rw-r--r--src/Runtime.Base/src/System/Void.cs12
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
+ {
+ }
+}