Skip to content

Instantly share code, notes, and snippets.

@mattwarren
Last active February 2, 2022 00:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mattwarren/a248782078d15c4ca2999f986ba7eacb to your computer and use it in GitHub Desktop.
Save mattwarren/a248782078d15c4ca2999f986ba7eacb to your computer and use it in GitHub Desktop.
diff --git a/src/Runtime.Base/src/System/Exception.cs b/src/Runtime.Base/src/System/Exception.cs
index cda417df7..3cf83434b 100644
--- a/src/Runtime.Base/src/System/Exception.cs
+++ b/src/Runtime.Base/src/System/Exception.cs
@@ -87,4 +87,25 @@ namespace System
{
public OutOfMemoryException() { }
}
+
+ internal class ObjectDisposedException : Exception
+ {
+ public ObjectDisposedException() { }
+
+ public ObjectDisposedException(string msg) { }
+ }
+
+ internal class DllNotFoundException : Exception
+ {
+ public DllNotFoundException() { }
+
+ public DllNotFoundException(string msg) { }
+ }
+
+ internal class EntryPointNotFoundException : Exception
+ {
+ public EntryPointNotFoundException() : base() { }
+
+ public EntryPointNotFoundException(string msg) : base(msg) { }
+ }
}
diff --git a/src/Runtime.Base/src/System/Runtime/GCStress.cs b/src/Runtime.Base/src/System/Runtime/GCStress.cs
index f4b64ed21..ffc0b7875 100644
--- a/src/Runtime.Base/src/System/Runtime/GCStress.cs
+++ b/src/Runtime.Base/src/System/Runtime/GCStress.cs
@@ -36,7 +36,7 @@ namespace System.Runtime
Head = Head.Next;
// notify redhawku.dll
- InternalCalls.RhpInitializeGcStress();
+ //InternalCalls.RhpInitializeGcStress();
#endif // FEATURE_GC_STRESS
}
diff --git a/src/Runtime.Base/src/System/Runtime/InternalCalls.cs b/src/Runtime.Base/src/System/Runtime/InternalCalls.cs
index e647e3ab0..0431c5bc0 100644
--- a/src/Runtime.Base/src/System/Runtime/InternalCalls.cs
+++ b/src/Runtime.Base/src/System/Runtime/InternalCalls.cs
@@ -194,10 +194,10 @@ namespace System.Runtime
//
// internal calls for GC stress
//
- [RuntimeImport(Redhawk.BaseName, "RhpInitializeGcStress")]
- [MethodImpl(MethodImplOptions.InternalCall)]
- [ManuallyManaged(GcPollPolicy.Never)]
- internal extern static unsafe void RhpInitializeGcStress();
+ //[RuntimeImport(Redhawk.BaseName, "RhpInitializeGcStress")]
+ //[MethodImpl(MethodImplOptions.InternalCall)]
+ //[ManuallyManaged(GcPollPolicy.Never)]
+ //internal extern static unsafe void RhpInitializeGcStress();
#endif // FEATURE_GC_STRESS
[RuntimeImport(Redhawk.BaseName, "RhpEHEnumInitFromStackFrameIterator")]
diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs
index 87d91c2c7..b7d0795e5 100644
--- a/src/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs
+++ b/src/Runtime.Base/src/System/Runtime/InteropServices/CharSet.cs
@@ -11,7 +11,7 @@ namespace System.Runtime.InteropServices
// Generally you probably want to use Auto, which does the
// right thing 99% of the time.
- internal enum CharSet
+ public enum CharSet
{
None = 1, // User didn't specify how to marshal strings.
Ansi = 2, // Strings should be marshalled as ANSI 1 byte chars.
diff --git a/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs b/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs
index 53a6fea04..4834423e9 100644
--- a/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs
+++ b/src/Runtime.Base/src/System/Runtime/InteropServices/DllImportAttribute.cs
@@ -7,6 +7,9 @@ namespace System.Runtime.InteropServices
[AttributeUsage(AttributeTargets.Method)]
public sealed class DllImportAttribute : Attribute
{
+ public string EntryPoint;
+ public CharSet CharSet;
+ public bool SetLastError;
public CallingConvention CallingConvention;
public DllImportAttribute(string dllName)
diff --git a/src/Runtime.Base/src/System/String.cs b/src/Runtime.Base/src/System/String.cs
index 0173be678..890f6e140 100644
--- a/src/Runtime.Base/src/System/String.cs
+++ b/src/Runtime.Base/src/System/String.cs
@@ -54,14 +54,14 @@ namespace System
// This type does not override GetHashCode, Equals
#pragma warning disable 0661, 0660
[StructLayout(LayoutKind.Sequential)]
- public class String
+ public partial class String
{
#if BIT64
private const int POINTER_SIZE = 8;
#else
private const int POINTER_SIZE = 4;
#endif
- // m_pEEType + m_stringLength
+ // m_pEEType + m_stringLength
internal const int FIRST_CHAR_OFFSET = POINTER_SIZE + sizeof(int);
// CS0169: The private field '{blah}' is never used
diff --git a/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs b/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs
index db6977ee5..8fa240d37 100644
--- a/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs
+++ b/src/System.Private.CoreLib/src/Internal/Runtime/ThreadStatics.cs
@@ -55,7 +55,9 @@ namespace Internal.Runtime
object[] newStorage = new object[requiredSize];
if (existingStorage != null)
{
- Array.Copy(existingStorage, newStorage, existingStorage.Length);
+ // TODO MattW FIX THIS!!!
+ //Array.Copy(existingStorage, newStorage, existingStorage.Length);
+ throw new InvalidOperationException();
}
// Install the newly created array as thread static storage for the given module
diff --git a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
index 79f5c2e83..1159cd81a 100644
--- a/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
+++ b/src/System.Private.CoreLib/src/System/Runtime/InteropServices/SafeHandle.cs
@@ -446,7 +446,7 @@ namespace System.Runtime.InteropServices
// which throws ObjectDisposed if someone passes an uninitialized WaitHandle into one of the Wait apis. We use an extension method
// because otherwise, the "null this" would trigger a NullReferenceException before we ever get to this check.
if (safeHandle == null)
- throw new ObjectDisposedException(SR.ObjectDisposed_Generic);
+ throw new ObjectDisposedException("ObjectDisposed_Generic"); // SR.ObjectDisposed_Generic);
safeHandle.DangerousAddRef_WithNoNullCheck();
}
}
diff --git a/src/Test.CoreLib/src/System/Console.cs b/src/Test.CoreLib/src/System/Console.cs
new file mode 100644
index 000000000..5ed92e2d5
--- /dev/null
+++ b/src/Test.CoreLib/src/System/Console.cs
@@ -0,0 +1,49 @@
+//using System.Text;
+//using System.Security;
+using Microsoft.Win32;
+using Microsoft.Win32.SafeHandles;
+
+namespace System
+{
+ // Code below 'borrowed' from https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/Internal/Console.cs
+ public static class Console
+ {
+ // Using static initialisers (or whatever these are) causes the program to hang!!!!
+ //private static readonly SafeFileHandle _outputHandle =
+ // new SafeFileHandle(Win32Native.GetStdHandle(Win32Native.STD_OUTPUT_HANDLE), false);
+
+ public static unsafe void Write(string s)
+ {
+ // TODO MattW - Cheat, until we've implemented UnicodeEncoding.GetBytes(string)
+ // This is 'Hello World! (hard-coded!!)' in UTF8
+ byte[] bytes = new byte[] {
+ 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 32,
+ 40, 104, 97, 114, 100, 45, 99, 111, 100, 101, 100, 33, 33, 41
+ };
+ //byte[] bytes = Encoding.UTF8.GetBytes(s);
+ //byte [] bytes = UnicodeEncoding.GetString()
+
+ Interop.mincore.OutputDebugString("Console.Write - (1) - " + s);
+ SafeFileHandle _outputHandle =
+ new SafeFileHandle(Win32Native.GetStdHandle(Win32Native.STD_OUTPUT_HANDLE), false);
+ Interop.mincore.OutputDebugString("Console.Write - (2)");
+ fixed (byte* pBytes = bytes)
+ {
+ int bytesWritten;
+ Interop.mincore.OutputDebugString("Console.Write - (3)");
+ Win32Native.WriteFile(_outputHandle, pBytes, bytes.Length, out bytesWritten, IntPtr.Zero);
+ Interop.mincore.OutputDebugString("Console.Write - (4)");
+ }
+ }
+
+ public static void WriteLine(string s)
+ {
+ Write(s + Environment.NewLine);
+ }
+
+ public static void WriteLine()
+ {
+ Write(Environment.NewLine);
+ }
+ }
+}
diff --git a/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs
index 014fe31b5..ea5626123 100644
--- a/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs
+++ b/src/Test.CoreLib/src/System/Runtime/RuntimeImports.cs
@@ -25,7 +25,7 @@ namespace System.Runtime
// but if a class library wants to factor differently (such as putting the GCHandle methods in an
// optional library, those methods can be moved to a different file/namespace/dll
- public static class RuntimeImports
+ public static partial class RuntimeImports
{
private const string RuntimeLibrary = "[MRT]";
@@ -99,6 +99,14 @@ namespace System.Runtime
[RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg32")]
internal extern static int InterlockedCompareExchange(ref int location1, int value, int comparand);
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+#if BIT64
+ [RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg64")]
+#else
+ [RuntimeImport(RuntimeLibrary, "RhpLockCmpXchg32")]
+#endif
+ internal extern static IntPtr InterlockedCompareExchange(ref IntPtr location1, IntPtr value, IntPtr comparand);
+
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhpMemoryBarrier")]
internal extern static void MemoryBarrier();
@@ -109,5 +117,25 @@ namespace System.Runtime
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[RuntimeImport(RuntimeLibrary, "RhBulkMoveWithWriteBarrier")]
internal static extern unsafe void RhBulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, nuint size);
+
+ // Mark an object instance as already finalized.
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "RhSuppressFinalize")]
+ internal static extern void RhSuppressFinalize(Object obj);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "RhNewString")]
+ internal static extern String RhNewString(EETypePtr pEEType, int length);
+
+ [DllImport(RuntimeImports.RuntimeLibrary)]
+ internal static extern unsafe void memmove(byte* dmem, byte* smem, nuint size);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "RhGetThreadStaticStorageForModule")]
+ internal static unsafe extern Array RhGetThreadStaticStorageForModule(Int32 moduleIndex);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ [RuntimeImport(RuntimeLibrary, "RhSetThreadStaticStorageForModule")]
+ internal static unsafe extern bool RhSetThreadStaticStorageForModule(Array storage, Int32 moduleIndex);
}
}
diff --git a/src/Test.CoreLib/src/System/String.Test.CoreLib.cs b/src/Test.CoreLib/src/System/String.Test.CoreLib.cs
new file mode 100644
index 000000000..004e929cd
--- /dev/null
+++ b/src/Test.CoreLib/src/System/String.Test.CoreLib.cs
@@ -0,0 +1,247 @@
+using System.Runtime;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+// From corert\src\System.Private.CoreLib\src\System\Buffer.cs
+#if BIT64
+using nint = System.Int64;
+using nuint = System.UInt64;
+#else
+using nint = System.Int32;
+using nuint = System.UInt32;
+#endif
+
+namespace System
+{
+ public partial class String
+ {
+ // From corert\src\System.Private.CoreLib\src\System\String.CoreRT.cs
+ internal static String FastAllocateString(int length)
+ {
+ // We allocate one extra char as an interop convenience so that our strings are null-
+ // terminated, however, we don't pass the extra +1 to the string allocation because the base
+ // size of this object includes the _firstChar field.
+ string newStr = RuntimeImports.RhNewString(EETypePtr.EETypePtrOf<string>(), length);
+ //Debug.Assert(newStr._stringLength == length);
+ return newStr;
+ }
+
+ internal static String FromChar(char c)
+ {
+ string result = string.FastAllocateString(1);
+ result._firstChar = c;
+ return result;
+ }
+
+ // From corert\src\System.Private.CoreLib\src\System\Buffer.cs
+ [MethodImplAttribute(MethodImplOptions.NoInlining)]
+ private static unsafe void _Memmove(byte* dest, byte* src, nuint len)
+ {
+ RuntimeImports.memmove(dest, src, len);
+ }
+
+ // From corert\src\System.Private.CoreLib\shared\System\String.cs
+ internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount)
+ {
+ //Buffer.Memmove((byte*)dmem, (byte*)smem, ((uint)charCount) * 2);
+ // For simplicity, I think we can just fall back to the runtime version,
+ // Buffer.Memmove(..) *seems* to be an optimised version for non-overlapping buffers
+ _Memmove((byte*)dmem, (byte*)smem, ((uint)charCount) * 2);
+ }
+
+ // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
+ private static unsafe void FillStringChecked(string dest, int destPos, string src)
+ {
+ //Debug.Assert(dest != null);
+ //Debug.Assert(src != null);
+ if (src.Length > dest.Length - destPos)
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ fixed (char* pDest = &dest._firstChar)
+ fixed (char* pSrc = &src._firstChar)
+ {
+ wstrcpy(pDest + destPos, pSrc, src.Length);
+ }
+ }
+
+ // From corert\src\System.Private.CoreLib\shared\System\String.cs
+ public static bool IsNullOrEmpty(string value)
+ {
+ // Using 0u >= (uint)value.Length rather than
+ // value.Length == 0 as it will elide the bounds check to
+ // the first char: value[0] if that is performed following the test
+ // for the same test cost.
+ // Ternary operator returning true/false prevents redundant asm generation:
+ // https://github.com/dotnet/coreclr/issues/914
+ return (value == null || 0u >= (uint)value.Length) ? true : false;
+ }
+
+ // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
+ public static string Concat(string str0, string str1)
+ {
+ if (IsNullOrEmpty(str0))
+ {
+ if (IsNullOrEmpty(str1))
+ {
+ return ""; //string.Empty;
+ }
+ return str1;
+ }
+
+ if (IsNullOrEmpty(str1))
+ {
+ return str0;
+ }
+
+ int str0Length = str0.Length;
+
+ string result = FastAllocateString(str0Length + str1.Length);
+
+ FillStringChecked(result, 0, str0);
+ FillStringChecked(result, str0Length, str1);
+
+ return result;
+ }
+
+ public static string Concat(string str0, int int1)
+ {
+ return Concat(str0, UnicodeEncoding.IntToString(int1));
+ }
+
+ // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
+ public static string Concat(string str0, string str1, string str2)
+ {
+ if (IsNullOrEmpty(str0))
+ {
+ return Concat(str1, str2);
+ }
+
+ if (IsNullOrEmpty(str1))
+ {
+ return Concat(str0, str2);
+ }
+
+ if (IsNullOrEmpty(str2))
+ {
+ return Concat(str0, str1);
+ }
+
+ int totalLength = str0.Length + str1.Length + str2.Length;
+
+ string result = FastAllocateString(totalLength);
+ FillStringChecked(result, 0, str0);
+ FillStringChecked(result, str0.Length, str1);
+ FillStringChecked(result, str0.Length + str1.Length, str2);
+
+ return result;
+ }
+
+ // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
+ public static string Concat(string str0, string str1, string str2, string str3)
+ {
+ if (IsNullOrEmpty(str0))
+ {
+ return Concat(str1, str2, str3);
+ }
+
+ if (IsNullOrEmpty(str1))
+ {
+ return Concat(str0, str2, str3);
+ }
+
+ if (IsNullOrEmpty(str2))
+ {
+ return Concat(str0, str1, str3);
+ }
+
+ if (IsNullOrEmpty(str3))
+ {
+ return Concat(str0, str1, str2);
+ }
+
+ int totalLength = str0.Length + str1.Length + str2.Length + str3.Length;
+
+ string result = FastAllocateString(totalLength);
+ FillStringChecked(result, 0, str0);
+ FillStringChecked(result, str0.Length, str1);
+ FillStringChecked(result, str0.Length + str1.Length, str2);
+ FillStringChecked(result, str0.Length + str1.Length + str2.Length, str3);
+
+ return result;
+ }
+
+ // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
+ public static string Concat(params string[] values)
+ {
+ if (values == null)
+ throw new ArgumentNullException(); // nameof(values));
+
+ if (values.Length <= 1)
+ {
+ return values.Length == 0 ?
+ "" : //string.Empty :
+ values[0] ?? ""; // string.Empty;
+ }
+
+ // It's possible that the input values array could be changed concurrently on another
+ // thread, such that we can't trust that each read of values[i] will be equivalent.
+ // Worst case, we can make a defensive copy of the array and use that, but we first
+ // optimistically try the allocation and copies assuming that the array isn't changing,
+ // which represents the 99.999% case, in particular since string.Concat is used for
+ // string concatenation by the languages, with the input array being a params array.
+
+ // Sum the lengths of all input strings
+ long totalLengthLong = 0;
+ for (int i = 0; i < values.Length; i++)
+ {
+ string value = values[i];
+ if (value != null)
+ {
+ totalLengthLong += value.Length;
+ }
+ }
+
+ // If it's too long, fail, or if it's empty, return an empty string.
+ if (totalLengthLong > int.MaxValue)
+ {
+ throw new OutOfMemoryException();
+ }
+ int totalLength = (int)totalLengthLong;
+ if (totalLength == 0)
+ {
+ return ""; //string.Empty;
+ }
+
+ // Allocate a new string and copy each input string into it
+ string result = FastAllocateString(totalLength);
+ int copiedLength = 0;
+ for (int i = 0; i < values.Length; i++)
+ {
+ string value = values[i];
+ if (!string.IsNullOrEmpty(value))
+ {
+ int valueLen = value.Length;
+ if (valueLen > totalLength - copiedLength)
+ {
+ copiedLength = -1;
+ break;
+ }
+
+ FillStringChecked(result, copiedLength, value);
+ copiedLength += valueLen;
+ }
+ }
+
+ // If we copied exactly the right amount, return the new string. Otherwise,
+ // something changed concurrently to mutate the input array: fall back to
+ // doing the concatenation again, but this time with a defensive copy. This
+ // fall back should be extremely rare.
+ // TODO MattW Fix This!!
+ //return copiedLength == totalLength ? result : Concat((string[])values.Clone());
+ return result;
+ }
+ }
+}
diff --git a/src/Test.CoreLib/src/System/Text/Encoding.cs b/src/Test.CoreLib/src/System/Text/Encoding.cs
new file mode 100644
index 000000000..27572f018
--- /dev/null
+++ b/src/Test.CoreLib/src/System/Text/Encoding.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace System.Text
+{
+ public class Encoding
+ {
+ public static UnicodeEncoding UTF8 = new UnicodeEncoding();
+ }
+}
diff --git a/src/Test.CoreLib/src/System/Text/UnicodeEncoding.cs b/src/Test.CoreLib/src/System/Text/UnicodeEncoding.cs
new file mode 100644
index 000000000..419500c39
--- /dev/null
+++ b/src/Test.CoreLib/src/System/Text/UnicodeEncoding.cs
@@ -0,0 +1,193 @@
+using System;
+
+namespace System.Text
+{
+ public class UnicodeEncoding //: Encoding
+ {
+ public static unsafe string GetString(byte* bytesPtr, int length)
+ {
+ Interop.mincore.OutputDebugString("Got here (1)");
+ bool succeeded = true;
+ int _index = 0, _currentLenCache = 0;
+ var _utf8Bytes = new byte[length];
+ Interop.mincore.OutputDebugString("Got here (2)");
+ Interop.mincore.OutputDebugString("Got here (3): " + IntToString(length));
+ for (int i = 0; i < length; ++i)
+ {
+ _utf8Bytes[i] = *bytesPtr;
+ bytesPtr++;
+ }
+
+ string result = "";
+ while (succeeded)
+ {
+ succeeded = Utf8Helper.TryDecodeCodePoint(_utf8Bytes, _index, out uint codePoint, out _currentLenCache);
+
+ if (succeeded)
+ {
+ var currentChar = String.FromChar((char)codePoint);
+ Interop.mincore.OutputDebugString(IntToString(_index) + " - SUCCEEDED - " + currentChar); // IntToString((int)codePoint));
+ result += currentChar;
+ }
+ else
+ Interop.mincore.OutputDebugString(IntToString(_index) + " - FAILED - " + String.FromChar((char)codePoint)); // IntToString((int)codePoint));
+ _index += _currentLenCache;
+ }
+
+ return result;
+ }
+
+ // From https://stackoverflow.com/questions/17575375/how-do-i-convert-an-int-to-a-string-in-c-sharp-without-using-tostring/17575453#17575453
+ public static string IntToString(int a)
+ {
+ var chars = new[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+ var str = ""; // string.Empty;
+ if (a == 0)
+ {
+ str = chars[0];
+ }
+ else if (a == int.MinValue)
+ {
+ str = "-2147483648";
+ }
+ else
+ {
+ bool isNegative = (a < 0);
+ if (isNegative)
+ {
+ a = -a;
+ }
+
+ while (a > 0)
+ {
+ str = chars[a % 10] + str;
+ a /= 10;
+ }
+
+ if (isNegative)
+ {
+ str = "-" + str;
+ }
+ }
+
+ return str;
+ }
+ }
+
+ // From https://github.com/dotnet/corefxlab/blob/master/src/System.Text.Utf8String/System/Text/Primitives/Utf8Helper.cs#L30
+ // Just changed from ReadOnlySpan<byte> -> byte[]
+ internal static class Utf8Helper
+ {
+ #region Constants
+ // To get this to compile with dotnet cli, we need to temporarily un-binary the magic values
+ private const byte b0000_0111U = 0x07; //7
+ private const byte b0000_1111U = 0x0F; //15
+ private const byte b0001_1111U = 0x1F; //31
+ private const byte b0011_1111U = 0x3F; //63
+ private const byte b0111_1111U = 0x7F; //127
+ private const byte b1000_0000U = 0x80; //128
+ private const byte b1100_0000U = 0xC0; //192
+ private const byte b1110_0000U = 0xE0; //224
+ private const byte b1111_0000U = 0xF0; //240
+ private const byte b1111_1000U = 0xF8; //248
+
+ private const byte NonFirstByteInCodePointValue = 0x80;
+ private const byte NonFirstByteInCodePointMask = 0xC0;
+
+ public const int MaxCodeUnitsPerCodePoint = 4;
+ #endregion Constants
+
+ public static bool TryDecodeCodePoint(byte[] utf8, int index, out uint codePoint, out int bytesConsumed)
+ {
+ if (index >= utf8.Length)
+ {
+ codePoint = default;
+ bytesConsumed = 0;
+ return false;
+ }
+
+ var first = utf8[index];
+
+ bytesConsumed = GetEncodedBytes(first);
+ if (bytesConsumed == 0 || utf8.Length - index < bytesConsumed)
+ {
+ bytesConsumed = 0;
+ codePoint = default;
+ return false;
+ }
+
+ switch (bytesConsumed)
+ {
+ case 1:
+ codePoint = first;
+ break;
+
+ case 2:
+ codePoint = (uint)(first & b0001_1111U);
+ break;
+
+ case 3:
+ codePoint = (uint)(first & b0000_1111U);
+ break;
+
+ case 4:
+ codePoint = (uint)(first & b0000_0111U);
+ break;
+
+ default:
+ codePoint = default;
+ bytesConsumed = 0;
+ return false;
+ }
+
+ for (var i = 1; i < bytesConsumed; i++)
+ {
+ uint current = utf8[index + i];
+ if ((current & b1100_0000U) != b1000_0000U)
+ {
+ bytesConsumed = 0;
+ codePoint = default;
+ return false;
+ }
+
+ codePoint = (codePoint << 6) | (b0011_1111U & current);
+ }
+
+ return true;
+ }
+
+ private static int GetEncodedBytes(byte b)
+ {
+ if ((b & b1000_0000U) == 0)
+ return 1;
+
+ if ((b & b1110_0000U) == b1100_0000U)
+ return 2;
+
+ if ((b & b1111_0000U) == b1110_0000U)
+ return 3;
+
+ if ((b & b1111_1000U) == b1111_0000U)
+ return 4;
+
+ return 0;
+ }
+
+ public static int GetNumberOfEncodedBytes(uint codePoint)
+ {
+ if (codePoint <= 0x7F)
+ return 1;
+
+ if (codePoint <= 0x7FF)
+ return 2;
+
+ if (codePoint <= 0xFFFF)
+ return 3;
+
+ if (codePoint <= 0x10FFFF)
+ return 4;
+
+ return 0;
+ }
+ }
+}
diff --git a/src/Test.CoreLib/src/System/Threading/Interlocked.cs b/src/Test.CoreLib/src/System/Threading/Interlocked.cs
index 73f72f46c..de2e2c4b1 100644
--- a/src/Test.CoreLib/src/System/Threading/Interlocked.cs
+++ b/src/Test.CoreLib/src/System/Threading/Interlocked.cs
@@ -15,6 +15,12 @@ namespace System.Threading
return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand);
}
+ [Intrinsic]
+ public static IntPtr CompareExchange(ref IntPtr location1, IntPtr value, IntPtr comparand)
+ {
+ return RuntimeImports.InterlockedCompareExchange(ref location1, value, comparand);
+ }
+
[Intrinsic]
public static void MemoryBarrier()
{
diff --git a/src/Test.CoreLib/src/System/__OtherRequiredCode.cs b/src/Test.CoreLib/src/System/__OtherRequiredCode.cs
new file mode 100644
index 000000000..1e4d7c856
--- /dev/null
+++ b/src/Test.CoreLib/src/System/__OtherRequiredCode.cs
@@ -0,0 +1,520 @@
+using System;
+using System.Runtime.InteropServices;
+using Microsoft.Win32.SafeHandles;
+using System.Runtime;
+using System.Threading;
+using System.Text;
+using System.Runtime.ConstrainedExecution;
+
+// From corert\src\System.Private.CoreLib\src\System\Buffer.cs
+#if BIT64
+using nint = System.Int64;
+using nuint = System.UInt64;
+#else
+using nint = System.Int32;
+using nuint = System.UInt32;
+#endif
+
+internal static partial class Interop
+{
+ // From corert\src\System.Private.CoreLib\shared\Interop\Windows\Interop.Libraries.cs
+ internal static partial class Libraries
+ {
+ internal const string Kernel32 = "kernel32.dll";
+ }
+
+ // From corert\src\System.Private.CoreLib\shared\Interop\Windows\Kernel32\Interop.CloseHandle.cs
+ internal partial class Kernel32
+ {
+ [DllImport(Libraries.Kernel32, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static extern bool CloseHandle(IntPtr handle);
+ }
+
+ internal static unsafe partial class mincore
+ {
+ // From corert\src\Common\src\Interop\Windows\mincore\Interop.DynamicLoad.cs
+ [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll")]
+ internal static extern IntPtr GetProcAddress(IntPtr hModule, byte* lpProcName);
+
+ [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll", EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode)]
+ internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags);
+
+ [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll")]
+ internal static extern bool FreeLibrary(IntPtr hModule);
+
+ // From corert\src\Common\src\Interop\Windows\mincore\Interop.GetLastError.cs
+ [DllImport("api-ms-win-core-errorhandling-l1-1-0.dll")]
+ internal extern static int GetLastError();
+
+ // From corert\src\Common\src\Interop\Windows\mincore\Interop.SetLastError.cs
+ [DllImport("api-ms-win-core-errorhandling-l1-1-0.dll")]
+ internal extern static void SetLastError(uint dwErrCode);
+
+ // From corert\src\System.Private.CoreLib\src\Interop\Interop.manual.cs
+ [DllImport("api-ms-win-core-debug-l1-1-0.dll", EntryPoint = "OutputDebugStringW", CharSet = CharSet.Unicode)]
+ internal extern static void OutputDebugString(string lpOutputString);
+ }
+}
+
+// From https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/Microsoft/Win32/Win32Native.cs
+namespace Microsoft.Win32
+{
+ internal static class Win32Native
+ {
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
+ internal static extern unsafe int WriteFile(SafeFileHandle handle, byte* bytes, int numBytesToWrite, out int numBytesWritten, IntPtr mustBeZero);
+
+ // Note, these are #defines used to extract handles, and are NOT handles.
+ internal const int STD_INPUT_HANDLE = -10;
+ internal const int STD_OUTPUT_HANDLE = -11;
+ internal const int STD_ERROR_HANDLE = -12;
+
+ [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
+ internal static extern IntPtr GetStdHandle(int nStdHandle); // param is NOT a handle, but it returns one!
+ }
+}
+
+namespace System.Diagnostics.CodeAnalysis
+{
+ public class SuppressMessageAttribute : Attribute
+ {
+ public SuppressMessageAttribute(string category, string checkId)
+ { }
+ }
+
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ // From corert\src\System.Private.CoreLib\shared\Microsoft\Win32\SafeHandles\SafeHandleZeroOrMinusOneIsInvalid.cs
+ // Class of safe handle which uses 0 or -1 as an invalid handle.
+ public abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle
+ {
+ protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero || handle == new IntPtr(-1);
+ }
+
+ // From corert\src\System.Private.CoreLib\shared\Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs (cut-down)
+ public sealed class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ public SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle)
+ {
+ SetHandle(preexistingHandle);
+ }
+
+ override protected bool ReleaseHandle()
+ {
+ return Interop.Kernel32.CloseHandle(handle);
+ }
+ }
+
+}
+
+namespace System
+{
+ // Code from corert\src\System.Private.CoreLib\src\System\GC.cs
+ public static partial class GC
+ {
+ public static void SuppressFinalize(Object obj)
+ {
+ if (obj == null)
+ {
+ throw new ArgumentNullException(); // nameof(obj));
+ }
+
+ RuntimeImports.RhSuppressFinalize(obj);
+ }
+ }
+
+ // Code from corert\src\System.Private.CoreLib\src\System\Environment.cs
+ public static partial class Environment
+ {
+ public static String NewLine
+ {
+ get
+ {
+#if !PLATFORM_UNIX
+ return "\r\n";
+#else
+ return "\n";
+#endif // !PLATFORM_UNIX
+ }
+ }
+ }
+
+ // From corert\src\System.Private.CoreLib\src\System\InvokeUtils.cs
+ public class InvokeUtils
+ {
+ public class ArgSetupState
+ {
+
+ }
+
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
+ internal static void DynamicInvokeArgSetupComplete(ref ArgSetupState argSetupState)
+ {
+ //int parametersLength = s_parameters != null ? s_parameters.Length : 0;
+
+ //if (s_curIndex != parametersLength)
+ //{
+ // throw new System.Reflection.TargetParameterCountException();
+ //}
+ //argSetupState.fComplete = true;
+ //argSetupState.nullableCopyBackObjects = s_nullableCopyBackObjects;
+ //s_nullableCopyBackObjects = null;
+
+ // TODO MattW fix this!!
+ //Console.WriteLine("## InvalidOperationException - DynamicInvokeArgSetupComplete ##");
+ throw new InvalidOperationException();
+ }
+
+ //[DebuggerStepThrough]
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
+ internal static ref IntPtr DynamicInvokeParamHelperIn(RuntimeTypeHandle rth)
+ {
+ ////
+ //// Call DynamicInvokeParamHelperCore as an in parameter, and return a managed byref to the interesting bit.
+ ////
+ //// This function exactly matches DynamicInvokeParamHelperRef except for the value of the enum passed to DynamicInvokeParamHelperCore
+ ////
+
+ //int index;
+ //DynamicInvokeParamLookupType paramLookupType;
+ //object obj = DynamicInvokeParamHelperCore(rth, out paramLookupType, out index, DynamicInvokeParamType.In);
+
+ //if (paramLookupType == DynamicInvokeParamLookupType.ValuetypeObjectReturned)
+ //{
+ // return ref Unsafe.As<byte, IntPtr>(ref obj.GetRawData());
+ //}
+ //else
+ //{
+ // return ref Unsafe.As<object, IntPtr>(ref Unsafe.As<object[]>(obj)[index]);
+ //}
+ // TODO MattW fix this!!
+ //Console.WriteLine("## InvalidOperationException - DynamicInvokeParamHelperIn ##");
+ throw new InvalidOperationException();
+ }
+
+ //[DebuggerStepThrough]
+ [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
+ internal static ref IntPtr DynamicInvokeParamHelperRef(RuntimeTypeHandle rth)
+ {
+ ////
+ //// Call DynamicInvokeParamHelperCore as a ref parameter, and return a managed byref to the interesting bit. As this can't actually be defined in C# there is an IL transform that fills this in.
+ ////
+ //// This function exactly matches DynamicInvokeParamHelperIn except for the value of the enum passed to DynamicInvokeParamHelperCore
+ ////
+
+ //int index;
+ //DynamicInvokeParamLookupType paramLookupType;
+ //object obj = DynamicInvokeParamHelperCore(rth, out paramLookupType, out index, DynamicInvokeParamType.Ref);
+
+ //if (paramLookupType == DynamicInvokeParamLookupType.ValuetypeObjectReturned)
+ //{
+ // return ref Unsafe.As<byte, IntPtr>(ref obj.GetRawData());
+ //}
+ //else
+ //{
+ // return ref Unsafe.As<object, IntPtr>(ref Unsafe.As<object[]>(obj)[index]);
+ //}
+ // TODO MattW fix this!!
+ //Console.WriteLine("## InvalidOperationException - DynamicInvokeParamHelperRef ##");
+ throw new InvalidOperationException();
+ }
+ }
+}
+
+namespace System.Runtime.InteropServices
+{
+ // From corert\src\System.Private.CoreLib\src\System\Runtime\InteropServices\PInvokeMarshal.Windows.cs
+ public partial class PInvokeMarshal
+ {
+ [ThreadStatic]
+ internal static int s_lastWin32Error;
+
+ public static void SaveLastWin32Error()
+ {
+ s_lastWin32Error = Interop.mincore.GetLastError();
+ }
+
+ public static void ClearLastWin32Error()
+ {
+ Interop.mincore.SetLastError(0);
+ }
+ }
+}
+
+namespace System.Runtime.CompilerServices
+{
+ // From corert\src\System.Private.CoreLib\src\System\Runtime\CompilerServices\RuntimeHelpers.cs
+ public 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;
+ }
+ }
+ }
+}
+
+// From corert\src\System.Private.CoreLib\src\Internal\Runtime\CompilerHelpers\InteropHelpers.cs
+namespace Internal.Runtime.CompilerHelpers
+{
+ internal static class InteropHelpers
+ {
+ internal static unsafe IntPtr ResolvePInvoke(MethodFixupCell* pCell)
+ {
+ Interop.mincore.OutputDebugString("In ResolvePInvoke");
+ if (pCell->Target != IntPtr.Zero)
+ return pCell->Target;
+
+ //return ResolvePInvokeSlow(pCell);
+ IntPtr result = ResolvePInvokeSlow(pCell);
+ Interop.mincore.OutputDebugString("In ResolvePInvoke (After ResolvePInvokeSlow())");
+ return result;
+ }
+
+ internal static unsafe IntPtr ResolvePInvokeSlow(MethodFixupCell* pCell)
+ {
+ Interop.mincore.OutputDebugString("In ResolvePInvokeSlow");
+ ModuleFixupCell* pModuleCell = pCell->Module;
+ IntPtr hModule = pModuleCell->Handle;
+ if (hModule == IntPtr.Zero)
+ {
+ FixupModuleCell(pModuleCell);
+ hModule = pModuleCell->Handle;
+ }
+
+ FixupMethodCell(hModule, pCell);
+ return pCell->Target;
+ }
+
+ internal static unsafe IntPtr TryResolveModule(string moduleName)
+ {
+ Interop.mincore.OutputDebugString("In TryResolveModule - " + moduleName);
+ IntPtr hModule = IntPtr.Zero;
+
+ // Try original name first
+ hModule = LoadLibrary(moduleName);
+ if (hModule != IntPtr.Zero)
+ return hModule;
+
+#if PLATFORM_UNIX
+ const string PAL_SHLIB_PREFIX = "lib";
+#if PLATFORM_OSX
+ const string PAL_SHLIB_SUFFIX = ".dylib";
+#else
+ const string PAL_SHLIB_SUFFIX = ".so";
+#endif
+
+ // Try prefix+name+suffix
+ hModule = LoadLibrary(PAL_SHLIB_PREFIX + moduleName + PAL_SHLIB_SUFFIX);
+ if (hModule != IntPtr.Zero) return hModule;
+
+ // Try name+suffix
+ hModule = LoadLibrary(moduleName + PAL_SHLIB_SUFFIX);
+ if (hModule != IntPtr.Zero) return hModule;
+
+ // Try prefix+name
+ hModule = LoadLibrary(PAL_SHLIB_PREFIX + moduleName);
+ if (hModule != IntPtr.Zero) return hModule;
+#endif
+ return IntPtr.Zero;
+ }
+
+ internal static unsafe IntPtr LoadLibrary(string moduleName)
+ {
+ Interop.mincore.OutputDebugString("In LoadLibrary " + moduleName);
+ IntPtr hModule;
+
+#if !PLATFORM_UNIX
+ hModule = Interop.mincore.LoadLibraryEx(moduleName, IntPtr.Zero, 0);
+#else
+ hModule = Interop.Sys.LoadLibrary(moduleName);
+#endif
+
+ return hModule;
+ }
+
+ internal static unsafe void FreeLibrary(IntPtr hModule)
+ {
+ Interop.mincore.OutputDebugString("In FreeLibrary");
+#if !PLATFORM_UNIX
+ Interop.mincore.FreeLibrary(hModule);
+#else
+ Interop.Sys.FreeLibrary(hModule);
+#endif
+ }
+
+ private static unsafe string GetModuleName(ModuleFixupCell* pCell)
+ {
+ byte* pModuleName = (byte*)pCell->ModuleName;
+ //return Encoding.UTF8.GetString(pModuleName, strlen(pModuleName));
+ Interop.mincore.OutputDebugString("In GetModuleName (Before strlen())");
+ var len = strlen(pModuleName);
+ Interop.mincore.OutputDebugString("In GetModuleName (Before Encoding.UTF8.GetString())");
+ //var moduleName = Encoding.UTF8.GetString(pModuleName, len); //, strlen(pModuleName));
+ var moduleName = UnicodeEncoding.GetString(pModuleName, strlen(pModuleName));
+ Interop.mincore.OutputDebugString("In GetModuleName -> " + moduleName + " (After Encoding.UTF8.GetString())");
+ return moduleName;
+ }
+
+ internal static unsafe void FixupModuleCell(ModuleFixupCell* pCell)
+ {
+ Interop.mincore.OutputDebugString("In FixupModuleCell (1a)");
+ string moduleName = GetModuleName(pCell);
+ Interop.mincore.OutputDebugString("In FixupModuleCell (1b)");
+ IntPtr hModule = TryResolveModule(moduleName);
+ Interop.mincore.OutputDebugString("In FixupModuleCell (1c)");
+ if (hModule != IntPtr.Zero)
+ {
+ var oldValue = Interlocked.CompareExchange(ref pCell->Handle, hModule, IntPtr.Zero);
+ if (oldValue != IntPtr.Zero)
+ {
+ // Some other thread won the race to fix it up.
+ FreeLibrary(hModule);
+ }
+ }
+ else
+ {
+ //throw new DllNotFoundException(SR.Format(SR.Arg_DllNotFoundExceptionParameterized, moduleName));
+ throw new DllNotFoundException("DllNotFoundExceptionParameterized - " + moduleName);
+ }
+ }
+
+ internal static unsafe void FixupMethodCell(IntPtr hModule, MethodFixupCell* pCell)
+ {
+ Interop.mincore.OutputDebugString("In FixupModuleCell (2a)");
+ byte* methodName = (byte*)pCell->MethodName;
+ Interop.mincore.OutputDebugString("In FixupModuleCell (2a) - " + UnicodeEncoding.GetString(methodName, strlen(methodName)));
+
+#if PLATFORM_WINDOWS
+ pCell->Target = GetProcAddress(hModule, methodName, pCell->CharSetMangling);
+#else
+ pCell->Target = Interop.Sys.GetProcAddress(hModule, methodName);
+#endif
+ if (pCell->Target == IntPtr.Zero)
+ {
+ //string entryPointName = Encoding.UTF8.GetString(methodName, strlen(methodName));
+ string entryPointName = UnicodeEncoding.GetString(methodName, strlen(methodName));
+ Interop.mincore.OutputDebugString("In FixupMethodCell -> " + entryPointName);
+ //throw new EntryPointNotFoundException(SR.Format(SR.Arg_EntryPointNotFoundExceptionParameterized, entryPointName, GetModuleName(pCell->Module)));
+ throw new EntryPointNotFoundException("EntryPointNotFoundExceptionParameterized - " + entryPointName + " - " + GetModuleName(pCell->Module));
+ }
+ else
+ {
+ Interop.mincore.OutputDebugString("In FixupMethodCell pCell->Target IS NOT IntPtr.Zero");
+ }
+ }
+
+#if PLATFORM_WINDOWS
+ private static unsafe IntPtr GetProcAddress(IntPtr hModule, byte* methodName, CharSet charSetMangling)
+ {
+ Interop.mincore.OutputDebugString("In GetProcAddress (1)");
+ // First look for the unmangled name. If it is unicode function, we are going
+ // to need to check for the 'W' API because it takes precedence over the
+ // unmangled one (on NT some APIs have unmangled ANSI exports).
+
+ var exactMatch = Interop.mincore.GetProcAddress(hModule, methodName);
+
+ Interop.mincore.OutputDebugString("In GetProcAddress (2)");
+
+ if ((charSetMangling == CharSet.Ansi && exactMatch != IntPtr.Zero) || charSetMangling == 0)
+ {
+ Interop.mincore.OutputDebugString("In GetProcAddress (3)");
+ return exactMatch;
+ }
+
+ Interop.mincore.OutputDebugString("In GetProcAddress (4)");
+
+ int nameLength = strlen(methodName);
+
+ // We need to add an extra byte for the suffix, and an extra byte for the null terminator
+ byte* probedMethodName = stackalloc byte[nameLength + 2];
+
+ for (int i = 0; i < nameLength; i++)
+ {
+ probedMethodName[i] = methodName[i];
+ }
+
+ probedMethodName[nameLength + 1] = 0;
+
+ probedMethodName[nameLength] = (charSetMangling == CharSet.Ansi) ? (byte)'A' : (byte)'W';
+
+ Interop.mincore.OutputDebugString("In GetProcAddress (5)");
+
+ IntPtr probedMethod = Interop.mincore.GetProcAddress(hModule, probedMethodName);
+ Interop.mincore.OutputDebugString("In GetProcAddress (6)");
+ if (probedMethod != IntPtr.Zero)
+ {
+ return probedMethod;
+ }
+
+ return exactMatch;
+ }
+#endif
+
+ internal static unsafe int strlen(byte* pString)
+ {
+ byte* p = pString;
+ while (*p != 0)
+ p++;
+ return checked((int)(p - pString));
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct ModuleFixupCell
+ {
+ public IntPtr Handle;
+ public IntPtr ModuleName;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct MethodFixupCell
+ {
+ public IntPtr Target;
+ public IntPtr MethodName;
+ public ModuleFixupCell* Module;
+ public CharSet CharSetMangling;
+ }
+ }
+}
+
+namespace System.Reflection
+{
+ //[CLSCompliant(false)]
+ public sealed unsafe class Pointer //: ISerializable
+ {
+ // CoreCLR: Do not add or remove fields without updating the ReflectionPointer class in runtimehandles.h
+ private readonly void* _ptr;
+ private readonly Type _ptrType;
+
+ private Pointer(void* ptr, Type ptrType)
+ {
+ //Debug.Assert(ptrType.IsRuntimeImplemented()); // CoreCLR: For CoreRT's sake, _ptrType has to be declared as "Type", but in fact, it is always a RuntimeType. Code on CoreCLR expects this.
+ _ptr = ptr;
+ _ptrType = ptrType;
+ }
+
+ public static object Box(void* ptr, Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException(); // nameof(type));
+ //if (!type.IsPointer)
+ // throw new ArgumentException(SR.Arg_MustBePointer, nameof(ptr));
+ //if (!type.IsRuntimeImplemented())
+ // throw new ArgumentException(SR.Arg_MustBeType, nameof(ptr));
+
+ return new Pointer(ptr, type);
+ }
+ }
+}
diff --git a/src/Test.CoreLib/src/Test.CoreLib.csproj b/src/Test.CoreLib/src/Test.CoreLib.csproj
index 685a4fd63..2e2087aa3 100644
--- a/src/Test.CoreLib/src/Test.CoreLib.csproj
+++ b/src/Test.CoreLib/src/Test.CoreLib.csproj
@@ -1,4 +1,4 @@
-<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<OutputType>Library</OutputType>
@@ -7,6 +7,7 @@
<IsCoreAssembly>true</IsCoreAssembly>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<EnableFxCopAnalyzers>false</EnableFxCopAnalyzers>
+ <ProjectGuid>{F32CD720-A195-4407-B8EF-7A82F88F8432}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>FEATURE_GC_STRESS;$(DefineConstants)</DefineConstants>
@@ -29,6 +30,81 @@
<DefineConstants Condition="'$(Platform)' == 'arm'">FEATURE_64BIT_ALIGNMENT;$(DefineConstants)</DefineConstants>
<DefineConstants Condition="'$(Platform)' == 'armel'">FEATURE_64BIT_ALIGNMENT;$(DefineConstants)</DefineConstants>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+ <OutputPath>..\..\..\bin\Windows_NT.x64.Debug\Test.CoreLib\</OutputPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>..\..\..\bin\Windows_NT.x64.Release\Test.CoreLib\</OutputPath>
+ <DefineConstants>INPLACE_RUNTIME;EETYPE_TYPE_MANAGER;FEATURE_GC_STRESS;AMD64;BIT64;PLATFORM_WINDOWS;CORERT;TRACE;;DEBUGRESOURCES;SIGNED</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <NoStdLib>true</NoStdLib>
+ <DebugType>portable</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <LangVersion>latest</LangVersion>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\x86\Release\</OutputPath>
+ <DefineConstants>INPLACE_RUNTIME;EETYPE_TYPE_MANAGER;FEATURE_GC_STRESS;X86;BIT32;PLATFORM_WINDOWS;CORERT;DEBUG;TRACE;;DEBUGRESOURCES;SIGNED</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <NoStdLib>true</NoStdLib>
+ <DebugType>portable</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <LangVersion>latest</LangVersion>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|arm'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\arm\Release\</OutputPath>
+ <DefineConstants>FEATURE_64BIT_ALIGNMENT;INPLACE_RUNTIME;EETYPE_TYPE_MANAGER;FEATURE_64BIT_ALIGNMENT;FEATURE_GC_STRESS;ARM;BIT32;PLATFORM_WINDOWS;CORERT;DEBUG;TRACE;;DEBUGRESOURCES;SIGNED</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <NoStdLib>true</NoStdLib>
+ <DebugType>portable</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <LangVersion>latest</LangVersion>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|armel'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\armel\Release\</OutputPath>
+ <DefineConstants>FEATURE_64BIT_ALIGNMENT;INPLACE_RUNTIME;EETYPE_TYPE_MANAGER;FEATURE_64BIT_ALIGNMENT;FEATURE_GC_STRESS;ARM;BIT32;PLATFORM_WINDOWS;CORERT;DEBUG;TRACE;;DEBUGRESOURCES;SIGNED</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <NoStdLib>true</NoStdLib>
+ <DebugType>portable</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <LangVersion>latest</LangVersion>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|arm64'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\arm64\Release\</OutputPath>
+ <DefineConstants>INPLACE_RUNTIME;EETYPE_TYPE_MANAGER;FEATURE_GC_STRESS;ARM64;BIT64;PLATFORM_WINDOWS;CORERT;DEBUG;TRACE;;DEBUGRESOURCES;SIGNED</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <NoStdLib>true</NoStdLib>
+ <DebugType>portable</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <LangVersion>latest</LangVersion>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|wasm'">
+ <DebugSymbols>true</DebugSymbols>
+ <OutputPath>bin\wasm\Release\</OutputPath>
+ <DefineConstants>INPLACE_RUNTIME;EETYPE_TYPE_MANAGER;FEATURE_GC_STRESS;WASM;BIT32;PLATFORM_WINDOWS;CORERT;DEBUG;TRACE;;DEBUGRESOURCES;SIGNED</DefineConstants>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+ <NoStdLib>true</NoStdLib>
+ <DebugType>portable</DebugType>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <LangVersion>latest</LangVersion>
+ <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ </PropertyGroup>
<ItemGroup Condition="'$(InPlaceRuntime)' == 'true'">
<Compile Include="..\..\Runtime.Base\src\System\Runtime\CachedInterfaceDispatch.cs">
<Link>Runtime.Base\src\System\Runtime\CachedInterfaceDispatch.cs</Link>
@@ -56,7 +132,7 @@
</Compile>
<Compile Include="..\..\Runtime.Base\src\System\Runtime\CastableObjectSupport.cs">
<Link>Runtime.Base\src\System\Runtime\CastableObjectSupport.cs</Link>
- </Compile>
+ </Compile>
<Compile Include="..\..\Runtime.Base\src\System\Runtime\RuntimeExports.cs">
<Link>Runtime.Base\src\System\Runtime\RuntimeExports.cs</Link>
</Compile>
@@ -228,9 +304,40 @@
<Compile Include="..\..\Runtime.Base\src\Internal\Runtime\CompilerServices\Unsafe.cs">
<Link>Internal\Runtime\CompilerServices\Unsafe.cs</Link>
</Compile>
+ <Compile Include="..\..\System.Private.CoreLib\shared\System\IDisposable.cs">
+ <Link>System\IDisposable.cs</Link>
+ </Compile>
+ <Compile Include="..\..\System.Private.CoreLib\shared\System\Runtime\CompilerServices\ExtensionAttribute.cs">
+ <Link>System\Runtime\CompilerServices\ExtensionAttribute.cs</Link>
+ </Compile>
+ <Compile Include="..\..\System.Private.CoreLib\shared\System\Runtime\ConstrainedExecution\CriticalFinalizerObject.cs">
+ <Link>System\Runtime\ConstrainedExecution\CriticalFinalizerObject.cs</Link>
+ </Compile>
+ <Compile Include="..\..\System.Private.CoreLib\shared\System\Runtime\InteropServices\MarshalAsAttribute.cs">
+ <Link>System\Runtime\InteropServices\MarshalAsAttribute.cs</Link>
+ </Compile>
+ <Compile Include="..\..\System.Private.CoreLib\shared\System\Runtime\InteropServices\UnmanagedType.cs">
+ <Link>System\Runtime\InteropServices\UnmanagedType.cs</Link>
+ </Compile>
+ <Compile Include="..\..\System.Private.CoreLib\shared\System\Runtime\InteropServices\VarEnum.cs">
+ <Link>System\Runtime\InteropServices\VarEnum.cs</Link>
+ </Compile>
+ <Compile Include="..\..\System.Private.CoreLib\shared\System\ThreadStaticAttribute.cs">
+ <Link>System\ThreadStaticAttribute.cs</Link>
+ </Compile>
+ <Compile Include="..\..\System.Private.CoreLib\src\Internal\Runtime\ThreadStatics.cs">
+ <Link>Internal\Runtime\ThreadStatics.cs</Link>
+ </Compile>
+ <Compile Include="..\..\System.Private.CoreLib\src\System\Runtime\InteropServices\SafeHandle.cs">
+ <Link>System\Runtime\InteropServices\SafeHandle.cs</Link>
+ </Compile>
+ <Compile Include="System\Console.cs" />
<Compile Include="System\Runtime\CompilerServices\ClassConstructorRunner.cs" />
<Compile Include="System\Runtime\CompilerServices\StaticClassConstructionContext.cs" />
<Compile Include="System\Runtime\RuntimeImports.cs" />
+ <Compile Include="System\String.Test.CoreLib.cs" />
+ <Compile Include="System\Text\Encoding.cs" />
+ <Compile Include="System\Text\UnicodeEncoding.cs" />
<Compile Include="System\Threading\Interlocked.cs" />
<Compile Include="System\Array.cs" />
<Compile Include="System\RuntimeExceptionHelpers.cs" />
@@ -240,11 +347,13 @@
<Compile Include="..\..\Common\src\Internal\Runtime\TypeManagerHandle.cs">
<Link>Internal\Runtime\TypeManagerHandle.cs</Link>
</Compile>
+ <Compile Include="System\__OtherRequiredCode.cs" />
</ItemGroup>
+ <ItemGroup />
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
<PropertyGroup>
<!-- Exclude AssemblyInfoPartialFile -->
<AssemblyInfoPartialFile>
</AssemblyInfoPartialFile>
</PropertyGroup>
-</Project>
+</Project>
@mattwarren
Copy link
Author

System\Console.cs

//using System.Text;
//using System.Security;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;

namespace System
{
    // Code below 'borrowed' from https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/Internal/Console.cs	
    public static class Console
    {
        // Using static initialisers (or whatever these are) causes the program to hang!!!!
        //private static readonly SafeFileHandle _outputHandle =
        //    new SafeFileHandle(Win32Native.GetStdHandle(Win32Native.STD_OUTPUT_HANDLE), false);

        public static unsafe void Write(string s)
        {
            // TODO MattW - Cheat, until we've implemented UnicodeEncoding.GetBytes(string)
            // This is 'Hello World! (hard-coded!!)' in UTF8
            byte[] bytes = new byte[] {
                72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 32,
                40, 104, 97, 114, 100, 45, 99, 111, 100, 101, 100, 33, 33, 41
            }; 
            //byte[] bytes = Encoding.UTF8.GetBytes(s);
            //byte [] bytes = UnicodeEncoding.GetString()

            Interop.mincore.OutputDebugString("Console.Write - (1) - " + s);
            SafeFileHandle _outputHandle =
                new SafeFileHandle(Win32Native.GetStdHandle(Win32Native.STD_OUTPUT_HANDLE), false);
            Interop.mincore.OutputDebugString("Console.Write - (2)");
            fixed (byte* pBytes = bytes)
            {
                int bytesWritten;
                Interop.mincore.OutputDebugString("Console.Write - (3)");
                Win32Native.WriteFile(_outputHandle, pBytes, bytes.Length, out bytesWritten, IntPtr.Zero);
                Interop.mincore.OutputDebugString("Console.Write - (4)");
            }
        }

        public static void WriteLine(string s)
        {
            Write(s + Environment.NewLine);
        }

        public static void WriteLine()
        {
            Write(Environment.NewLine);
        }
    }
}

@mattwarren
Copy link
Author

System\String.Test.CoreLib.cs

using System.Runtime;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;

// From corert\src\System.Private.CoreLib\src\System\Buffer.cs
#if BIT64
using nint = System.Int64;
using nuint = System.UInt64;
#else
using nint = System.Int32;
using nuint = System.UInt32;
#endif

namespace System
{
    public partial class String
    {
        // From corert\src\System.Private.CoreLib\src\System\String.CoreRT.cs
        internal static String FastAllocateString(int length)
        {
            // We allocate one extra char as an interop convenience so that our strings are null-
            // terminated, however, we don't pass the extra +1 to the string allocation because the base
            // size of this object includes the _firstChar field.
            string newStr = RuntimeImports.RhNewString(EETypePtr.EETypePtrOf<string>(), length);
            //Debug.Assert(newStr._stringLength == length);
            return newStr;
        }

        internal static String FromChar(char c)
        {
            string result = string.FastAllocateString(1);
            result._firstChar = c;
            return result;
        }

        // From corert\src\System.Private.CoreLib\src\System\Buffer.cs
        [MethodImplAttribute(MethodImplOptions.NoInlining)]
        private static unsafe void _Memmove(byte* dest, byte* src, nuint len)
        {
            RuntimeImports.memmove(dest, src, len);
        }

        // From corert\src\System.Private.CoreLib\shared\System\String.cs
        internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount)
        {
            //Buffer.Memmove((byte*)dmem, (byte*)smem, ((uint)charCount) * 2);
            // For simplicity, I think we can just fall back to the runtime version, 
            // Buffer.Memmove(..) *seems* to be an optimised version for non-overlapping buffers
            _Memmove((byte*)dmem, (byte*)smem, ((uint)charCount) * 2);
        }

        // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
        private static unsafe void FillStringChecked(string dest, int destPos, string src)
        {
            //Debug.Assert(dest != null);
            //Debug.Assert(src != null);
            if (src.Length > dest.Length - destPos)
            {
                throw new IndexOutOfRangeException();
            }

            fixed (char* pDest = &dest._firstChar)
            fixed (char* pSrc = &src._firstChar)
            {
                wstrcpy(pDest + destPos, pSrc, src.Length);
            }
        }

        // From corert\src\System.Private.CoreLib\shared\System\String.cs
        public static bool IsNullOrEmpty(string value)
        {
            // Using 0u >= (uint)value.Length rather than
            // value.Length == 0 as it will elide the bounds check to
            // the first char: value[0] if that is performed following the test
            // for the same test cost.
            // Ternary operator returning true/false prevents redundant asm generation:
            // https://github.com/dotnet/coreclr/issues/914
            return (value == null || 0u >= (uint)value.Length) ? true : false;
        }

        // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
        public static string Concat(string str0, string str1)
        {
            if (IsNullOrEmpty(str0))
            {
                if (IsNullOrEmpty(str1))
                {
                    return ""; //string.Empty;
                }
                return str1;
            }

            if (IsNullOrEmpty(str1))
            {
                return str0;
            }

            int str0Length = str0.Length;

            string result = FastAllocateString(str0Length + str1.Length);

            FillStringChecked(result, 0, str0);
            FillStringChecked(result, str0Length, str1);

            return result;
        }

        public static string Concat(string str0, int int1)
        {
            return Concat(str0, UnicodeEncoding.IntToString(int1));
        }

        // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
        public static string Concat(string str0, string str1, string str2)
        {
            if (IsNullOrEmpty(str0))
            {
                return Concat(str1, str2);
            }

            if (IsNullOrEmpty(str1))
            {
                return Concat(str0, str2);
            }

            if (IsNullOrEmpty(str2))
            {
                return Concat(str0, str1);
            }

            int totalLength = str0.Length + str1.Length + str2.Length;

            string result = FastAllocateString(totalLength);
            FillStringChecked(result, 0, str0);
            FillStringChecked(result, str0.Length, str1);
            FillStringChecked(result, str0.Length + str1.Length, str2);

            return result;
        }

        // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
        public static string Concat(string str0, string str1, string str2, string str3)
        {
            if (IsNullOrEmpty(str0))
            {
                return Concat(str1, str2, str3);
            }

            if (IsNullOrEmpty(str1))
            {
                return Concat(str0, str2, str3);
            }

            if (IsNullOrEmpty(str2))
            {
                return Concat(str0, str1, str3);
            }

            if (IsNullOrEmpty(str3))
            {
                return Concat(str0, str1, str2);
            }

            int totalLength = str0.Length + str1.Length + str2.Length + str3.Length;

            string result = FastAllocateString(totalLength);
            FillStringChecked(result, 0, str0);
            FillStringChecked(result, str0.Length, str1);
            FillStringChecked(result, str0.Length + str1.Length, str2);
            FillStringChecked(result, str0.Length + str1.Length + str2.Length, str3);

            return result;
        }

        // From corert\src\System.Private.CoreLib\shared\System\String.Manipulation.cs
        public static string Concat(params string[] values)
        {
            if (values == null)
                throw new ArgumentNullException(); // nameof(values));

            if (values.Length <= 1)
            {
                return values.Length == 0 ?
                    "" : //string.Empty :
                    values[0] ?? ""; // string.Empty;
            }

            // It's possible that the input values array could be changed concurrently on another
            // thread, such that we can't trust that each read of values[i] will be equivalent.
            // Worst case, we can make a defensive copy of the array and use that, but we first
            // optimistically try the allocation and copies assuming that the array isn't changing,
            // which represents the 99.999% case, in particular since string.Concat is used for
            // string concatenation by the languages, with the input array being a params array.

            // Sum the lengths of all input strings
            long totalLengthLong = 0;
            for (int i = 0; i < values.Length; i++)
            {
                string value = values[i];
                if (value != null)
                {
                    totalLengthLong += value.Length;
                }
            }

            // If it's too long, fail, or if it's empty, return an empty string.
            if (totalLengthLong > int.MaxValue)
            {
                throw new OutOfMemoryException();
            }
            int totalLength = (int)totalLengthLong;
            if (totalLength == 0)
            {
                return ""; //string.Empty;
            }

            // Allocate a new string and copy each input string into it
            string result = FastAllocateString(totalLength);
            int copiedLength = 0;
            for (int i = 0; i < values.Length; i++)
            {
                string value = values[i];
                if (!string.IsNullOrEmpty(value))
                {
                    int valueLen = value.Length;
                    if (valueLen > totalLength - copiedLength)
                    {
                        copiedLength = -1;
                        break;
                    }

                    FillStringChecked(result, copiedLength, value);
                    copiedLength += valueLen;
                }
            }

            // If we copied exactly the right amount, return the new string.  Otherwise,
            // something changed concurrently to mutate the input array: fall back to
            // doing the concatenation again, but this time with a defensive copy. This
            // fall back should be extremely rare.
            // TODO MattW Fix This!!
            //return copiedLength == totalLength ? result : Concat((string[])values.Clone());
            return result;
        }
    }
}

@mattwarren
Copy link
Author

System\Text\Encoding.cs

using System;

namespace System.Text
{
    public class Encoding
    {
        public static UnicodeEncoding UTF8 = new UnicodeEncoding();
    }
}

@mattwarren
Copy link
Author

System\Text\UnicodeEncoding.cs

using System;

namespace System.Text
{
    public class UnicodeEncoding //: Encoding
    {
        public static unsafe string GetString(byte* bytesPtr, int length)
        {
            Interop.mincore.OutputDebugString("Got here (1)");
            bool succeeded = true;
            int _index = 0, _currentLenCache = 0;
            var _utf8Bytes = new byte[length];
            Interop.mincore.OutputDebugString("Got here (2)");
            Interop.mincore.OutputDebugString("Got here (3): " + IntToString(length));
            for (int i = 0; i < length; ++i)
            {
                _utf8Bytes[i] = *bytesPtr;
                bytesPtr++;
            }

            string result = "";
            while (succeeded)
            {
                succeeded = Utf8Helper.TryDecodeCodePoint(_utf8Bytes, _index, out uint codePoint, out _currentLenCache);

                if (succeeded)
                {
                    var currentChar = String.FromChar((char)codePoint);
                    Interop.mincore.OutputDebugString(IntToString(_index) + " - SUCCEEDED - " + currentChar); // IntToString((int)codePoint));
                    result += currentChar;
                }
                else
                    Interop.mincore.OutputDebugString(IntToString(_index) + " - FAILED - " + String.FromChar((char)codePoint)); // IntToString((int)codePoint));
                _index += _currentLenCache;
            }            

            return result;
        }

        // From https://stackoverflow.com/questions/17575375/how-do-i-convert-an-int-to-a-string-in-c-sharp-without-using-tostring/17575453#17575453
        public static string IntToString(int a)
        {
            var chars = new[] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
            var str = ""; // string.Empty;
            if (a == 0)
            {
                str = chars[0];
            }
            else if (a == int.MinValue)
            {
                str = "-2147483648";
            }
            else
            {
                bool isNegative = (a < 0);
                if (isNegative)
                {
                    a = -a;
                }

                while (a > 0)
                {
                    str = chars[a % 10] + str;
                    a /= 10;
                }

                if (isNegative)
                {
                    str = "-" + str;
                }
            }

            return str;
        }
    }

    // From https://github.com/dotnet/corefxlab/blob/master/src/System.Text.Utf8String/System/Text/Primitives/Utf8Helper.cs#L30
    // Just changed from ReadOnlySpan<byte> -> byte[]
    internal static class Utf8Helper
    {
        #region Constants
        // To get this to compile with dotnet cli, we need to temporarily un-binary the magic values
        private const byte b0000_0111U = 0x07; //7
        private const byte b0000_1111U = 0x0F; //15
        private const byte b0001_1111U = 0x1F; //31
        private const byte b0011_1111U = 0x3F; //63
        private const byte b0111_1111U = 0x7F; //127
        private const byte b1000_0000U = 0x80; //128
        private const byte b1100_0000U = 0xC0; //192
        private const byte b1110_0000U = 0xE0; //224
        private const byte b1111_0000U = 0xF0; //240
        private const byte b1111_1000U = 0xF8; //248

        private const byte NonFirstByteInCodePointValue = 0x80;
        private const byte NonFirstByteInCodePointMask = 0xC0;

        public const int MaxCodeUnitsPerCodePoint = 4;
        #endregion Constants

        public static bool TryDecodeCodePoint(byte[] utf8, int index, out uint codePoint, out int bytesConsumed)
        {
            if (index >= utf8.Length)
            {
                codePoint = default;
                bytesConsumed = 0;
                return false;
            }

            var first = utf8[index];

            bytesConsumed = GetEncodedBytes(first);
            if (bytesConsumed == 0 || utf8.Length - index < bytesConsumed)
            {
                bytesConsumed = 0;
                codePoint = default;
                return false;
            }

            switch (bytesConsumed)
            {
                case 1:
                    codePoint = first;
                    break;

                case 2:
                    codePoint = (uint)(first & b0001_1111U);
                    break;

                case 3:
                    codePoint = (uint)(first & b0000_1111U);
                    break;

                case 4:
                    codePoint = (uint)(first & b0000_0111U);
                    break;

                default:
                    codePoint = default;
                    bytesConsumed = 0;
                    return false;
            }

            for (var i = 1; i < bytesConsumed; i++)
            {
                uint current = utf8[index + i];
                if ((current & b1100_0000U) != b1000_0000U)
                {
                    bytesConsumed = 0;
                    codePoint = default;
                    return false;
                }

                codePoint = (codePoint << 6) | (b0011_1111U & current);
            }

            return true;
        }

        private static int GetEncodedBytes(byte b)
        {
            if ((b & b1000_0000U) == 0)
                return 1;

            if ((b & b1110_0000U) == b1100_0000U)
                return 2;

            if ((b & b1111_0000U) == b1110_0000U)
                return 3;

            if ((b & b1111_1000U) == b1111_0000U)
                return 4;

            return 0;
        }

        public static int GetNumberOfEncodedBytes(uint codePoint)
        {
            if (codePoint <= 0x7F)
                return 1;

            if (codePoint <= 0x7FF)
                return 2;

            if (codePoint <= 0xFFFF)
                return 3;

            if (codePoint <= 0x10FFFF)
                return 4;

            return 0;
        }
    }
}

@mattwarren
Copy link
Author

System__OtherRequiredCode.cs

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Runtime;
using System.Threading;
using System.Text;
using System.Runtime.ConstrainedExecution;

// From corert\src\System.Private.CoreLib\src\System\Buffer.cs
#if BIT64
using nint = System.Int64;
using nuint = System.UInt64;
#else
using nint = System.Int32;
using nuint = System.UInt32;
#endif

internal static partial class Interop
{
    // From corert\src\System.Private.CoreLib\shared\Interop\Windows\Interop.Libraries.cs
    internal static partial class Libraries
    {
        internal const string Kernel32 = "kernel32.dll";
    }

    // From corert\src\System.Private.CoreLib\shared\Interop\Windows\Kernel32\Interop.CloseHandle.cs
    internal partial class Kernel32
    {
        [DllImport(Libraries.Kernel32, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool CloseHandle(IntPtr handle);
    }
    
    internal static unsafe partial class mincore
    {
        // From corert\src\Common\src\Interop\Windows\mincore\Interop.DynamicLoad.cs
        [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll")]
        internal static extern IntPtr GetProcAddress(IntPtr hModule, byte* lpProcName);

        [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll", EntryPoint = "LoadLibraryExW", CharSet = CharSet.Unicode)]
        internal static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags);

        [DllImport("api-ms-win-core-libraryloader-l1-2-0.dll")]
        internal static extern bool FreeLibrary(IntPtr hModule);

        // From corert\src\Common\src\Interop\Windows\mincore\Interop.GetLastError.cs
        [DllImport("api-ms-win-core-errorhandling-l1-1-0.dll")]
        internal extern static int GetLastError();

        // From corert\src\Common\src\Interop\Windows\mincore\Interop.SetLastError.cs
        [DllImport("api-ms-win-core-errorhandling-l1-1-0.dll")]
        internal extern static void SetLastError(uint dwErrCode);

        // From corert\src\System.Private.CoreLib\src\Interop\Interop.manual.cs
        [DllImport("api-ms-win-core-debug-l1-1-0.dll", EntryPoint = "OutputDebugStringW", CharSet = CharSet.Unicode)]
        internal extern static void OutputDebugString(string lpOutputString);
    }
}

// From https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/Microsoft/Win32/Win32Native.cs
namespace Microsoft.Win32
{
    internal static class Win32Native
    {
        [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
        internal static extern unsafe int WriteFile(SafeFileHandle handle, byte* bytes, int numBytesToWrite, out int numBytesWritten, IntPtr mustBeZero);

        // Note, these are #defines used to extract handles, and are NOT handles.
        internal const int STD_INPUT_HANDLE = -10;
        internal const int STD_OUTPUT_HANDLE = -11;
        internal const int STD_ERROR_HANDLE = -12;

        [DllImport(Interop.Libraries.Kernel32, SetLastError = true)]
        internal static extern IntPtr GetStdHandle(int nStdHandle);  // param is NOT a handle, but it returns one!
    }
}

namespace System.Diagnostics.CodeAnalysis
{
    public class SuppressMessageAttribute : Attribute
    {
        public SuppressMessageAttribute(string category, string checkId)
        { }
    }

}

namespace Microsoft.Win32.SafeHandles
{
    // From corert\src\System.Private.CoreLib\shared\Microsoft\Win32\SafeHandles\SafeHandleZeroOrMinusOneIsInvalid.cs
    // Class of safe handle which uses 0 or -1 as an invalid handle.
    public abstract class SafeHandleZeroOrMinusOneIsInvalid : SafeHandle
    {
        protected SafeHandleZeroOrMinusOneIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle)
        {
        }

        public override bool IsInvalid => handle == IntPtr.Zero || handle == new IntPtr(-1);
    }

    // From corert\src\System.Private.CoreLib\shared\Microsoft\Win32\SafeHandles\SafeFileHandle.Windows.cs (cut-down)
    public sealed class SafeFileHandle : SafeHandleZeroOrMinusOneIsInvalid
    {        
        public SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle)
        {
            SetHandle(preexistingHandle);
        }               

        override protected bool ReleaseHandle()
        {
            return Interop.Kernel32.CloseHandle(handle);
        }
    }

}

namespace System
{
    // Code from corert\src\System.Private.CoreLib\src\System\GC.cs
    public static partial class GC
    {
        public static void SuppressFinalize(Object obj)
        {
            if (obj == null)
            {
                throw new ArgumentNullException(); // nameof(obj));
            }

            RuntimeImports.RhSuppressFinalize(obj);
        }
    }

    // Code from corert\src\System.Private.CoreLib\src\System\Environment.cs
    public static partial class Environment
    {
        public static String NewLine
        {
            get
            {
#if !PLATFORM_UNIX
                return "\r\n";
#else
                return "\n";
#endif // !PLATFORM_UNIX
            }
        }
    }

    // From corert\src\System.Private.CoreLib\src\System\InvokeUtils.cs
    public class InvokeUtils
    {
        public class ArgSetupState
        {

        }

        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        internal static void DynamicInvokeArgSetupComplete(ref ArgSetupState argSetupState)
        {
            //int parametersLength = s_parameters != null ? s_parameters.Length : 0;

            //if (s_curIndex != parametersLength)
            //{
            //    throw new System.Reflection.TargetParameterCountException();
            //}
            //argSetupState.fComplete = true;
            //argSetupState.nullableCopyBackObjects = s_nullableCopyBackObjects;
            //s_nullableCopyBackObjects = null;

            // TODO MattW fix this!!
            //Console.WriteLine("## InvalidOperationException - DynamicInvokeArgSetupComplete ##");
            throw new InvalidOperationException();
        }

        //[DebuggerStepThrough]
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        internal static ref IntPtr DynamicInvokeParamHelperIn(RuntimeTypeHandle rth)
        {
            ////
            //// Call DynamicInvokeParamHelperCore as an in parameter, and return a managed byref to the interesting bit.
            ////
            //// This function exactly matches DynamicInvokeParamHelperRef except for the value of the enum passed to DynamicInvokeParamHelperCore
            //// 

            //int index;
            //DynamicInvokeParamLookupType paramLookupType;
            //object obj = DynamicInvokeParamHelperCore(rth, out paramLookupType, out index, DynamicInvokeParamType.In);

            //if (paramLookupType == DynamicInvokeParamLookupType.ValuetypeObjectReturned)
            //{
            //    return ref Unsafe.As<byte, IntPtr>(ref obj.GetRawData());
            //}
            //else
            //{
            //    return ref Unsafe.As<object, IntPtr>(ref Unsafe.As<object[]>(obj)[index]);
            //}
            // TODO MattW fix this!!
            //Console.WriteLine("## InvalidOperationException - DynamicInvokeParamHelperIn ##");
            throw new InvalidOperationException();
        }

        //[DebuggerStepThrough]
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
        internal static ref IntPtr DynamicInvokeParamHelperRef(RuntimeTypeHandle rth)
        {
            ////
            //// Call DynamicInvokeParamHelperCore as a ref parameter, and return a managed byref to the interesting bit. As this can't actually be defined in C# there is an IL transform that fills this in.
            ////
            //// This function exactly matches DynamicInvokeParamHelperIn except for the value of the enum passed to DynamicInvokeParamHelperCore
            //// 

            //int index;
            //DynamicInvokeParamLookupType paramLookupType;
            //object obj = DynamicInvokeParamHelperCore(rth, out paramLookupType, out index, DynamicInvokeParamType.Ref);

            //if (paramLookupType == DynamicInvokeParamLookupType.ValuetypeObjectReturned)
            //{
            //    return ref Unsafe.As<byte, IntPtr>(ref obj.GetRawData());
            //}
            //else
            //{
            //    return ref Unsafe.As<object, IntPtr>(ref Unsafe.As<object[]>(obj)[index]);
            //}
            // TODO MattW fix this!!
            //Console.WriteLine("## InvalidOperationException - DynamicInvokeParamHelperRef ##");
            throw new InvalidOperationException();
        }
    }
}

namespace System.Runtime.InteropServices
{
    // From corert\src\System.Private.CoreLib\src\System\Runtime\InteropServices\PInvokeMarshal.Windows.cs
    public partial class PInvokeMarshal
    {
        [ThreadStatic]
        internal static int s_lastWin32Error;

        public static void SaveLastWin32Error()
        {
            s_lastWin32Error = Interop.mincore.GetLastError();
        }

        public static void ClearLastWin32Error()
        {
            Interop.mincore.SetLastError(0);
        }
    }
}

namespace System.Runtime.CompilerServices
{
    // From corert\src\System.Private.CoreLib\src\System\Runtime\CompilerServices\RuntimeHelpers.cs
    public 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;
            }
        }
    }
}

// From corert\src\System.Private.CoreLib\src\Internal\Runtime\CompilerHelpers\InteropHelpers.cs
namespace Internal.Runtime.CompilerHelpers
{
    internal static class InteropHelpers
    {
        internal static unsafe IntPtr ResolvePInvoke(MethodFixupCell* pCell)
        {
            Interop.mincore.OutputDebugString("In ResolvePInvoke");
            if (pCell->Target != IntPtr.Zero)
                return pCell->Target;

            //return ResolvePInvokeSlow(pCell);
            IntPtr result = ResolvePInvokeSlow(pCell);
            Interop.mincore.OutputDebugString("In ResolvePInvoke (After ResolvePInvokeSlow())");
            return result;
        }

        internal static unsafe IntPtr ResolvePInvokeSlow(MethodFixupCell* pCell)
        {
            Interop.mincore.OutputDebugString("In ResolvePInvokeSlow");
            ModuleFixupCell* pModuleCell = pCell->Module;
            IntPtr hModule = pModuleCell->Handle;
            if (hModule == IntPtr.Zero)
            {
                FixupModuleCell(pModuleCell);
                hModule = pModuleCell->Handle;
            }

            FixupMethodCell(hModule, pCell);
            return pCell->Target;
        }

        internal static unsafe IntPtr TryResolveModule(string moduleName)
        {
            Interop.mincore.OutputDebugString("In TryResolveModule - " + moduleName);
            IntPtr hModule = IntPtr.Zero;

            // Try original name first
            hModule = LoadLibrary(moduleName);
            if (hModule != IntPtr.Zero)
                return hModule;

#if PLATFORM_UNIX
            const string PAL_SHLIB_PREFIX = "lib";
#if PLATFORM_OSX
            const string PAL_SHLIB_SUFFIX = ".dylib";
#else
            const string PAL_SHLIB_SUFFIX = ".so";
#endif

             // Try prefix+name+suffix
            hModule = LoadLibrary(PAL_SHLIB_PREFIX + moduleName + PAL_SHLIB_SUFFIX);
            if (hModule != IntPtr.Zero) return hModule;

            // Try name+suffix
            hModule = LoadLibrary(moduleName + PAL_SHLIB_SUFFIX);
            if (hModule != IntPtr.Zero) return hModule;

            // Try prefix+name
            hModule = LoadLibrary(PAL_SHLIB_PREFIX + moduleName);
            if (hModule != IntPtr.Zero) return hModule;
#endif
            return IntPtr.Zero;
        }

        internal static unsafe IntPtr LoadLibrary(string moduleName)
        {
            Interop.mincore.OutputDebugString("In LoadLibrary " + moduleName);
            IntPtr hModule;

#if !PLATFORM_UNIX
            hModule = Interop.mincore.LoadLibraryEx(moduleName, IntPtr.Zero, 0);
#else
            hModule = Interop.Sys.LoadLibrary(moduleName);
#endif

            return hModule;
        }

        internal static unsafe void FreeLibrary(IntPtr hModule)
        {
            Interop.mincore.OutputDebugString("In FreeLibrary");
#if !PLATFORM_UNIX
            Interop.mincore.FreeLibrary(hModule);
#else
            Interop.Sys.FreeLibrary(hModule);
#endif
        }

        private static unsafe string GetModuleName(ModuleFixupCell* pCell)
        {
            byte* pModuleName = (byte*)pCell->ModuleName;
            //return Encoding.UTF8.GetString(pModuleName, strlen(pModuleName));
            Interop.mincore.OutputDebugString("In GetModuleName (Before strlen())");
            var len = strlen(pModuleName);
            Interop.mincore.OutputDebugString("In GetModuleName (Before Encoding.UTF8.GetString())");
            //var moduleName = Encoding.UTF8.GetString(pModuleName, len); //, strlen(pModuleName));
            var moduleName = UnicodeEncoding.GetString(pModuleName, strlen(pModuleName));
            Interop.mincore.OutputDebugString("In GetModuleName -> " + moduleName + " (After Encoding.UTF8.GetString())");
            return moduleName;            
        }

        internal static unsafe void FixupModuleCell(ModuleFixupCell* pCell)
        {
            Interop.mincore.OutputDebugString("In FixupModuleCell (1a)");
            string moduleName = GetModuleName(pCell);
            Interop.mincore.OutputDebugString("In FixupModuleCell (1b)");
            IntPtr hModule = TryResolveModule(moduleName);
            Interop.mincore.OutputDebugString("In FixupModuleCell (1c)");
            if (hModule != IntPtr.Zero)
            {
                var oldValue = Interlocked.CompareExchange(ref pCell->Handle, hModule, IntPtr.Zero);
                if (oldValue != IntPtr.Zero)
                {
                    // Some other thread won the race to fix it up.
                    FreeLibrary(hModule);
                }
            }
            else
            {
                //throw new DllNotFoundException(SR.Format(SR.Arg_DllNotFoundExceptionParameterized, moduleName));
                throw new DllNotFoundException("DllNotFoundExceptionParameterized - " + moduleName);
            }
        }

        internal static unsafe void FixupMethodCell(IntPtr hModule, MethodFixupCell* pCell)
        {
            Interop.mincore.OutputDebugString("In FixupModuleCell (2a)");
            byte* methodName = (byte*)pCell->MethodName;
            Interop.mincore.OutputDebugString("In FixupModuleCell (2a) - " + UnicodeEncoding.GetString(methodName, strlen(methodName)));

#if PLATFORM_WINDOWS
            pCell->Target = GetProcAddress(hModule, methodName, pCell->CharSetMangling);
#else
            pCell->Target = Interop.Sys.GetProcAddress(hModule, methodName);
#endif
            if (pCell->Target == IntPtr.Zero)
            {
                //string entryPointName = Encoding.UTF8.GetString(methodName, strlen(methodName));
                string entryPointName = UnicodeEncoding.GetString(methodName, strlen(methodName));
                Interop.mincore.OutputDebugString("In FixupMethodCell -> " + entryPointName);
                //throw new EntryPointNotFoundException(SR.Format(SR.Arg_EntryPointNotFoundExceptionParameterized, entryPointName, GetModuleName(pCell->Module)));
                throw new EntryPointNotFoundException("EntryPointNotFoundExceptionParameterized - " + entryPointName + " - " + GetModuleName(pCell->Module));                
            }
            else
            {
                Interop.mincore.OutputDebugString("In FixupMethodCell pCell->Target IS NOT IntPtr.Zero");
            }
        }

#if PLATFORM_WINDOWS
        private static unsafe IntPtr GetProcAddress(IntPtr hModule, byte* methodName, CharSet charSetMangling)
        {
            Interop.mincore.OutputDebugString("In GetProcAddress (1)");
            // First look for the unmangled name.  If it is unicode function, we are going
            // to need to check for the 'W' API because it takes precedence over the
            // unmangled one (on NT some APIs have unmangled ANSI exports).

            var exactMatch = Interop.mincore.GetProcAddress(hModule, methodName);

            Interop.mincore.OutputDebugString("In GetProcAddress (2)");

            if ((charSetMangling == CharSet.Ansi && exactMatch != IntPtr.Zero) || charSetMangling == 0)
            {
                Interop.mincore.OutputDebugString("In GetProcAddress (3)");
                return exactMatch;
            }

            Interop.mincore.OutputDebugString("In GetProcAddress (4)");

            int nameLength = strlen(methodName);

            // We need to add an extra byte for the suffix, and an extra byte for the null terminator
            byte* probedMethodName = stackalloc byte[nameLength + 2];

            for (int i = 0; i < nameLength; i++)
            {
                probedMethodName[i] = methodName[i];
            }

            probedMethodName[nameLength + 1] = 0;

            probedMethodName[nameLength] = (charSetMangling == CharSet.Ansi) ? (byte)'A' : (byte)'W';

            Interop.mincore.OutputDebugString("In GetProcAddress (5)");

            IntPtr probedMethod = Interop.mincore.GetProcAddress(hModule, probedMethodName);
            Interop.mincore.OutputDebugString("In GetProcAddress (6)");
            if (probedMethod != IntPtr.Zero)
            {
                return probedMethod;
            }

            return exactMatch;
        }
#endif

        internal static unsafe int strlen(byte* pString)
        {
            byte* p = pString;
            while (*p != 0)
                p++;
            return checked((int)(p - pString));
        }

        [StructLayout(LayoutKind.Sequential)]
        internal unsafe struct ModuleFixupCell
        {
            public IntPtr Handle;
            public IntPtr ModuleName;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal unsafe struct MethodFixupCell
        {
            public IntPtr Target;
            public IntPtr MethodName;
            public ModuleFixupCell* Module;
            public CharSet CharSetMangling;
        }
    }
}

namespace System.Reflection
{
    //[CLSCompliant(false)]
    public sealed unsafe class Pointer //: ISerializable
    {
        // CoreCLR: Do not add or remove fields without updating the ReflectionPointer class in runtimehandles.h
        private readonly void* _ptr;
        private readonly Type _ptrType;

        private Pointer(void* ptr, Type ptrType)
        {
            //Debug.Assert(ptrType.IsRuntimeImplemented()); // CoreCLR: For CoreRT's sake, _ptrType has to be declared as "Type", but in fact, it is always a RuntimeType. Code on CoreCLR expects this.
            _ptr = ptr;
            _ptrType = ptrType;
        }

        public static object Box(void* ptr, Type type)
        {
            if (type == null)
                throw new ArgumentNullException(); // nameof(type));
            //if (!type.IsPointer)
            //    throw new ArgumentException(SR.Arg_MustBePointer, nameof(ptr));
            //if (!type.IsRuntimeImplemented())
            //    throw new ArgumentException(SR.Arg_MustBeType, nameof(ptr));

            return new Pointer(ptr, type);
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment