summaryrefslogtreecommitdiff
path: root/src/mscorlib/src
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2017-04-27 07:54:50 (GMT)
committerJiyoung Yun <jy910.yun@samsung.com>2017-04-27 07:54:50 (GMT)
commit5b975f8233e8c8d17b215372f89ca713b45d6a0b (patch)
tree0267bcc331458a01f4c26fafd28110a72273beb3 /src/mscorlib/src
parenta56e30c8d33048216567753d9d3fefc2152af8ac (diff)
downloadcoreclr-5b975f8233e8c8d17b215372f89ca713b45d6a0b.zip
coreclr-5b975f8233e8c8d17b215372f89ca713b45d6a0b.tar.gz
coreclr-5b975f8233e8c8d17b215372f89ca713b45d6a0b.tar.bz2
Imported Upstream version 2.0.0.11599upstream/2.0.0.11599
Diffstat (limited to 'src/mscorlib/src')
-rw-r--r--src/mscorlib/src/Microsoft/Win32/Registry.cs134
-rw-r--r--src/mscorlib/src/Microsoft/Win32/RegistryKey.cs672
-rw-r--r--src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs10
-rw-r--r--src/mscorlib/src/Microsoft/Win32/RegistryView.cs22
-rw-r--r--src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs10
-rw-r--r--src/mscorlib/src/System/Collections/Generic/Dictionary.cs89
-rw-r--r--src/mscorlib/src/System/Collections/Generic/List.cs1
-rw-r--r--src/mscorlib/src/System/Delegate.cs5
-rw-r--r--src/mscorlib/src/System/Environment.cs2
-rw-r--r--src/mscorlib/src/System/Globalization/CompareInfo.cs48
-rw-r--r--src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs61
-rw-r--r--src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs38
-rw-r--r--src/mscorlib/src/System/IO/Stream.cs4
-rw-r--r--src/mscorlib/src/System/ReadOnlySpan.cs313
-rw-r--r--src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs24
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs2
-rw-r--r--src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs9
-rw-r--r--src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs3
-rw-r--r--src/mscorlib/src/System/Span.cs910
-rw-r--r--src/mscorlib/src/System/String.Manipulation.cs7
-rw-r--r--src/mscorlib/src/System/String.cs2
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/Task.cs3
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs2
-rw-r--r--src/mscorlib/src/System/Threading/Tasks/future.cs6
-rw-r--r--src/mscorlib/src/System/ThrowHelper.cs1
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.Win32.cs30
-rw-r--r--src/mscorlib/src/System/TimeZoneInfo.cs114
27 files changed, 1074 insertions, 1448 deletions
diff --git a/src/mscorlib/src/Microsoft/Win32/Registry.cs b/src/mscorlib/src/Microsoft/Win32/Registry.cs
index aa2dd9b..d0dbb0f 100644
--- a/src/mscorlib/src/Microsoft/Win32/Registry.cs
+++ b/src/mscorlib/src/Microsoft/Win32/Registry.cs
@@ -2,12 +2,142 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
+
namespace Microsoft.Win32
{
+ /**
+ * Registry encapsulation. Contains members representing all top level system
+ * keys.
+ *
+ * @security(checkClassLinking=on)
+ */
+ //This class contains only static members and does not need to be serializable.
internal static class Registry
{
- public static readonly RegistryKey CurrentUser = RegistryKey.CurrentUser;
- public static readonly RegistryKey LocalMachine = RegistryKey.LocalMachine;
+ /**
+ * Current User Key.
+ *
+ * This key should be used as the root for all user specific settings.
+ */
+ public static readonly RegistryKey CurrentUser = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER);
+
+ /**
+ * Local Machine Key.
+ *
+ * This key should be used as the root for all machine specific settings.
+ */
+ public static readonly RegistryKey LocalMachine = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE);
+
+ /**
+ * Classes Root Key.
+ *
+ * This is the root key of class information.
+ */
+ public static readonly RegistryKey ClassesRoot = RegistryKey.GetBaseKey(RegistryKey.HKEY_CLASSES_ROOT);
+
+ /**
+ * Users Root Key.
+ *
+ * This is the root of users.
+ */
+ public static readonly RegistryKey Users = RegistryKey.GetBaseKey(RegistryKey.HKEY_USERS);
+
+ /**
+ * Performance Root Key.
+ *
+ * This is where dynamic performance data is stored on NT.
+ */
+ public static readonly RegistryKey PerformanceData = RegistryKey.GetBaseKey(RegistryKey.HKEY_PERFORMANCE_DATA);
+
+ /**
+ * Current Config Root Key.
+ *
+ * This is where current configuration information is stored.
+ */
+ public static readonly RegistryKey CurrentConfig = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_CONFIG);
+
+ //
+ // Following function will parse a keyName and returns the basekey for it.
+ // It will also store the subkey name in the out parameter.
+ // If the keyName is not valid, we will throw ArgumentException.
+ // The return value shouldn't be null.
+ //
+ private static RegistryKey GetBaseKeyFromKeyName(string keyName, out string subKeyName)
+ {
+ if (keyName == null)
+ {
+ throw new ArgumentNullException(nameof(keyName));
+ }
+
+ string basekeyName;
+ int i = keyName.IndexOf('\\');
+ if (i != -1)
+ {
+ basekeyName = keyName.Substring(0, i).ToUpper(System.Globalization.CultureInfo.InvariantCulture);
+ }
+ else
+ {
+ basekeyName = keyName.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
+ }
+ RegistryKey basekey = null;
+
+ switch (basekeyName)
+ {
+ case "HKEY_CURRENT_USER":
+ basekey = Registry.CurrentUser;
+ break;
+ case "HKEY_LOCAL_MACHINE":
+ basekey = Registry.LocalMachine;
+ break;
+ case "HKEY_CLASSES_ROOT":
+ basekey = Registry.ClassesRoot;
+ break;
+ case "HKEY_USERS":
+ basekey = Registry.Users;
+ break;
+ case "HKEY_PERFORMANCE_DATA":
+ basekey = Registry.PerformanceData;
+ break;
+ case "HKEY_CURRENT_CONFIG":
+ basekey = Registry.CurrentConfig;
+ break;
+ default:
+ throw new ArgumentException(SR.Format(SR.Arg_RegInvalidKeyName, nameof(keyName)));
+ }
+ if (i == -1 || i == keyName.Length)
+ {
+ subKeyName = string.Empty;
+ }
+ else
+ {
+ subKeyName = keyName.Substring(i + 1, keyName.Length - i - 1);
+ }
+ return basekey;
+ }
+
+ public static object GetValue(string keyName, string valueName, object defaultValue)
+ {
+ string subKeyName;
+ RegistryKey basekey = GetBaseKeyFromKeyName(keyName, out subKeyName);
+ BCLDebug.Assert(basekey != null, "basekey can't be null.");
+ RegistryKey key = basekey.OpenSubKey(subKeyName);
+ if (key == null)
+ { // if the key doesn't exist, do nothing
+ return null;
+ }
+ try
+ {
+ return key.GetValue(valueName, defaultValue);
+ }
+ finally
+ {
+ key.Close();
+ }
+ }
}
}
diff --git a/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
index e39b959..521ea65 100644
--- a/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
+++ b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
@@ -2,6 +2,28 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+
+/*
+ Note on transaction support:
+ Eventually we will want to add support for NT's transactions to our
+ RegistryKey API's (possibly Whidbey M3?). When we do this, here's
+ the list of API's we need to make transaction-aware:
+
+ RegCreateKeyEx
+ RegDeleteKey
+ RegDeleteValue
+ RegEnumKeyEx
+ RegEnumValue
+ RegOpenKeyEx
+ RegQueryInfoKey
+ RegQueryValueEx
+ RegSetValueEx
+
+ We can ignore RegConnectRegistry (remote registry access doesn't yet have
+ transaction support) and RegFlushKey. RegCloseKey doesn't require any
+ additional work. .
+ */
+
/*
Note on ACL support:
The key thing to note about ACL's is you set them on a kernel object like a
@@ -30,28 +52,34 @@ using Microsoft.Win32.SafeHandles;
using System;
using System.Buffers;
using System.Collections.Generic;
-using System.Diagnostics;
+using System.Diagnostics.Contracts;
using System.IO;
+using System.Text;
namespace Microsoft.Win32
{
/**
* Registry encapsulation. To get an instance of a RegistryKey use the
* Registry class's static members then call OpenSubKey.
+ *
+ * @see Registry
+ * @security(checkDllCalls=off)
+ * @security(checkClassLinking=on)
*/
internal sealed class RegistryKey : MarshalByRefObject, IDisposable
{
- // Use the public Registry.CurrentUser
- internal static readonly RegistryKey CurrentUser =
- GetBaseKey(new IntPtr(unchecked((int)0x80000001)), "HKEY_CURRENT_USER");
-
- // Use the public Registry.LocalMachine
- internal static readonly RegistryKey LocalMachine =
- GetBaseKey(new IntPtr(unchecked((int)0x80000002)), "HKEY_LOCAL_MACHINE");
-
// We could use const here, if C# supported ELEMENT_TYPE_I fully.
- private static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));
- private static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
+ internal static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000));
+ internal static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));
+ internal static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
+ internal static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003));
+ internal static readonly IntPtr HKEY_PERFORMANCE_DATA = new IntPtr(unchecked((int)0x80000004));
+ internal static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005));
+
+ // Dirty indicates that we have munged data that should be potentially
+ // written to disk.
+ //
+ private const int STATE_DIRTY = 0x0001;
// SystemKey indicates that this is a "SYSTEMKEY" and shouldn't be "opened"
// or "closed".
@@ -62,6 +90,20 @@ namespace Microsoft.Win32
//
private const int STATE_WRITEACCESS = 0x0004;
+ // Indicates if this key is for HKEY_PERFORMANCE_DATA
+ private const int STATE_PERF_DATA = 0x0008;
+
+ // Names of keys. This array must be in the same order as the HKEY values listed above.
+ //
+ private static readonly String[] hkeyNames = new String[] {
+ "HKEY_CLASSES_ROOT",
+ "HKEY_CURRENT_USER",
+ "HKEY_LOCAL_MACHINE",
+ "HKEY_USERS",
+ "HKEY_PERFORMANCE_DATA",
+ "HKEY_CURRENT_CONFIG",
+ };
+
// MSDN defines the following limits for registry key names & values:
// Key Name: 255 characters
// Value name: 16,383 Unicode characters
@@ -71,7 +113,31 @@ namespace Microsoft.Win32
private volatile SafeRegistryHandle hkey = null;
private volatile int state = 0;
- private volatile string keyName;
+ private volatile String keyName;
+ private volatile bool remoteKey = false;
+ private volatile RegistryKeyPermissionCheck checkMode;
+ private volatile RegistryView regView = RegistryView.Default;
+
+ /**
+ * RegistryInternalCheck values. Useful only for CheckPermission
+ */
+ private enum RegistryInternalCheck
+ {
+ CheckSubKeyWritePermission = 0,
+ CheckSubKeyReadPermission = 1,
+ CheckSubKeyCreatePermission = 2,
+ CheckSubTreeReadPermission = 3,
+ CheckSubTreeWritePermission = 4,
+ CheckSubTreeReadWritePermission = 5,
+ CheckValueWritePermission = 6,
+ CheckValueCreatePermission = 7,
+ CheckValueReadPermission = 8,
+ CheckKeyReadPermission = 9,
+ CheckSubTreePermission = 10,
+ CheckOpenSubKeyWithWritablePermission = 11,
+ CheckOpenSubKeyPermission = 12
+ };
+
/**
* Creates a RegistryKey.
@@ -82,10 +148,12 @@ namespace Microsoft.Win32
* The remoteKey flag when set to true indicates that we are dealing with registry entries
* on a remote machine and requires the program making these calls to have full trust.
*/
- private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey)
+ private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey, bool remoteKey, bool isPerfData, RegistryView view)
{
this.hkey = hkey;
keyName = "";
+ this.remoteKey = remoteKey;
+ regView = view;
if (systemkey)
{
state |= STATE_SYSTEMKEY;
@@ -94,6 +162,17 @@ namespace Microsoft.Win32
{
state |= STATE_WRITEACCESS;
}
+ if (isPerfData)
+ state |= STATE_PERF_DATA;
+ ValidateKeyView(view);
+ }
+
+ /**
+ * Closes this key, flushes it to disk if the contents have been modified.
+ */
+ public void Close()
+ {
+ Dispose(true);
}
private void Dispose(bool disposing)
@@ -115,6 +194,21 @@ namespace Microsoft.Win32
hkey = null;
}
}
+ else if (disposing && IsPerfDataKey())
+ {
+ // System keys should never be closed. However, we want to call RegCloseKey
+ // on HKEY_PERFORMANCE_DATA when called from PerformanceCounter.CloseSharedResources
+ // (i.e. when disposing is true) so that we release the PERFLIB cache and cause it
+ // to be refreshed (by re-reading the registry) when accessed subsequently.
+ // This is the only way we can see the just installed perf counter.
+ // NOTE: since HKEY_PERFORMANCE_DATA is process wide, there is inherent race condition in closing
+ // the key asynchronously. While Vista is smart enough to rebuild the PERFLIB resources
+ // in this situation the down level OSes are not. We have a small window between
+ // the dispose below and usage elsewhere (other threads). This is By Design.
+ // This is less of an issue when OS > NT5 (i.e Vista & higher), we can close the perfkey
+ // (to release & refresh PERFLIB resources) and the OS will rebuild PERFLIB as necessary.
+ SafeRegistryHandle.RegCloseKey(RegistryKey.HKEY_PERFORMANCE_DATA);
+ }
}
}
@@ -123,13 +217,13 @@ namespace Microsoft.Win32
Dispose(true);
}
- public void DeleteValue(string name, bool throwOnMissingValue)
+ public void DeleteValue(String name, bool throwOnMissingValue)
{
EnsureWriteable();
int errorCode = Win32Native.RegDeleteValue(hkey, name);
//
- // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE
+ // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE
// This still means the name doesn't exist. We need to be consistent with previous OS.
//
if (errorCode == Win32Native.ERROR_FILE_NOT_FOUND || errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE)
@@ -138,44 +232,77 @@ namespace Microsoft.Win32
{
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyValueAbsent);
}
-
// Otherwise, just return giving no indication to the user.
// (For compatibility)
}
-
// We really should throw an exception here if errorCode was bad,
// but we can't for compatibility reasons.
- Debug.Assert(errorCode == 0, "RegDeleteValue failed. Here's your error code: " + errorCode);
+ BCLDebug.Correctness(errorCode == 0, "RegDeleteValue failed. Here's your error code: " + errorCode);
+ }
+
+ /**
+ * Retrieves a new RegistryKey that represents the requested key. Valid
+ * values are:
+ *
+ * HKEY_CLASSES_ROOT,
+ * HKEY_CURRENT_USER,
+ * HKEY_LOCAL_MACHINE,
+ * HKEY_USERS,
+ * HKEY_PERFORMANCE_DATA,
+ * HKEY_CURRENT_CONFIG,
+ * HKEY_DYN_DATA.
+ *
+ * @param hKey HKEY_* to open.
+ *
+ * @return the RegistryKey requested.
+ */
+ internal static RegistryKey GetBaseKey(IntPtr hKey)
+ {
+ return GetBaseKey(hKey, RegistryView.Default);
}
- private static RegistryKey GetBaseKey(IntPtr hKey, string keyName)
+ internal static RegistryKey GetBaseKey(IntPtr hKey, RegistryView view)
{
- SafeRegistryHandle srh = new SafeRegistryHandle(hKey, ownsHandle: false);
+ int index = ((int)hKey) & 0x0FFFFFFF;
+ BCLDebug.Assert(index >= 0 && index < hkeyNames.Length, "index is out of range!");
+ BCLDebug.Assert((((int)hKey) & 0xFFFFFFF0) == 0x80000000, "Invalid hkey value!");
+
+ bool isPerf = hKey == HKEY_PERFORMANCE_DATA;
+ // only mark the SafeHandle as ownsHandle if the key is HKEY_PERFORMANCE_DATA.
+ SafeRegistryHandle srh = new SafeRegistryHandle(hKey, isPerf);
- RegistryKey key = new RegistryKey(srh, true, true);
- key.keyName = keyName;
+ RegistryKey key = new RegistryKey(srh, true, true, false, isPerf, view);
+ key.checkMode = RegistryKeyPermissionCheck.Default;
+ key.keyName = hkeyNames[index];
return key;
}
- /// <summary>
- /// Retrieves a subkey or null if the operation failed.
- /// </summary>
- /// <param name="writable">True to open writable, otherwise opens the key read-only.</param>
+ /**
+ * Retrieves a subkey. If readonly is <b>true</b>, then the subkey is opened with
+ * read-only access.
+ *
+ * @param name Name or path of subkey to open.
+ * @param readonly Set to <b>true</b> if you only need readonly access.
+ *
+ * @return the Subkey requested, or <b>null</b> if the operation failed.
+ */
public RegistryKey OpenSubKey(string name, bool writable)
{
ValidateKeyName(name);
EnsureNotDisposed();
+ name = FixupName(name); // Fixup multiple slashes to a single slash
SafeRegistryHandle result = null;
int ret = Win32Native.RegOpenKeyEx(hkey,
name,
0,
- writable ? Win32Native.KEY_READ | Win32Native.KEY_WRITE : Win32Native.KEY_READ,
+ GetRegistryKeyAccess(writable) | (int)regView,
out result);
if (ret == 0 && !result.IsInvalid)
{
- RegistryKey key = new RegistryKey(result, writable, false);
+ RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView);
+ key.checkMode = GetSubKeyPermissonCheck(writable);
key.keyName = keyName + "\\" + name;
return key;
}
@@ -191,6 +318,41 @@ namespace Microsoft.Win32
return null;
}
+ // This required no security checks. This is to get around the Deleting SubKeys which only require
+ // write permission. They call OpenSubKey which required read. Now instead call this function w/o security checks
+ internal RegistryKey InternalOpenSubKey(String name, bool writable)
+ {
+ ValidateKeyName(name);
+ EnsureNotDisposed();
+
+ SafeRegistryHandle result = null;
+ int ret = Win32Native.RegOpenKeyEx(hkey,
+ name,
+ 0,
+ GetRegistryKeyAccess(writable) | (int)regView,
+ out result);
+
+ if (ret == 0 && !result.IsInvalid)
+ {
+ RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView);
+ key.keyName = keyName + "\\" + name;
+ return key;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a subkey with read only permissions.
+ *
+ * @param name Name or path of subkey to open.
+ *
+ * @return the Subkey requested, or <b>null</b> if the operation failed.
+ */
+ public RegistryKey OpenSubKey(String name)
+ {
+ return OpenSubKey(name, false);
+ }
+
/// <summary>
/// Retrieves an array of strings containing all the subkey names.
/// </summary>
@@ -319,6 +481,22 @@ namespace Microsoft.Win32
}
/**
+ * Retrieves the specified value. <b>null</b> is returned if the value
+ * doesn't exist.
+ *
+ * Note that <var>name</var> can be null or "", at which point the
+ * unnamed or default value of this Registry key is returned, if any.
+ *
+ * @param name Name of value to retrieve.
+ *
+ * @return the data associated with the value.
+ */
+ public Object GetValue(String name)
+ {
+ return InternalGetValue(name, null, false, true);
+ }
+
+ /**
* Retrieves the specified value. <i>defaultValue</i> is returned if the value doesn't exist.
*
* Note that <var>name</var> can be null or "", at which point the
@@ -333,11 +511,30 @@ namespace Microsoft.Win32
*
* @return the data associated with the value.
*/
- public object GetValue(string name, object defaultValue = null, bool doNotExpand = false)
+ public Object GetValue(String name, Object defaultValue)
{
- EnsureNotDisposed();
+ return InternalGetValue(name, defaultValue, false, true);
+ }
+
+ public Object GetValue(String name, Object defaultValue, RegistryValueOptions options)
+ {
+ if (options < RegistryValueOptions.None || options > RegistryValueOptions.DoNotExpandEnvironmentNames)
+ {
+ throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options));
+ }
+ bool doNotExpand = (options == RegistryValueOptions.DoNotExpandEnvironmentNames);
+ return InternalGetValue(name, defaultValue, doNotExpand, true);
+ }
+
+ internal Object InternalGetValue(String name, Object defaultValue, bool doNotExpand, bool checkSecurity)
+ {
+ if (checkSecurity)
+ {
+ // Name can be null! It's the most common use of RegQueryValueEx
+ EnsureNotDisposed();
+ }
- object data = defaultValue;
+ Object data = defaultValue;
int type = 0;
int datasize = 0;
@@ -345,20 +542,54 @@ namespace Microsoft.Win32
if (ret != 0)
{
- // For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data).
- // Some OS's returned ERROR_MORE_DATA even in success cases, so we
- // want to continue on through the function.
- if (ret != Win32Native.ERROR_MORE_DATA)
- return data;
+ if (IsPerfDataKey())
+ {
+ int size = 65000;
+ int sizeInput = size;
+
+ int r;
+ byte[] blob = new byte[size];
+ while (Win32Native.ERROR_MORE_DATA == (r = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref sizeInput)))
+ {
+ if (size == Int32.MaxValue)
+ {
+ // ERROR_MORE_DATA was returned however we cannot increase the buffer size beyond Int32.MaxValue
+ Win32Error(r, name);
+ }
+ else if (size > (Int32.MaxValue / 2))
+ {
+ // at this point in the loop "size * 2" would cause an overflow
+ size = Int32.MaxValue;
+ }
+ else
+ {
+ size *= 2;
+ }
+ sizeInput = size;
+ blob = new byte[size];
+ }
+ if (r != 0)
+ Win32Error(r, name);
+ return blob;
+ }
+ else
+ {
+ // For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data).
+ // Some OS's returned ERROR_MORE_DATA even in success cases, so we
+ // want to continue on through the function.
+ if (ret != Win32Native.ERROR_MORE_DATA)
+ return data;
+ }
}
if (datasize < 0)
{
// unexpected code path
- Debug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize");
+ BCLDebug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize");
datasize = 0;
}
+
switch (type)
{
case Win32Native.REG_NONE:
@@ -378,7 +609,7 @@ namespace Microsoft.Win32
goto case Win32Native.REG_BINARY;
}
long blob = 0;
- Debug.Assert(datasize == 8, "datasize==8");
+ BCLDebug.Assert(datasize == 8, "datasize==8");
// Here, datasize must be 8 when calling this
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
@@ -393,7 +624,7 @@ namespace Microsoft.Win32
goto case Win32Native.REG_QWORD;
}
int blob = 0;
- Debug.Assert(datasize == 4, "datasize==4");
+ BCLDebug.Assert(datasize == 4, "datasize==4");
// Here, datasize must be four when calling this
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
@@ -420,13 +651,13 @@ namespace Microsoft.Win32
ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
if (blob.Length > 0 && blob[blob.Length - 1] == (char)0)
{
- data = new string(blob, 0, blob.Length - 1);
+ data = new String(blob, 0, blob.Length - 1);
}
else
{
// in the very unlikely case the data is missing null termination,
// pass in the whole char[] to prevent truncating a character
- data = new string(blob);
+ data = new String(blob);
}
}
break;
@@ -451,17 +682,17 @@ namespace Microsoft.Win32
if (blob.Length > 0 && blob[blob.Length - 1] == (char)0)
{
- data = new string(blob, 0, blob.Length - 1);
+ data = new String(blob, 0, blob.Length - 1);
}
else
{
// in the very unlikely case the data is missing null termination,
// pass in the whole char[] to prevent truncating a character
- data = new string(blob);
+ data = new String(blob);
}
if (!doNotExpand)
- data = Environment.ExpandEnvironmentVariables((string)data);
+ data = Environment.ExpandEnvironmentVariables((String)data);
}
break;
case Win32Native.REG_MULTI_SZ:
@@ -502,7 +733,8 @@ namespace Microsoft.Win32
blob[blob.Length - 1] = (char)0;
}
- IList<string> strings = new List<string>();
+
+ IList<String> strings = new List<String>();
int cur = 0;
int len = blob.Length;
@@ -516,28 +748,28 @@ namespace Microsoft.Win32
if (nextNull < len)
{
- Debug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0");
+ BCLDebug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0");
if (nextNull - cur > 0)
{
- strings.Add(new string(blob, cur, nextNull - cur));
+ strings.Add(new String(blob, cur, nextNull - cur));
}
else
{
// we found an empty string. But if we're at the end of the data,
// it's just the extra null terminator.
if (nextNull != len - 1)
- strings.Add(string.Empty);
+ strings.Add(String.Empty);
}
}
else
{
- strings.Add(new string(blob, cur, len - cur));
+ strings.Add(new String(blob, cur, len - cur));
}
cur = nextNull + 1;
}
- data = new string[strings.Count];
- strings.CopyTo((string[])data, 0);
+ data = new String[strings.Count];
+ strings.CopyTo((String[])data, 0);
}
break;
case Win32Native.REG_LINK:
@@ -560,10 +792,26 @@ namespace Microsoft.Win32
private bool IsPerfDataKey()
{
- return false;
+ return (state & STATE_PERF_DATA) != 0;
+ }
+
+ private void SetDirty()
+ {
+ state |= STATE_DIRTY;
+ }
+
+ /**
+ * Sets the specified value.
+ *
+ * @param name Name of value to store data in.
+ * @param value Data to store.
+ */
+ public void SetValue(String name, Object value)
+ {
+ SetValue(name, value, RegistryValueKind.Unknown);
}
- public unsafe void SetStringValue(string name, string value)
+ public unsafe void SetValue(String name, Object value, RegistryValueKind valueKind)
{
if (value == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
@@ -573,17 +821,167 @@ namespace Microsoft.Win32
throw new ArgumentException(SR.Arg_RegValStrLenBug);
}
+ if (!Enum.IsDefined(typeof(RegistryValueKind), valueKind))
+ throw new ArgumentException(SR.Arg_RegBadKeyKind, nameof(valueKind));
+
EnsureWriteable();
- int result = Win32Native.RegSetValueEx(hkey,
- name,
- 0,
- RegistryValueKind.String,
- value,
- checked(value.Length * 2 + 2));
+ if (valueKind == RegistryValueKind.Unknown)
+ {
+ // this is to maintain compatibility with the old way of autodetecting the type.
+ // SetValue(string, object) will come through this codepath.
+ valueKind = CalculateValueKind(value);
+ }
+
+ int ret = 0;
+ try
+ {
+ switch (valueKind)
+ {
+ case RegistryValueKind.ExpandString:
+ case RegistryValueKind.String:
+ {
+ String data = value.ToString();
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ valueKind,
+ data,
+ checked(data.Length * 2 + 2));
+ break;
+ }
+
+ case RegistryValueKind.MultiString:
+ {
+ // Other thread might modify the input array after we calculate the buffer length.
+ // Make a copy of the input array to be safe.
+ string[] dataStrings = (string[])(((string[])value).Clone());
+ int sizeInBytes = 0;
+
+ // First determine the size of the array
+ //
+ for (int i = 0; i < dataStrings.Length; i++)
+ {
+ if (dataStrings[i] == null)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetStrArrNull);
+ }
+ sizeInBytes = checked(sizeInBytes + (dataStrings[i].Length + 1) * 2);
+ }
+ sizeInBytes = checked(sizeInBytes + 2);
+
+ byte[] basePtr = new byte[sizeInBytes];
+ fixed (byte* b = basePtr)
+ {
+ IntPtr currentPtr = new IntPtr((void*)b);
+
+ // Write out the strings...
+ //
+ for (int i = 0; i < dataStrings.Length; i++)
+ {
+ // Assumes that the Strings are always null terminated.
+ String.InternalCopy(dataStrings[i], currentPtr, (checked(dataStrings[i].Length * 2)));
+ currentPtr = new IntPtr((long)currentPtr + (checked(dataStrings[i].Length * 2)));
+ *(char*)(currentPtr.ToPointer()) = '\0';
+ currentPtr = new IntPtr((long)currentPtr + 2);
+ }
+
+ *(char*)(currentPtr.ToPointer()) = '\0';
+ currentPtr = new IntPtr((long)currentPtr + 2);
+
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ RegistryValueKind.MultiString,
+ basePtr,
+ sizeInBytes);
+ }
+ break;
+ }
+
+ case RegistryValueKind.None:
+ case RegistryValueKind.Binary:
+ byte[] dataBytes = (byte[])value;
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ (valueKind == RegistryValueKind.None ? Win32Native.REG_NONE : RegistryValueKind.Binary),
+ dataBytes,
+ dataBytes.Length);
+ break;
+
+ case RegistryValueKind.DWord:
+ {
+ // We need to use Convert here because we could have a boxed type cannot be
+ // unboxed and cast at the same time. I.e. ((int)(object)(short) 5) will fail.
+ int data = Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture);
+
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ RegistryValueKind.DWord,
+ ref data,
+ 4);
+ break;
+ }
- if (result != 0)
- Win32Error(result, null);
+ case RegistryValueKind.QWord:
+ {
+ long data = Convert.ToInt64(value, System.Globalization.CultureInfo.InvariantCulture);
+
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ RegistryValueKind.QWord,
+ ref data,
+ 8);
+ break;
+ }
+ }
+ }
+ catch (OverflowException)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+ catch (InvalidOperationException)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+ catch (FormatException)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+ catch (InvalidCastException)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+
+ if (ret == 0)
+ {
+ SetDirty();
+ }
+ else
+ Win32Error(ret, null);
+ }
+
+ private RegistryValueKind CalculateValueKind(Object value)
+ {
+ // This logic matches what used to be in SetValue(string name, object value) in the v1.0 and v1.1 days.
+ // Even though we could add detection for an int64 in here, we want to maintain compatibility with the
+ // old behavior.
+ if (value is Int32)
+ return RegistryValueKind.DWord;
+ else if (value is Array)
+ {
+ if (value is byte[])
+ return RegistryValueKind.Binary;
+ else if (value is String[])
+ return RegistryValueKind.MultiString;
+ else
+ throw new ArgumentException(SR.Format(SR.Arg_RegSetBadArrType, value.GetType().Name));
+ }
+ else
+ return RegistryValueKind.String;
}
/**
@@ -591,7 +989,7 @@ namespace Microsoft.Win32
*
* @return a string representing the key.
*/
- public override string ToString()
+ public override String ToString()
{
EnsureNotDisposed();
return keyName;
@@ -604,7 +1002,7 @@ namespace Microsoft.Win32
* error, and depending on the error, insert a string into the message
* gotten from the ResourceManager.
*/
- internal void Win32Error(int errorCode, string str)
+ internal void Win32Error(int errorCode, String str)
{
switch (errorCode)
{
@@ -613,6 +1011,27 @@ namespace Microsoft.Win32
throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_RegistryKeyGeneric_Key, str));
else
throw new UnauthorizedAccessException();
+
+ case Win32Native.ERROR_INVALID_HANDLE:
+ /**
+ * For normal RegistryKey instances we dispose the SafeRegHandle and throw IOException.
+ * However, for HKEY_PERFORMANCE_DATA (on a local or remote machine) we avoid disposing the
+ * SafeRegHandle and only throw the IOException. This is to workaround reentrancy issues
+ * in PerformanceCounter.NextValue() where the API could throw {NullReference, ObjectDisposed, ArgumentNull}Exception
+ * on reentrant calls because of this error code path in RegistryKey
+ *
+ * Normally we'd make our caller synchronize access to a shared RegistryKey instead of doing something like this,
+ * however we shipped PerformanceCounter.NextValue() un-synchronized in v2.0RTM and customers have taken a dependency on
+ * this behavior (being able to simultaneously query multiple remote-machine counters on multiple threads, instead of
+ * having serialized access).
+ */
+ if (!IsPerfDataKey())
+ {
+ hkey.SetHandleAsInvalid();
+ hkey = null;
+ }
+ goto default;
+
case Win32Native.ERROR_FILE_NOT_FOUND:
throw new IOException(SR.Arg_RegKeyNotFound, errorCode);
@@ -621,6 +1040,76 @@ namespace Microsoft.Win32
}
}
+ internal static String FixupName(String name)
+ {
+ BCLDebug.Assert(name != null, "[FixupName]name!=null");
+ if (name.IndexOf('\\') == -1)
+ return name;
+
+ StringBuilder sb = new StringBuilder(name);
+ FixupPath(sb);
+ int temp = sb.Length - 1;
+ if (temp >= 0 && sb[temp] == '\\') // Remove trailing slash
+ sb.Length = temp;
+ return sb.ToString();
+ }
+
+
+ private static void FixupPath(StringBuilder path)
+ {
+ Contract.Requires(path != null);
+ int length = path.Length;
+ bool fixup = false;
+ char markerChar = (char)0xFFFF;
+
+ int i = 1;
+ while (i < length - 1)
+ {
+ if (path[i] == '\\')
+ {
+ i++;
+ while (i < length)
+ {
+ if (path[i] == '\\')
+ {
+ path[i] = markerChar;
+ i++;
+ fixup = true;
+ }
+ else
+ break;
+ }
+ }
+ i++;
+ }
+
+ if (fixup)
+ {
+ i = 0;
+ int j = 0;
+ while (i < length)
+ {
+ if (path[i] == markerChar)
+ {
+ i++;
+ continue;
+ }
+ path[j] = path[i];
+ i++;
+ j++;
+ }
+ path.Length += j - i;
+ }
+ }
+
+ private bool ContainsRegistryValue(string name)
+ {
+ int type = 0;
+ int datasize = 0;
+ int retval = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize);
+ return retval == 0;
+ }
+
private void EnsureNotDisposed()
{
if (hkey == null)
@@ -638,8 +1127,41 @@ namespace Microsoft.Win32
}
}
+ private static int GetRegistryKeyAccess(bool isWritable)
+ {
+ int winAccess;
+ if (!isWritable)
+ {
+ winAccess = Win32Native.KEY_READ;
+ }
+ else
+ {
+ winAccess = Win32Native.KEY_READ | Win32Native.KEY_WRITE;
+ }
+
+ return winAccess;
+ }
+
+ private RegistryKeyPermissionCheck GetSubKeyPermissonCheck(bool subkeyWritable)
+ {
+ if (checkMode == RegistryKeyPermissionCheck.Default)
+ {
+ return checkMode;
+ }
+
+ if (subkeyWritable)
+ {
+ return RegistryKeyPermissionCheck.ReadWriteSubTree;
+ }
+ else
+ {
+ return RegistryKeyPermissionCheck.ReadSubTree;
+ }
+ }
+
static private void ValidateKeyName(string name)
{
+ Contract.Ensures(name != null);
if (name == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.name);
@@ -660,9 +1182,33 @@ namespace Microsoft.Win32
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyStrLenBug);
}
+ static private void ValidateKeyView(RegistryView view)
+ {
+ if (view != RegistryView.Default && view != RegistryView.Registry32 && view != RegistryView.Registry64)
+ {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidRegistryViewCheck, ExceptionArgument.view);
+ }
+ }
+
// Win32 constants for error handling
private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
}
+
+ [Flags]
+ internal enum RegistryValueOptions
+ {
+ None = 0,
+ DoNotExpandEnvironmentNames = 1
+ }
+
+ // the name for this API is meant to mimic FileMode, which has similar values
+
+ internal enum RegistryKeyPermissionCheck
+ {
+ Default = 0,
+ ReadSubTree = 1,
+ ReadWriteSubTree = 2
+ }
}
diff --git a/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs b/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs
index 90c880b..6e2a4f3 100644
--- a/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs
+++ b/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+
namespace Microsoft.Win32
{
internal enum RegistryValueKind
@@ -12,11 +13,8 @@ namespace Microsoft.Win32
DWord = Win32Native.REG_DWORD,
MultiString = Win32Native.REG_MULTI_SZ,
QWord = Win32Native.REG_QWORD,
- Unknown = 0,
-
- // REG_NONE is defined as zero but BCL, mistakingly overrode this value.
- // Now instead of using Win32Native.REG_NONE we use "-1" and play games internally.
- None = unchecked((int)0xFFFFFFFF),
- }
+ Unknown = 0, // REG_NONE is defined as zero but BCL
+ None = unchecked((int)0xFFFFFFFF), // mistakingly overrode this value.
+ } // Now instead of using Win32Native.REG_NONE we use "-1" and play games internally.
}
diff --git a/src/mscorlib/src/Microsoft/Win32/RegistryView.cs b/src/mscorlib/src/Microsoft/Win32/RegistryView.cs
new file mode 100644
index 0000000..e415865
--- /dev/null
+++ b/src/mscorlib/src/Microsoft/Win32/RegistryView.cs
@@ -0,0 +1,22 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+//
+//
+// Implements Microsoft.Win32.RegistryView
+//
+// ======================================================================================
+
+using System;
+
+namespace Microsoft.Win32
+{
+ internal enum RegistryView
+ {
+ Default = 0, // 0x0000 operate on the default registry view
+ Registry64 = Win32Native.KEY_WOW64_64KEY, // 0x0100 operate on the 64-bit registry view
+ Registry32 = Win32Native.KEY_WOW64_32KEY, // 0x0200 operate on the 32-bit registry view
+ };
+}
diff --git a/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs b/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs
index 1215000..64dbb2c 100644
--- a/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs
+++ b/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs
@@ -2,9 +2,19 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+//
+//
+//
+// Implements Microsoft.Win32.SafeHandles.SafeRegistryHandle
+//
+// ======================================================================================
+
using System;
using System.Security;
using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+using System.Runtime.ConstrainedExecution;
+using System.Runtime.Versioning;
namespace Microsoft.Win32.SafeHandles
{
diff --git a/src/mscorlib/src/System/Collections/Generic/Dictionary.cs b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
index 409b23b..e360eef 100644
--- a/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
+++ b/src/mscorlib/src/System/Collections/Generic/Dictionary.cs
@@ -48,6 +48,7 @@ namespace System.Collections.Generic
using System.Collections;
using System.Diagnostics;
using System.Diagnostics.Contracts;
+ using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
/// <summary>
@@ -428,8 +429,7 @@ namespace System.Collections.Generic
if (buckets == null) Initialize(0);
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
- int targetBucket = hashCode % buckets.Length;
-
+ int targetBucket = hashCode % buckets.Length;
#if FEATURE_RANDOMIZED_STRING_HASHING
int collisionCount = 0;
#endif
@@ -452,7 +452,6 @@ namespace System.Collections.Generic
return false;
}
-
#if FEATURE_RANDOMIZED_STRING_HASHING
collisionCount++;
#endif
@@ -599,27 +598,40 @@ namespace System.Collections.Generic
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int bucket = hashCode % buckets.Length;
int last = -1;
- for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next)
+ int i = buckets[bucket];
+ while (i >= 0)
{
- if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
+ ref Entry entry = ref entries[i];
+
+ if (entry.hashCode == hashCode && comparer.Equals(entry.key, key))
{
if (last < 0)
{
- buckets[bucket] = entries[i].next;
+ buckets[bucket] = entry.next;
}
else
{
- entries[last].next = entries[i].next;
+ entries[last].next = entry.next;
+ }
+ entry.hashCode = -1;
+ entry.next = freeList;
+
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
+ {
+ entry.key = default(TKey);
+ }
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
+ {
+ entry.value = default(TValue);
}
- entries[i].hashCode = -1;
- entries[i].next = freeList;
- entries[i].key = default(TKey);
- entries[i].value = default(TValue);
freeList = i;
freeCount++;
version++;
return true;
}
+
+ last = i;
+ i = entry.next;
}
}
return false;
@@ -640,30 +652,43 @@ namespace System.Collections.Generic
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
int bucket = hashCode % buckets.Length;
int last = -1;
- for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next)
+ int i = buckets[bucket];
+ while (i >= 0)
{
- if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
+ ref Entry entry = ref entries[i];
+
+ if (entry.hashCode == hashCode && comparer.Equals(entry.key, key))
{
if (last < 0)
{
- buckets[bucket] = entries[i].next;
+ buckets[bucket] = entry.next;
}
else
{
- entries[last].next = entries[i].next;
+ entries[last].next = entry.next;
}
- value = entries[i].value;
+ value = entry.value;
- entries[i].hashCode = -1;
- entries[i].next = freeList;
- entries[i].key = default(TKey);
- entries[i].value = default(TValue);
+ entry.hashCode = -1;
+ entry.next = freeList;
+
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TKey>())
+ {
+ entry.key = default(TKey);
+ }
+ if (RuntimeHelpers.IsReferenceOrContainsReferences<TValue>())
+ {
+ entry.value = default(TValue);
+ }
freeList = i;
freeCount++;
version++;
return true;
}
+
+ last = i;
+ i = entry.next;
}
}
value = default(TValue);
@@ -955,13 +980,13 @@ namespace System.Collections.Generic
// dictionary.count+1 could be negative if dictionary.count is Int32.MaxValue
while ((uint)index < (uint)dictionary.count)
{
- if (dictionary.entries[index].hashCode >= 0)
+ ref Entry entry = ref dictionary.entries[index++];
+
+ if (entry.hashCode >= 0)
{
- current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);
- index++;
+ current = new KeyValuePair<TKey, TValue>(entry.key, entry.value);
return true;
}
- index++;
}
index = dictionary.count + 1;
@@ -1231,13 +1256,13 @@ namespace System.Collections.Generic
while ((uint)index < (uint)dictionary.count)
{
- if (dictionary.entries[index].hashCode >= 0)
+ ref Entry entry = ref dictionary.entries[index++];
+
+ if (entry.hashCode >= 0)
{
- currentKey = dictionary.entries[index].key;
- index++;
+ currentKey = entry.key;
return true;
}
- index++;
}
index = dictionary.count + 1;
@@ -1459,13 +1484,13 @@ namespace System.Collections.Generic
while ((uint)index < (uint)dictionary.count)
{
- if (dictionary.entries[index].hashCode >= 0)
+ ref Entry entry = ref dictionary.entries[index++];
+
+ if (entry.hashCode >= 0)
{
- currentValue = dictionary.entries[index].value;
- index++;
+ currentValue = entry.value;
return true;
}
- index++;
}
index = dictionary.count + 1;
currentValue = default(TValue);
diff --git a/src/mscorlib/src/System/Collections/Generic/List.cs b/src/mscorlib/src/System/Collections/Generic/List.cs
index 67d1668..4e48088 100644
--- a/src/mscorlib/src/System/Collections/Generic/List.cs
+++ b/src/mscorlib/src/System/Collections/Generic/List.cs
@@ -350,6 +350,7 @@ namespace System.Collections.Generic
// Clears the contents of List.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
diff --git a/src/mscorlib/src/System/Delegate.cs b/src/mscorlib/src/System/Delegate.cs
index de0ff65..75ec57a 100644
--- a/src/mscorlib/src/System/Delegate.cs
+++ b/src/mscorlib/src/System/Delegate.cs
@@ -179,7 +179,10 @@ namespace System
else
return unchecked((int)((long)this._methodPtrAux));
*/
- return GetType().GetHashCode();
+ if (_methodPtrAux.IsNull())
+ return ( _target != null ? RuntimeHelpers.GetHashCode(_target) * 33 : 0) + GetType().GetHashCode();
+ else
+ return GetType().GetHashCode();
}
public static Delegate Combine(Delegate a, Delegate b)
diff --git a/src/mscorlib/src/System/Environment.cs b/src/mscorlib/src/System/Environment.cs
index b9070ae..dddbdc2 100644
--- a/src/mscorlib/src/System/Environment.cs
+++ b/src/mscorlib/src/System/Environment.cs
@@ -820,7 +820,7 @@ namespace System
}
else
{
- environmentKey.SetStringValue(variable, value);
+ environmentKey.SetValue(variable, value);
}
}
}
diff --git a/src/mscorlib/src/System/Globalization/CompareInfo.cs b/src/mscorlib/src/System/Globalization/CompareInfo.cs
index 285a81d..b2c2208 100644
--- a/src/mscorlib/src/System/Globalization/CompareInfo.cs
+++ b/src/mscorlib/src/System/Globalization/CompareInfo.cs
@@ -752,7 +752,6 @@ namespace System.Globalization
return IndexOfCore(source, new string(value, 1), startIndex, count, options, null);
}
-
public unsafe virtual int IndexOf(string source, string value, int startIndex, int count, CompareOptions options)
{
// Validate inputs
@@ -802,6 +801,53 @@ namespace System.Globalization
return IndexOfCore(source, value, startIndex, count, options, null);
}
+ // The following IndexOf overload is mainly used by String.Replace. This overload assumes the parameters are already validated
+ // and the caller is passing a valid matchLengthPtr pointer.
+ internal unsafe int IndexOf(string source, string value, int startIndex, int count, CompareOptions options, int* matchLengthPtr)
+ {
+ Debug.Assert(source != null);
+ Debug.Assert(value != null);
+ Debug.Assert(startIndex >= 0);
+ Debug.Assert(matchLengthPtr != null);
+ *matchLengthPtr = 0;
+
+ if (source.Length == 0)
+ {
+ if (value.Length == 0)
+ {
+ return 0;
+ }
+ return -1;
+ }
+
+ if (startIndex >= source.Length)
+ {
+ return -1;
+ }
+
+ if (options == CompareOptions.OrdinalIgnoreCase)
+ {
+ int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: true);
+ if (res >= 0)
+ {
+ *matchLengthPtr = value.Length;
+ }
+ return res;
+ }
+
+ if (_invariantMode)
+ {
+ int res = IndexOfOrdinal(source, value, startIndex, count, ignoreCase: (options & (CompareOptions.IgnoreCase | CompareOptions.OrdinalIgnoreCase)) != 0);
+ if (res >= 0)
+ {
+ *matchLengthPtr = value.Length;
+ }
+ return res;
+ }
+
+ return IndexOfCore(source, value, startIndex, count, options, matchLengthPtr);
+ }
+
internal int IndexOfOrdinal(string source, string value, int startIndex, int count, bool ignoreCase)
{
if (_invariantMode)
diff --git a/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs b/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs
index 4ba95c8..869b809 100644
--- a/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs
+++ b/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs
@@ -42,41 +42,54 @@ namespace System.Globalization
int hijriAdvance = 0;
Microsoft.Win32.RegistryKey key = null;
- using (key = Registry.CurrentUser.OpenSubKey(InternationalRegKey, writable: false))
+ try
{
- if (key == null)
- return 0;
+ // Open in read-only mode.
+ // Use InternalOpenSubKey so that we avoid the security check.
+ key = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER).OpenSubKey(InternationalRegKey, false);
+ }
+ //If this fails for any reason, we'll just return 0.
+ catch (ObjectDisposedException) { return 0; }
+ catch (ArgumentException) { return 0; }
- Object value = key.GetValue(HijriAdvanceRegKeyEntry);
- if (value == null)
- {
- return (0);
- }
- String str = value.ToString();
- if (String.Compare(str, 0, HijriAdvanceRegKeyEntry, 0, HijriAdvanceRegKeyEntry.Length, StringComparison.OrdinalIgnoreCase) == 0)
+ if (key != null)
+ {
+ try
{
- if (str.Length == HijriAdvanceRegKeyEntry.Length)
- hijriAdvance = -1;
- else
+ Object value = key.InternalGetValue(HijriAdvanceRegKeyEntry, null, false, false);
+ if (value == null)
{
- str = str.Substring(HijriAdvanceRegKeyEntry.Length);
- try
+ return (0);
+ }
+ String str = value.ToString();
+ if (String.Compare(str, 0, HijriAdvanceRegKeyEntry, 0, HijriAdvanceRegKeyEntry.Length, StringComparison.OrdinalIgnoreCase) == 0)
+ {
+ if (str.Length == HijriAdvanceRegKeyEntry.Length)
+ hijriAdvance = -1;
+ else
{
- int advance = Int32.Parse(str.ToString(), CultureInfo.InvariantCulture);
- if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri))
+ str = str.Substring(HijriAdvanceRegKeyEntry.Length);
+ try
{
- hijriAdvance = advance;
+ int advance = Int32.Parse(str.ToString(), CultureInfo.InvariantCulture);
+ if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri))
+ {
+ hijriAdvance = advance;
+ }
}
+ // If we got garbage from registry just ignore it.
+ // hijriAdvance = 0 because of declaraction assignment up above.
+ catch (ArgumentException) { }
+ catch (FormatException) { }
+ catch (OverflowException) { }
}
- // If we got garbage from registry just ignore it.
- // hijriAdvance = 0 because of declaraction assignment up above.
- catch (ArgumentException) { }
- catch (FormatException) { }
- catch (OverflowException) { }
}
}
+ finally
+ {
+ key.Close();
+ }
}
-
return (hijriAdvance);
}
}
diff --git a/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs b/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs
index fe8b1b5..a83c4fa 100644
--- a/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs
+++ b/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs
@@ -36,30 +36,30 @@ namespace System.Globalization
try
{
- using (RegistryKey key = Registry.LocalMachine.OpenSubKey(c_japaneseErasHive, writable: false))
+ // Need to access registry
+ RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(c_japaneseErasHive, false);
+
+ // Abort if we didn't find anything
+ if (key == null) return null;
+
+ // Look up the values in our reg key
+ String[] valueNames = key.GetValueNames();
+ if (valueNames != null && valueNames.Length > 0)
{
- // Abort if we didn't find anything
- if (key == null) return null;
+ registryEraRanges = new EraInfo[valueNames.Length];
- // Look up the values in our reg key
- String[] valueNames = key.GetValueNames();
- if (valueNames != null && valueNames.Length > 0)
+ // Loop through the registry and read in all the values
+ for (int i = 0; i < valueNames.Length; i++)
{
- registryEraRanges = new EraInfo[valueNames.Length];
-
- // Loop through the registry and read in all the values
- for (int i = 0; i < valueNames.Length; i++)
- {
- // See if the era is a valid date
- EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString());
+ // See if the era is a valid date
+ EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString());
- // continue if not valid
- if (era == null) continue;
+ // continue if not valid
+ if (era == null) continue;
- // Remember we found one.
- registryEraRanges[iFoundEras] = era;
- iFoundEras++;
- }
+ // Remember we found one.
+ registryEraRanges[iFoundEras] = era;
+ iFoundEras++;
}
}
}
diff --git a/src/mscorlib/src/System/IO/Stream.cs b/src/mscorlib/src/System/IO/Stream.cs
index 92fe374..65cc4dd 100644
--- a/src/mscorlib/src/System/IO/Stream.cs
+++ b/src/mscorlib/src/System/IO/Stream.cs
@@ -522,14 +522,14 @@ namespace System.IO
// If the wait has already completed, run the task.
if (asyncWaiter.IsCompleted)
{
- Debug.Assert(asyncWaiter.IsRanToCompletion, "The semaphore wait should always complete successfully.");
+ Debug.Assert(asyncWaiter.IsCompletedSuccessfully, "The semaphore wait should always complete successfully.");
RunReadWriteTask(readWriteTask);
}
else // Otherwise, wait for our turn, and then run the task.
{
asyncWaiter.ContinueWith((t, state) =>
{
- Debug.Assert(t.IsRanToCompletion, "The semaphore wait should always complete successfully.");
+ Debug.Assert(t.IsCompletedSuccessfully, "The semaphore wait should always complete successfully.");
var rwt = (ReadWriteTask)state;
rwt._stream.RunReadWriteTask(rwt); // RunReadWriteTask(readWriteTask);
}, readWriteTask, default(CancellationToken), TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
diff --git a/src/mscorlib/src/System/ReadOnlySpan.cs b/src/mscorlib/src/System/ReadOnlySpan.cs
deleted file mode 100644
index 8d0fbad..0000000
--- a/src/mscorlib/src/System/ReadOnlySpan.cs
+++ /dev/null
@@ -1,313 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Runtime.CompilerServices;
-using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
-using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
-
-#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
-
-namespace System
-{
- /// <summary>
- /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
- /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
- /// </summary>
- public struct ReadOnlySpan<T>
- {
- /// <summary>A byref or a native ptr.</summary>
- private readonly ByReference<T> _pointer;
- /// <summary>The number of elements this ReadOnlySpan contains.</summary>
- private readonly int _length;
-
- /// <summary>
- /// Creates a new read-only span over the entirety of the target array.
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan(T[] array)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
-
- _pointer = new ByReference<T>(ref JitHelpers.GetArrayData(array));
- _length = array.Length;
- }
-
- /// <summary>
- /// Creates a new read-only span over the portion of the target array beginning
- /// at 'start' index and covering the remainder of the array.
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <param name="start">The index at which to begin the read-only span.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> is not in the range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan(T[] array, int start)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if ((uint)start > (uint)array.Length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
- _length = array.Length - start;
- }
-
- /// <summary>
- /// Creates a new read-only span over the portion of the target array beginning
- /// at 'start' index and ending at 'end' index (exclusive).
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <param name="start">The index at which to begin the read-only span.</param>
- /// <param name="length">The number of items in the read-only span.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan(T[] array, int start, int length)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
- _length = length;
- }
-
- /// <summary>
- /// Creates a new read-only span over the target unmanaged buffer. Clearly this
- /// is quite dangerous, because we are creating arbitrarily typed T's
- /// out of a void*-typed block of memory. And the length is not checked.
- /// But if this creation is correct, then all subsequent uses are correct.
- /// </summary>
- /// <param name="pointer">An unmanaged pointer to memory.</param>
- /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
- /// </exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="length"/> is negative.
- /// </exception>
- [CLSCompliant(false)]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public unsafe ReadOnlySpan(void* pointer, int length)
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
- if (length < 0)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
- _length = length;
- }
-
- /// <summary>
- /// Create a new read-only span over a portion of a regular managed object. This can be useful
- /// if part of a managed object represents a "fixed array." This is dangerous because neither the
- /// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that
- /// "rawPointer" actually lies within <paramref name="obj"/>.
- /// </summary>
- /// <param name="obj">The managed object that contains the data to span over.</param>
- /// <param name="objectData">A reference to data within that object.</param>
- /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<T> DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan<T>(ref objectData, length);
-
- // Constructor for internal use only.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal ReadOnlySpan(ref T ptr, int length)
- {
- Debug.Assert(length >= 0);
-
- _pointer = new ByReference<T>(ref ptr);
- _length = length;
- }
-
- /// <summary>
- /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
- /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ref T DangerousGetPinnableReference()
- {
- return ref _pointer.Value;
- }
-
- /// <summary>
- /// The number of items in the read-only span.
- /// </summary>
- public int Length => _length;
-
- /// <summary>
- /// Returns true if Length is 0.
- /// </summary>
- public bool IsEmpty => _length == 0;
-
- /// <summary>
- /// Returns the specified element of the read-only span.
- /// </summary>
- /// <param name="index"></param>
- /// <returns></returns>
- /// <exception cref="System.IndexOutOfRangeException">
- /// Thrown when index less than 0 or index greater than or equal to Length
- /// </exception>
- public T this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- if ((uint)index >= (uint)_length)
- ThrowHelper.ThrowIndexOutOfRangeException();
-
- return Unsafe.Add(ref _pointer.Value, index);
- }
- }
-
- /// <summary>
- /// Copies the contents of this read-only span into destination span. If the source
- /// and destinations overlap, this method behaves as if the original values in
- /// a temporary location before the destination is overwritten.
- ///
- /// <param name="destination">The span to copy items into.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when the destination Span is shorter than the source Span.
- /// </exception>
- /// </summary>
- public void CopyTo(Span<T> destination)
- {
- if (!TryCopyTo(destination))
- ThrowHelper.ThrowArgumentException_DestinationTooShort();
- }
-
- /// Copies the contents of this read-only span into destination span. If the source
- /// and destinations overlap, this method behaves as if the original values in
- /// a temporary location before the destination is overwritten.
- /// </summary>
- /// <returns>If the destination span is shorter than the source span, this method
- /// return false and no data is written to the destination.</returns>
- /// <param name="destination">The span to copy items into.</param>
- public bool TryCopyTo(Span<T> destination)
- {
- if ((uint)_length > (uint)destination.Length)
- return false;
-
- SpanHelper.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, _length);
- return true;
- }
-
- /// <summary>
- /// Returns true if left and right point at the same memory and have the same length. Note that
- /// this does *not* check to see if the *contents* are equal.
- /// </summary>
- public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right)
- {
- return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
- }
-
- /// <summary>
- /// Returns false if left and right point at the same memory and have the same length. Note that
- /// this does *not* check to see if the *contents* are equal.
- /// </summary>
- public static bool operator !=(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => !(left == right);
-
- /// <summary>
- /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==.
- /// <exception cref="System.NotSupportedException">
- /// Always thrown by this method.
- /// </exception>
- /// </summary>
- [Obsolete("Equals() on Span will always throw an exception. Use == instead.")]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override bool Equals(object obj)
- {
- throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan);
- }
-
- /// <summary>
- /// This method is not supported as spans cannot be boxed.
- /// <exception cref="System.NotSupportedException">
- /// Always thrown by this method.
- /// </exception>
- /// </summary>
- [Obsolete("GetHashCode() on Span will always throw an exception.")]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override int GetHashCode()
- {
- throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan);
- }
-
- /// <summary>
- /// Defines an implicit conversion of an array to a <see cref="ReadOnlySpan{T}"/>
- /// </summary>
- public static implicit operator ReadOnlySpan<T>(T[] array) => new ReadOnlySpan<T>(array);
-
- /// <summary>
- /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="ReadOnlySpan{T}"/>
- /// </summary>
- public static implicit operator ReadOnlySpan<T>(ArraySegment<T> arraySegment) => new ReadOnlySpan<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- /// <summary>
- /// Forms a slice out of the given read-only span, beginning at 'start'.
- /// </summary>
- /// <param name="start">The index at which to begin this slice.</param>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan<T> Slice(int start)
- {
- if ((uint)start > (uint)_length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
- }
-
- /// <summary>
- /// Forms a slice out of the given read-only span, beginning at 'start', of given length
- /// </summary>
- /// <param name="start">The index at which to begin this slice.</param>
- /// <param name="length">The desired length for the slice (exclusive).</param>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ReadOnlySpan<T> Slice(int start, int length)
- {
- if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
- }
-
- /// <summary>
- /// Copies the contents of this read-only span into a new array. This heap
- /// allocates, so should generally be avoided, however it is sometimes
- /// necessary to bridge the gap with APIs written in terms of arrays.
- /// </summary>
- public T[] ToArray()
- {
- if (_length == 0)
- return Array.Empty<T>();
-
- var destination = new T[_length];
- SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref _pointer.Value, _length);
- return destination;
- }
-
- /// <summary>
- /// Returns a 0-length read-only span whose base is the null pointer.
- /// </summary>
- public static ReadOnlySpan<T> Empty => default(ReadOnlySpan<T>);
- }
-}
diff --git a/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs b/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs
index 82966db..708f79b 100644
--- a/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs
+++ b/src/mscorlib/src/System/Reflection/Assembly.CoreCLR.cs
@@ -101,14 +101,20 @@ namespace System.Reflection
{
Contract.Ensures(Contract.Result<Assembly>() != null);
Contract.Ensures(!Contract.Result<Assembly>().ReflectionOnly);
-
+
+ AssemblyName modifiedAssemblyRef = null;
if (assemblyRef != null && assemblyRef.CodeBase != null)
{
- throw new NotSupportedException(SR.NotSupported_AssemblyLoadCodeBase);
+ modifiedAssemblyRef = (AssemblyName)assemblyRef.Clone();
+ modifiedAssemblyRef.CodeBase = null;
}
-
+ else
+ {
+ modifiedAssemblyRef = assemblyRef;
+ }
+
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return RuntimeAssembly.InternalLoadAssemblyName(assemblyRef, null, null, ref stackMark, true /*thrownOnFileNotFound*/, false /*forIntrospection*/);
+ return RuntimeAssembly.InternalLoadAssemblyName(modifiedAssemblyRef, null, null, ref stackMark, true /*thrownOnFileNotFound*/, false /*forIntrospection*/);
}
// Locate an assembly by its name. The name can be strong or
@@ -119,13 +125,19 @@ namespace System.Reflection
Contract.Ensures(Contract.Result<Assembly>() != null);
Contract.Ensures(!Contract.Result<Assembly>().ReflectionOnly);
+ AssemblyName modifiedAssemblyRef = null;
if (assemblyRef != null && assemblyRef.CodeBase != null)
{
- throw new NotSupportedException(SR.NotSupported_AssemblyLoadCodeBase);
+ modifiedAssemblyRef = (AssemblyName)assemblyRef.Clone();
+ modifiedAssemblyRef.CodeBase = null;
+ }
+ else
+ {
+ modifiedAssemblyRef = assemblyRef;
}
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
- return RuntimeAssembly.InternalLoadAssemblyName(assemblyRef, null, null, ref stackMark, true /*thrownOnFileNotFound*/, false /*forIntrospection*/, ptrLoadContextBinder);
+ return RuntimeAssembly.InternalLoadAssemblyName(modifiedAssemblyRef, null, null, ref stackMark, true /*thrownOnFileNotFound*/, false /*forIntrospection*/, ptrLoadContextBinder);
}
// Loads the assembly with a COFF based IMAGE containing
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs b/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
index e2fa6ca..c35658e 100644
--- a/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
+++ b/src/mscorlib/src/System/Runtime/CompilerServices/TaskAwaiter.cs
@@ -146,7 +146,7 @@ namespace System.Runtime.CompilerServices
task.NotifyDebuggerOfWaitCompletionIfNecessary();
// And throw an exception if the task is faulted or canceled.
- if (!task.IsRanToCompletion) ThrowForNonSuccess(task);
+ if (!task.IsCompletedSuccessfully) ThrowForNonSuccess(task);
}
/// <summary>Throws an exception to handle a task that completed in a state other than RanToCompletion.</summary>
diff --git a/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs b/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs
index b86835f..d520135 100644
--- a/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs
+++ b/src/mscorlib/src/System/Runtime/CompilerServices/jithelpers.cs
@@ -216,14 +216,7 @@ namespace System.Runtime.CompilerServices
private extern static bool IsAddressInStack(IntPtr ptr);
#endif
- static internal bool ByRefLessThan<T>(ref T refA, ref T refB)
- {
- // The body of this function will be replaced by the EE with unsafe code!!!
- // See getILIntrinsicImplementation for how this happens.
- throw new InvalidOperationException();
- }
-
- static internal ref T GetArrayData<T>(T[] array)
+ static internal ref byte GetRawSzArrayData(this Array array)
{
// The body of this function will be replaced by the EE with unsafe code!!!
// See getILIntrinsicImplementation for how this happens.
diff --git a/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs b/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs
index 248e0d5..6fb6311 100644
--- a/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs
+++ b/src/mscorlib/src/System/Runtime/InteropServices/Marshal.cs
@@ -824,6 +824,9 @@ namespace System.Runtime.InteropServices
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
+ public static extern /* struct _EXCEPTION_POINTERS* */ IntPtr GetExceptionPointers();
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
public static extern int GetExceptionCode();
diff --git a/src/mscorlib/src/System/Span.cs b/src/mscorlib/src/System/Span.cs
deleted file mode 100644
index 4211083..0000000
--- a/src/mscorlib/src/System/Span.cs
+++ /dev/null
@@ -1,910 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-using System.Diagnostics;
-using System.Runtime;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
-using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
-
-#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
-
-#if BIT64
-using nuint = System.UInt64;
-#else
-using nuint = System.UInt32;
-#endif
-
-namespace System
-{
- /// <summary>
- /// Span represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
- /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
- /// </summary>
- public struct Span<T>
- {
- /// <summary>A byref or a native ptr.</summary>
- private readonly ByReference<T> _pointer;
- /// <summary>The number of elements this Span contains.</summary>
- private readonly int _length;
-
- /// <summary>
- /// Creates a new span over the entirety of the target array.
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span(T[] array)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if (default(T) == null && array.GetType() != typeof(T[]))
- ThrowHelper.ThrowArrayTypeMismatchException();
-
- _pointer = new ByReference<T>(ref JitHelpers.GetArrayData(array));
- _length = array.Length;
- }
-
- /// <summary>
- /// Creates a new span over the portion of the target array beginning
- /// at 'start' index and covering the remainder of the array.
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <param name="start">The index at which to begin the span.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> is not in the range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span(T[] array, int start)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if (default(T) == null && array.GetType() != typeof(T[]))
- ThrowHelper.ThrowArrayTypeMismatchException();
- if ((uint)start > (uint)array.Length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
- _length = array.Length - start;
- }
-
- /// <summary>
- /// Creates a new span over the portion of the target array beginning
- /// at 'start' index and ending at 'end' index (exclusive).
- /// </summary>
- /// <param name="array">The target array.</param>
- /// <param name="start">The index at which to begin the span.</param>
- /// <param name="length">The number of items in the span.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span(T[] array, int start, int length)
- {
- if (array == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
- if (default(T) == null && array.GetType() != typeof(T[]))
- ThrowHelper.ThrowArrayTypeMismatchException();
- if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start));
- _length = length;
- }
-
- /// <summary>
- /// Creates a new span over the target unmanaged buffer. Clearly this
- /// is quite dangerous, because we are creating arbitrarily typed T's
- /// out of a void*-typed block of memory. And the length is not checked.
- /// But if this creation is correct, then all subsequent uses are correct.
- /// </summary>
- /// <param name="pointer">An unmanaged pointer to memory.</param>
- /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
- /// </exception>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="length"/> is negative.
- /// </exception>
- [CLSCompliant(false)]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public unsafe Span(void* pointer, int length)
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
- if (length < 0)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
- _length = length;
- }
-
- /// <summary>
- /// Create a new span over a portion of a regular managed object. This can be useful
- /// if part of a managed object represents a "fixed array." This is dangerous because neither the
- /// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that
- /// "rawPointer" actually lies within <paramref name="obj"/>.
- /// </summary>
- /// <param name="obj">The managed object that contains the data to span over.</param>
- /// <param name="objectData">A reference to data within that object.</param>
- /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Span<T> DangerousCreate(object obj, ref T objectData, int length) => new Span<T>(ref objectData, length);
-
- // Constructor for internal use only.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal Span(ref T ptr, int length)
- {
- Debug.Assert(length >= 0);
-
- _pointer = new ByReference<T>(ref ptr);
- _length = length;
- }
-
- /// <summary>
- /// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
- /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
- /// </summary>
- public ref T DangerousGetPinnableReference()
- {
- return ref _pointer.Value;
- }
-
- /// <summary>
- /// The number of items in the span.
- /// </summary>
- public int Length => _length;
-
- /// <summary>
- /// Returns true if Length is 0.
- /// </summary>
- public bool IsEmpty => _length == 0;
-
- /// <summary>
- /// Returns a reference to specified element of the Span.
- /// </summary>
- /// <param name="index"></param>
- /// <returns></returns>
- /// <exception cref="System.IndexOutOfRangeException">
- /// Thrown when index less than 0 or index greater than or equal to Length
- /// </exception>
- public ref T this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- if ((uint)index >= (uint)_length)
- ThrowHelper.ThrowIndexOutOfRangeException();
-
- return ref Unsafe.Add(ref _pointer.Value, index);
- }
- }
-
- /// <summary>
- /// Clears the contents of this span.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Clear()
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- {
- SpanHelper.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf<T>() / sizeof(nuint)));
- }
- else
- {
- SpanHelper.ClearWithoutReferences(ref Unsafe.As<T, byte>(ref _pointer.Value), (nuint)_length * (nuint)Unsafe.SizeOf<T>());
- }
- }
-
- /// <summary>
- /// Fills the contents of this span with the given value.
- /// </summary>
- public void Fill(T value)
- {
- if (Unsafe.SizeOf<T>() == 1)
- {
- uint length = (uint)_length;
- if (length == 0)
- return;
-
- T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loop below.
- Unsafe.InitBlockUnaligned(ref Unsafe.As<T, byte>(ref _pointer.Value), Unsafe.As<T, byte>(ref tmp), length);
- }
- else
- {
- // Do all math as nuint to avoid unnecessary 64->32->64 bit integer truncations
- nuint length = (uint)_length;
- if (length == 0)
- return;
-
- ref T r = ref DangerousGetPinnableReference();
-
- // TODO: Create block fill for value types of power of two sizes e.g. 2,4,8,16
-
- nuint elementSize = (uint)Unsafe.SizeOf<T>();
- nuint i = 0;
- for (; i < (length & ~(nuint)7); i += 8)
- {
- Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 4) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 5) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 6) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 7) * elementSize) = value;
- }
- if (i < (length & ~(nuint)3))
- {
- Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
- Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
- i += 4;
- }
- for (; i < length; i++)
- {
- Unsafe.AddByteOffset<T>(ref r, i * elementSize) = value;
- }
- }
- }
-
- /// <summary>
- /// Copies the contents of this span into destination span. If the source
- /// and destinations overlap, this method behaves as if the original values in
- /// a temporary location before the destination is overwritten.
- /// </summary>
- /// <param name="destination">The span to copy items into.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when the destination Span is shorter than the source Span.
- /// </exception>
- public void CopyTo(Span<T> destination)
- {
- if (!TryCopyTo(destination))
- ThrowHelper.ThrowArgumentException_DestinationTooShort();
- }
-
- /// <summary>
- /// Copies the contents of this span into destination span. If the source
- /// and destinations overlap, this method behaves as if the original values in
- /// a temporary location before the destination is overwritten.
- /// </summary>
- /// <param name="destination">The span to copy items into.</param>
- /// <returns>If the destination span is shorter than the source span, this method
- /// return false and no data is written to the destination.</returns>
- public bool TryCopyTo(Span<T> destination)
- {
- if ((uint)_length > (uint)destination.Length)
- return false;
-
- SpanHelper.CopyTo<T>(ref destination._pointer.Value, ref _pointer.Value, _length);
- return true;
- }
-
- /// <summary>
- /// Returns true if left and right point at the same memory and have the same length. Note that
- /// this does *not* check to see if the *contents* are equal.
- /// </summary>
- public static bool operator ==(Span<T> left, Span<T> right)
- {
- return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
- }
-
- /// <summary>
- /// Returns false if left and right point at the same memory and have the same length. Note that
- /// this does *not* check to see if the *contents* are equal.
- /// </summary>
- public static bool operator !=(Span<T> left, Span<T> right) => !(left == right);
-
- /// <summary>
- /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==.
- /// <exception cref="System.NotSupportedException">
- /// Always thrown by this method.
- /// </exception>
- /// </summary>
- [Obsolete("Equals() on Span will always throw an exception. Use == instead.")]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override bool Equals(object obj)
- {
- throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan);
- }
-
- /// <summary>
- /// This method is not supported as spans cannot be boxed.
- /// <exception cref="System.NotSupportedException">
- /// Always thrown by this method.
- /// </exception>
- /// </summary>
- [Obsolete("GetHashCode() on Span will always throw an exception.")]
- [EditorBrowsable(EditorBrowsableState.Never)]
- public override int GetHashCode()
- {
- throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan);
- }
-
- /// <summary>
- /// Defines an implicit conversion of an array to a <see cref="Span{T}"/>
- /// </summary>
- public static implicit operator Span<T>(T[] array) => new Span<T>(array);
-
- /// <summary>
- /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Span{T}"/>
- /// </summary>
- public static implicit operator Span<T>(ArraySegment<T> arraySegment) => new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
-
- /// <summary>
- /// Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpan{T}"/>
- /// </summary>
- public static implicit operator ReadOnlySpan<T>(Span<T> span) => new ReadOnlySpan<T>(ref span._pointer.Value, span._length);
-
- /// <summary>
- /// Forms a slice out of the given span, beginning at 'start'.
- /// </summary>
- /// <param name="start">The index at which to begin this slice.</param>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span<T> Slice(int start)
- {
- if ((uint)start > (uint)_length)
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
- }
-
- /// <summary>
- /// Forms a slice out of the given span, beginning at 'start', of given length
- /// </summary>
- /// <param name="start">The index at which to begin this slice.</param>
- /// <param name="length">The desired length for the slice (exclusive).</param>
- /// <exception cref="System.ArgumentOutOfRangeException">
- /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Span<T> Slice(int start, int length)
- {
- if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
- ThrowHelper.ThrowArgumentOutOfRangeException();
-
- return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
- }
-
- /// <summary>
- /// Copies the contents of this span into a new array. This heap
- /// allocates, so should generally be avoided, however it is sometimes
- /// necessary to bridge the gap with APIs written in terms of arrays.
- /// </summary>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public T[] ToArray()
- {
- if (_length == 0)
- return Array.Empty<T>();
-
- var destination = new T[_length];
- SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref _pointer.Value, _length);
- return destination;
- }
-
- // <summary>
- /// Returns an empty <see cref="Span{T}"/>
- /// </summary>
- public static Span<T> Empty => default(Span<T>);
- }
-
- public static class Span
- {
- /// <summary>
- /// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
- /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
- /// </summary>
- /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="T"/> contains pointers.
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Span<byte> AsBytes<T>(this Span<T> source)
- where T : struct
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
-
- return new Span<byte>(
- ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference()),
- checked(source.Length * Unsafe.SizeOf<T>()));
- }
-
- /// <summary>
- /// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes.
- /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
- /// </summary>
- /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="T"/> contains pointers.
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source)
- where T : struct
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
-
- return new ReadOnlySpan<byte>(
- ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference()),
- checked(source.Length * Unsafe.SizeOf<T>()));
- }
-
- /// <summary>
- /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
- /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
- /// </summary>
- /// <remarks>
- /// Supported only for platforms that support misaligned memory access.
- /// </remarks>
- /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Span<TTo> NonPortableCast<TFrom, TTo>(this Span<TFrom> source)
- where TFrom : struct
- where TTo : struct
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
- if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
-
- return new Span<TTo>(
- ref Unsafe.As<TFrom, TTo>(ref source.DangerousGetPinnableReference()),
- checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
- }
-
- /// <summary>
- /// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
- /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
- /// </summary>
- /// <remarks>
- /// Supported only for platforms that support misaligned memory access.
- /// </remarks>
- /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
- /// <exception cref="System.ArgumentException">
- /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
- /// </exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<TTo> NonPortableCast<TFrom, TTo>(this ReadOnlySpan<TFrom> source)
- where TFrom : struct
- where TTo : struct
- {
- if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
- if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
- ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
-
- return new ReadOnlySpan<TTo>(
- ref Unsafe.As<TFrom, TTo>(ref source.DangerousGetPinnableReference()),
- checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
- }
-
- /// <summary>
- /// Creates a new readonly span over the portion of the target string.
- /// </summary>
- /// <param name="text">The target string.</param>
- /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null
- /// reference (Nothing in Visual Basic).</exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ReadOnlySpan<char> AsSpan(this string text)
- {
- if (text == null)
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
-
- return new ReadOnlySpan<char>(ref text.GetFirstCharRef(), text.Length);
- }
- }
-
- internal static class SpanHelper
- {
- internal static unsafe void CopyTo<T>(ref T destination, ref T source, int elementsCount)
- {
- if (Unsafe.AreSame(ref destination, ref source))
- return;
-
- if (elementsCount <= 1)
- {
- if (elementsCount == 1)
- {
- destination = source;
- }
- return;
- }
-
- nuint byteCount = (nuint)elementsCount * (nuint)Unsafe.SizeOf<T>();
- if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
- {
- fixed (byte* pDestination = &Unsafe.As<T, byte>(ref destination))
- {
- fixed (byte* pSource = &Unsafe.As<T, byte>(ref source))
- {
- Buffer.Memmove(pDestination, pSource, byteCount);
- }
- }
- }
- else
- {
- RuntimeImports.RhBulkMoveWithWriteBarrier(
- ref Unsafe.As<T, byte>(ref destination),
- ref Unsafe.As<T, byte>(ref source),
- byteCount);
- }
- }
-
- internal static unsafe void ClearWithoutReferences(ref byte b, nuint byteLength)
- {
- if (byteLength == 0)
- return;
-
-#if AMD64
- if (byteLength > 4096) goto PInvoke;
- Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength);
- return;
-#else // AMD64
- // TODO: Optimize this method on X86 machine
- // Note: It's important that this switch handles lengths at least up to 22.
- // See notes below near the main loop for why.
-
- // The switch will be very fast since it can be implemented using a jump
- // table in assembly. See http://stackoverflow.com/a/449297/4077294 for more info.
-
- switch (byteLength)
- {
- case 1:
- b = 0;
- return;
- case 2:
- Unsafe.As<byte, short>(ref b) = 0;
- return;
- case 3:
- Unsafe.As<byte, short>(ref b) = 0;
- Unsafe.Add<byte>(ref b, 2) = 0;
- return;
- case 4:
- Unsafe.As<byte, int>(ref b) = 0;
- return;
- case 5:
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.Add<byte>(ref b, 4) = 0;
- return;
- case 6:
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
- return;
- case 7:
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
- Unsafe.Add<byte>(ref b, 6) = 0;
- return;
- case 8:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
-#endif
- return;
- case 9:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
-#endif
- Unsafe.Add<byte>(ref b, 8) = 0;
- return;
- case 10:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
-#endif
- Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- return;
- case 11:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
-#endif
- Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.Add<byte>(ref b, 10) = 0;
- return;
- case 12:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
-#endif
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- return;
- case 13:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
-#endif
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.Add<byte>(ref b, 12) = 0;
- return;
- case 14:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
-#endif
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
- return;
- case 15:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
-#endif
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
- Unsafe.Add<byte>(ref b, 14) = 0;
- return;
- case 16:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
- Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
-#endif
- return;
- case 17:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
- Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
-#endif
- Unsafe.Add<byte>(ref b, 16) = 0;
- return;
- case 18:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
- Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
-#endif
- Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
- return;
- case 19:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
- Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
-#endif
- Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
- Unsafe.Add<byte>(ref b, 18) = 0;
- return;
- case 20:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
- Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
-#endif
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
- return;
- case 21:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
- Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
-#endif
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
- Unsafe.Add<byte>(ref b, 20) = 0;
- return;
- case 22:
-#if BIT64
- Unsafe.As<byte, long>(ref b) = 0;
- Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
-#else
- Unsafe.As<byte, int>(ref b) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
-#endif
- Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
- Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 20)) = 0;
- return;
- }
-
- // P/Invoke into the native version for large lengths
- if (byteLength >= 512) goto PInvoke;
-
- nuint i = 0; // byte offset at which we're copying
-
- if ((Unsafe.As<byte, int>(ref b) & 3) != 0)
- {
- if ((Unsafe.As<byte, int>(ref b) & 1) != 0)
- {
- Unsafe.AddByteOffset<byte>(ref b, i) = 0;
- i += 1;
- if ((Unsafe.As<byte, int>(ref b) & 2) != 0)
- goto IntAligned;
- }
- Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
- i += 2;
- }
-
- IntAligned:
-
- // On 64-bit IntPtr.Size == 8, so we want to advance to the next 8-aligned address. If
- // (int)b % 8 is 0, 5, 6, or 7, we will already have advanced by 0, 3, 2, or 1
- // bytes to the next aligned address (respectively), so do nothing. On the other hand,
- // if it is 1, 2, 3, or 4 we will want to copy-and-advance another 4 bytes until
- // we're aligned.
- // The thing 1, 2, 3, and 4 have in common that the others don't is that if you
- // subtract one from them, their 3rd lsb will not be set. Hence, the below check.
-
- if (((Unsafe.As<byte, int>(ref b) - 1) & 4) == 0)
- {
- Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
- i += 4;
- }
-
- nuint end = byteLength - 16;
- byteLength -= i; // lower 4 bits of byteLength represent how many bytes are left *after* the unrolled loop
-
- // We know due to the above switch-case that this loop will always run 1 iteration; max
- // bytes we clear before checking is 23 (7 to align the pointers, 16 for 1 iteration) so
- // the switch handles lengths 0-22.
- Debug.Assert(end >= 7 && i <= end);
-
- // This is separated out into a different variable, so the i + 16 addition can be
- // performed at the start of the pipeline and the loop condition does not have
- // a dependency on the writes.
- nuint counter;
-
- do
- {
- counter = i + 16;
-
- // This loop looks very costly since there appear to be a bunch of temporary values
- // being created with the adds, but the jit (for x86 anyways) will convert each of
- // these to use memory addressing operands.
-
- // So the only cost is a bit of code size, which is made up for by the fact that
- // we save on writes to b.
-
-#if BIT64
- Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
- Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i + 8)) = 0;
-#else
- Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 4)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 8)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 12)) = 0;
-#endif
-
- i = counter;
-
- // See notes above for why this wasn't used instead
- // i += 16;
- }
- while (counter <= end);
-
- if ((byteLength & 8) != 0)
- {
-#if BIT64
- Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
-#else
- Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
- Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 4)) = 0;
-#endif
- i += 8;
- }
- if ((byteLength & 4) != 0)
- {
- Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
- i += 4;
- }
- if ((byteLength & 2) != 0)
- {
- Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
- i += 2;
- }
- if ((byteLength & 1) != 0)
- {
- Unsafe.AddByteOffset<byte>(ref b, i) = 0;
- // We're not using i after this, so not needed
- // i += 1;
- }
-
- return;
-#endif // AMD64
-
- PInvoke:
- RuntimeImports.RhZeroMemory(ref b, byteLength);
- }
-
- internal static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength)
- {
- if (pointerSizeLength == 0)
- return;
-
- // TODO: Perhaps do switch casing to improve small size perf
-
- nuint i = 0;
- nuint n = 0;
- while ((n = i + 8) <= (pointerSizeLength))
- {
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 4) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 5) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 6) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 7) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- i = n;
- }
- if ((n = i + 4) <= (pointerSizeLength))
- {
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- i = n;
- }
- if ((n = i + 2) <= (pointerSizeLength))
- {
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- i = n;
- }
- if ((i + 1) <= (pointerSizeLength))
- {
- Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
- }
- }
- }
-}
diff --git a/src/mscorlib/src/System/String.Manipulation.cs b/src/mscorlib/src/System/String.Manipulation.cs
index 33e341c..b71ffa2 100644
--- a/src/mscorlib/src/System/String.Manipulation.cs
+++ b/src/mscorlib/src/System/String.Manipulation.cs
@@ -1022,8 +1022,10 @@ namespace System
{
if (oldValue == null)
throw new ArgumentNullException(nameof(oldValue));
+ if (oldValue.Length == 0)
+ throw new ArgumentException(SR.Argument_StringZeroLength, nameof(oldValue));
- // If they asked to replace oldValue with a null, replace all occurences
+ // If they asked to replace oldValue with a null, replace all occurrences
// with the empty string.
if (newValue == null)
newValue = string.Empty;
@@ -1037,10 +1039,11 @@ namespace System
int matchLength = 0;
bool hasDoneAnyReplacements = false;
+ CompareInfo ci = referenceCulture.CompareInfo;
do
{
- index = referenceCulture.CompareInfo.IndexOfCore(this, oldValue, startIndex, m_stringLength - startIndex, options, &matchLength);
+ index = ci.IndexOf(this, oldValue, startIndex, m_stringLength - startIndex, options, &matchLength);
if (index >= 0)
{
// append the unmodified portion of string
diff --git a/src/mscorlib/src/System/String.cs b/src/mscorlib/src/System/String.cs
index f3a4d0f..0a17c1e 100644
--- a/src/mscorlib/src/System/String.cs
+++ b/src/mscorlib/src/System/String.cs
@@ -876,7 +876,7 @@ namespace System
}
}
- internal ref char GetFirstCharRef()
+ internal ref char GetRawStringData()
{
return ref m_firstChar;
}
diff --git a/src/mscorlib/src/System/Threading/Tasks/Task.cs b/src/mscorlib/src/System/Threading/Tasks/Task.cs
index 8e2e6a4..0a9248c 100644
--- a/src/mscorlib/src/System/Threading/Tasks/Task.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/Task.cs
@@ -1467,8 +1467,7 @@ namespace System.Threading.Tasks
return (flags & TASK_STATE_COMPLETED_MASK) != 0;
}
- // For use in InternalWait -- marginally faster than (Task.Status == TaskStatus.RanToCompletion)
- internal bool IsRanToCompletion
+ public bool IsCompletedSuccessfully
{
get { return (m_stateFlags & TASK_STATE_COMPLETED_MASK) == TASK_STATE_RAN_TO_COMPLETION; }
}
diff --git a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs
index 848a0ec..de22235 100644
--- a/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/TaskContinuation.cs
@@ -320,7 +320,7 @@ namespace System.Threading.Tasks
// activation criteria of the TaskContinuationOptions.
TaskContinuationOptions options = m_options;
bool isRightKind =
- completedTask.IsRanToCompletion ?
+ completedTask.IsCompletedSuccessfully ?
(options & TaskContinuationOptions.NotOnRanToCompletion) == 0 :
(completedTask.IsCanceled ?
(options & TaskContinuationOptions.NotOnCanceled) == 0 :
diff --git a/src/mscorlib/src/System/Threading/Tasks/future.cs b/src/mscorlib/src/System/Threading/Tasks/future.cs
index 26c2388..bf9000e 100644
--- a/src/mscorlib/src/System/Threading/Tasks/future.cs
+++ b/src/mscorlib/src/System/Threading/Tasks/future.cs
@@ -374,7 +374,7 @@ namespace System.Threading.Tasks
{
get
{
- return IsRanToCompletion ? "" + m_result : SR.TaskT_DebuggerNoResult;
+ return IsCompletedSuccessfully ? "" + m_result : SR.TaskT_DebuggerNoResult;
}
}
@@ -491,10 +491,10 @@ namespace System.Threading.Tasks
if (waitCompletionNotification) NotifyDebuggerOfWaitCompletionIfNecessary();
// Throw an exception if appropriate.
- if (!IsRanToCompletion) ThrowIfExceptional(includeTaskCanceledExceptions: true);
+ if (!IsCompletedSuccessfully) ThrowIfExceptional(includeTaskCanceledExceptions: true);
// We shouldn't be here if the result has not been set.
- Debug.Assert(IsRanToCompletion, "Task<T>.Result getter: Expected result to have been set.");
+ Debug.Assert(IsCompletedSuccessfully, "Task<T>.Result getter: Expected result to have been set.");
return m_result;
}
diff --git a/src/mscorlib/src/System/ThrowHelper.cs b/src/mscorlib/src/System/ThrowHelper.cs
index 4dcf8d4..ff76738 100644
--- a/src/mscorlib/src/System/ThrowHelper.cs
+++ b/src/mscorlib/src/System/ThrowHelper.cs
@@ -416,6 +416,7 @@ namespace System
type,
stateMachine,
pHandle,
+ values
}
//
diff --git a/src/mscorlib/src/System/TimeZoneInfo.Win32.cs b/src/mscorlib/src/System/TimeZoneInfo.Win32.cs
index b6585bd..4f740bd 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.Win32.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.Win32.cs
@@ -398,7 +398,7 @@ namespace System
baseOffset = baseOffset + match.Rule.BaseUtcOffsetDelta;
if (match.Rule.HasDaylightSaving)
{
- isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.Offset, match.Rule, out isAmbiguousLocalDst, Local);
+ isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.Offset, match.Rule, null, out isAmbiguousLocalDst, Local);
baseOffset += (isDaylightSavings ? match.Rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
}
}
@@ -576,8 +576,8 @@ namespace System
// read LastEntry {(yearN, 1, 1) - MaxValue }
// read the FirstEntry and LastEntry key values (ex: "1980", "2038")
- int first = (int)dynamicKey.GetValue(FirstEntryValue, -1);
- int last = (int)dynamicKey.GetValue(LastEntryValue, -1);
+ int first = (int)dynamicKey.GetValue(FirstEntryValue, -1, RegistryValueOptions.None);
+ int last = (int)dynamicKey.GetValue(LastEntryValue, -1, RegistryValueOptions.None);
if (first == -1 || last == -1 || first > last)
{
@@ -587,7 +587,7 @@ namespace System
// read the first year entry
Win32Native.RegistryTimeZoneInformation dtzi;
- byte[] regValue = dynamicKey.GetValue(first.ToString(CultureInfo.InvariantCulture)) as byte[];
+ byte[] regValue = dynamicKey.GetValue(first.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as byte[];
if (regValue == null || regValue.Length != RegByteLength)
{
rules = null;
@@ -620,7 +620,7 @@ namespace System
// read the middle year entries
for (int i = first + 1; i < last; i++)
{
- regValue = dynamicKey.GetValue(i.ToString(CultureInfo.InvariantCulture)) as byte[];
+ regValue = dynamicKey.GetValue(i.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as byte[];
if (regValue == null || regValue.Length != RegByteLength)
{
rules = null;
@@ -640,7 +640,7 @@ namespace System
}
// read the last year entry
- regValue = dynamicKey.GetValue(last.ToString(CultureInfo.InvariantCulture)) as byte[];
+ regValue = dynamicKey.GetValue(last.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as byte[];
dtzi = new Win32Native.RegistryTimeZoneInformation(regValue);
if (regValue == null || regValue.Length != RegByteLength)
{
@@ -719,7 +719,7 @@ namespace System
}
Win32Native.RegistryTimeZoneInformation registryTimeZoneInfo;
- byte[] regValue = key.GetValue(TimeZoneInfoValue) as byte[];
+ byte[] regValue = key.GetValue(TimeZoneInfoValue, null, RegistryValueOptions.None) as byte[];
if (regValue == null || regValue.Length != RegByteLength) return false;
registryTimeZoneInfo = new Win32Native.RegistryTimeZoneInformation(regValue);
@@ -756,7 +756,7 @@ namespace System
//
if (result)
{
- string registryStandardName = key.GetValue(StandardValue, string.Empty) as string;
+ string registryStandardName = key.GetValue(StandardValue, string.Empty, RegistryValueOptions.None) as string;
result = string.Equals(registryStandardName, timeZone.StandardName, StringComparison.Ordinal);
}
return result;
@@ -884,9 +884,9 @@ namespace System
daylightName = string.Empty;
// read the MUI_ registry keys
- string displayNameMuiResource = key.GetValue(MuiDisplayValue, string.Empty) as string;
- string standardNameMuiResource = key.GetValue(MuiStandardValue, string.Empty) as string;
- string daylightNameMuiResource = key.GetValue(MuiDaylightValue, string.Empty) as string;
+ string displayNameMuiResource = key.GetValue(MuiDisplayValue, string.Empty, RegistryValueOptions.None) as string;
+ string standardNameMuiResource = key.GetValue(MuiStandardValue, string.Empty, RegistryValueOptions.None) as string;
+ string daylightNameMuiResource = key.GetValue(MuiDaylightValue, string.Empty, RegistryValueOptions.None) as string;
// try to load the strings from the native resource DLL(s)
if (!string.IsNullOrEmpty(displayNameMuiResource))
@@ -907,15 +907,15 @@ namespace System
// fallback to using the standard registry keys
if (string.IsNullOrEmpty(displayName))
{
- displayName = key.GetValue(DisplayValue, string.Empty) as string;
+ displayName = key.GetValue(DisplayValue, string.Empty, RegistryValueOptions.None) as string;
}
if (string.IsNullOrEmpty(standardName))
{
- standardName = key.GetValue(StandardValue, string.Empty) as string;
+ standardName = key.GetValue(StandardValue, string.Empty, RegistryValueOptions.None) as string;
}
if (string.IsNullOrEmpty(daylightName))
{
- daylightName = key.GetValue(DaylightValue, string.Empty) as string;
+ daylightName = key.GetValue(DaylightValue, string.Empty, RegistryValueOptions.None) as string;
}
return true;
@@ -964,7 +964,7 @@ namespace System
}
Win32Native.RegistryTimeZoneInformation defaultTimeZoneInformation;
- byte[] regValue = key.GetValue(TimeZoneInfoValue) as byte[];
+ byte[] regValue = key.GetValue(TimeZoneInfoValue, null, RegistryValueOptions.None) as byte[];
if (regValue == null || regValue.Length != RegByteLength)
{
// the registry value could not be cast to a byte array
diff --git a/src/mscorlib/src/System/TimeZoneInfo.cs b/src/mscorlib/src/System/TimeZoneInfo.cs
index 29ea33a..8ed7e22 100644
--- a/src/mscorlib/src/System/TimeZoneInfo.cs
+++ b/src/mscorlib/src/System/TimeZoneInfo.cs
@@ -172,10 +172,11 @@ namespace System
DateTime adjustedTime = ConvertTime(dateTimeOffset, this).DateTime;
bool isAmbiguous = false;
- AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
}
@@ -232,10 +233,11 @@ namespace System
}
bool isAmbiguous = false;
- AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
}
@@ -263,15 +265,15 @@ namespace System
}
// note the time is already adjusted
- private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTime)
+ private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTime, out int? ruleIndex)
{
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
+ AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
if (rule != null && rule.NoDaylightTransitions && !rule.HasDaylightSaving)
{
// When using NoDaylightTransitions rules, each rule is only for one offset.
// When looking for the Daylight savings rules, and we found the non-DST rule,
// then we get the rule right before this rule.
- return GetPreviousAdjustmentRule(rule);
+ return GetPreviousAdjustmentRule(rule, ruleIndex);
}
return rule;
@@ -282,12 +284,23 @@ namespace System
/// If the specified rule is the first AdjustmentRule, or it isn't in _adjustmentRules,
/// then the specified rule is returned.
/// </summary>
- private AdjustmentRule GetPreviousAdjustmentRule(AdjustmentRule rule)
+ private AdjustmentRule GetPreviousAdjustmentRule(AdjustmentRule rule, int? ruleIndex)
{
+ Debug.Assert(rule.NoDaylightTransitions, "GetPreviousAdjustmentRule should only be used with NoDaylightTransitions rules.");
+
+ if (ruleIndex.HasValue && 0 < ruleIndex.Value && ruleIndex.Value < _adjustmentRules.Length)
+ {
+ return _adjustmentRules[ruleIndex.Value - 1];
+ }
+
AdjustmentRule result = rule;
for (int i = 1; i < _adjustmentRules.Length; i++)
{
- if (rule.Equals(_adjustmentRules[i]))
+ // use ReferenceEquals here instead of AdjustmentRule.Equals because
+ // ReferenceEquals is much faster. This is safe because all the callers
+ // of GetPreviousAdjustmentRule pass in a rule that was retrieved from
+ // _adjustmentRules. A different approach will be needed if this ever changes.
+ if (ReferenceEquals(rule, _adjustmentRules[i]))
{
result = _adjustmentRules[i - 1];
break;
@@ -407,10 +420,11 @@ namespace System
dateTime.Kind == DateTimeKind.Utc ? ConvertTime(dateTime, s_utcTimeZone, this, flags, cachedData) :
dateTime;
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
return GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
}
return false;
@@ -492,10 +506,11 @@ namespace System
//
// handle the normal cases...
//
- AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
return GetIsDaylightSavings(adjustedTime, rule, daylightTime, flags);
}
else
@@ -515,11 +530,12 @@ namespace System
(dateTime.Kind == DateTimeKind.Local && s_cachedData.GetCorrespondingKind(this) == DateTimeKind.Local))
{
// only check Unspecified and (Local when this TimeZoneInfo instance is Local)
- AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime);
+ int? ruleIndex;
+ AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime, out ruleIndex);
if (rule != null && rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule);
+ DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule, ruleIndex);
isInvalid = GetIsInvalidTime(dateTime, rule, daylightTime);
}
else
@@ -661,7 +677,8 @@ namespace System
// performance for the normal case at the expense of the 'ArgumentException'
// case and Loss-less Local special cases.
//
- AdjustmentRule sourceRule = sourceTimeZone.GetAdjustmentRuleForTime(dateTime);
+ int? sourceRuleIndex;
+ AdjustmentRule sourceRule = sourceTimeZone.GetAdjustmentRuleForTime(dateTime, out sourceRuleIndex);
TimeSpan sourceOffset = sourceTimeZone.BaseUtcOffset;
if (sourceRule != null)
@@ -670,7 +687,7 @@ namespace System
if (sourceRule.HasDaylightSaving)
{
bool sourceIsDaylightSavings = false;
- DaylightTimeStruct sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule);
+ DaylightTimeStruct sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule, sourceRuleIndex);
// 'dateTime' might be in an invalid time range since it is in an AdjustmentRule
// period that supports DST
@@ -1048,10 +1065,19 @@ namespace System
_supportsDaylightSavingTime = (bool)info.GetValue("SupportsDaylightSavingTime", typeof(bool));
}
- private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTimeisUtc = false)
+ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, out int? ruleIndex)
+ {
+ AdjustmentRule result = GetAdjustmentRuleForTime(dateTime, dateTimeisUtc: false, ruleIndex: out ruleIndex);
+ Debug.Assert(result == null || ruleIndex.HasValue, "If an AdjustmentRule was found, ruleIndex should also be set.");
+
+ return result;
+ }
+
+ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTimeisUtc, out int? ruleIndex)
{
if (_adjustmentRules == null || _adjustmentRules.Length == 0)
{
+ ruleIndex = null;
return null;
}
@@ -1076,6 +1102,7 @@ namespace System
int compareResult = CompareAdjustmentRuleToDateTime(rule, previousRule, dateTime, date, dateTimeisUtc);
if (compareResult == 0)
{
+ ruleIndex = median;
return rule;
}
else if (compareResult < 0)
@@ -1088,6 +1115,7 @@ namespace System
}
}
+ ruleIndex = null;
return null;
}
@@ -1199,7 +1227,7 @@ namespace System
/// <summary>
/// Helper function that returns a DaylightTime from a year and AdjustmentRule.
/// </summary>
- private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule)
+ private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule, int? ruleIndex)
{
TimeSpan delta = rule.DaylightDelta;
DateTime startTime;
@@ -1211,7 +1239,7 @@ namespace System
// Convert the UTC times into adjusted time zone times.
// use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule
- AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule);
+ AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex);
startTime = ConvertFromUtc(rule.DateStart, previousRule.DaylightDelta, previousRule.BaseUtcOffsetDelta);
endTime = ConvertFromUtc(rule.DateEnd, rule.DaylightDelta, rule.BaseUtcOffsetDelta);
@@ -1301,12 +1329,12 @@ namespace System
/// <summary>
/// Gets the offset that should be used to calculate DST start times from a UTC time.
/// </summary>
- private TimeSpan GetDaylightSavingsStartOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule)
+ private TimeSpan GetDaylightSavingsStartOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule, int? ruleIndex)
{
if (rule.NoDaylightTransitions)
{
// use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule
- AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule);
+ AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex);
return baseUtcOffset + previousRule.BaseUtcOffsetDelta + previousRule.DaylightDelta;
}
else
@@ -1328,7 +1356,7 @@ namespace System
/// Helper function that checks if a given dateTime is in Daylight Saving Time (DST).
/// This function assumes the dateTime is in UTC and AdjustmentRule is in a different time zone.
/// </summary>
- private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpan utc, AdjustmentRule rule, out bool isAmbiguousLocalDst, TimeZoneInfo zone)
+ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpan utc, AdjustmentRule rule, int? ruleIndex, out bool isAmbiguousLocalDst, TimeZoneInfo zone)
{
isAmbiguousLocalDst = false;
@@ -1338,7 +1366,7 @@ namespace System
}
// Get the daylight changes for the year of the specified time.
- DaylightTimeStruct daylightTime = zone.GetDaylightTime(year, rule);
+ DaylightTimeStruct daylightTime = zone.GetDaylightTime(year, rule, ruleIndex);
// The start and end times represent the range of universal times that are in DST for that year.
// Within that there is an ambiguous hour, usually right at the end, but at the beginning in
@@ -1352,14 +1380,20 @@ namespace System
// Note we handle the similar case when rule year start with daylight saving and previous year end with daylight saving.
bool ignoreYearAdjustment = false;
- TimeSpan dstStartOffset = zone.GetDaylightSavingsStartOffsetFromUtc(utc, rule);
+ TimeSpan dstStartOffset = zone.GetDaylightSavingsStartOffsetFromUtc(utc, rule, ruleIndex);
DateTime startTime;
if (rule.IsStartDateMarkerForBeginningOfYear() && daylightTime.Start.Year > DateTime.MinValue.Year)
{
- AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime(new DateTime(daylightTime.Start.Year - 1, 12, 31));
+ int? previousYearRuleIndex;
+ AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime(
+ new DateTime(daylightTime.Start.Year - 1, 12, 31),
+ out previousYearRuleIndex);
if (previousYearRule != null && previousYearRule.IsEndDateMarkerForEndOfYear())
{
- DaylightTimeStruct previousDaylightTime = zone.GetDaylightTime(daylightTime.Start.Year - 1, previousYearRule);
+ DaylightTimeStruct previousDaylightTime = zone.GetDaylightTime(
+ daylightTime.Start.Year - 1,
+ previousYearRule,
+ previousYearRuleIndex);
startTime = previousDaylightTime.Start - utc - previousYearRule.BaseUtcOffsetDelta;
ignoreYearAdjustment = true;
}
@@ -1377,7 +1411,10 @@ namespace System
DateTime endTime;
if (rule.IsEndDateMarkerForEndOfYear() && daylightTime.End.Year < DateTime.MaxValue.Year)
{
- AdjustmentRule nextYearRule = zone.GetAdjustmentRuleForTime(new DateTime(daylightTime.End.Year + 1, 1, 1));
+ int? nextYearRuleIndex;
+ AdjustmentRule nextYearRule = zone.GetAdjustmentRuleForTime(
+ new DateTime(daylightTime.End.Year + 1, 1, 1),
+ out nextYearRuleIndex);
if (nextYearRule != null && nextYearRule.IsStartDateMarkerForBeginningOfYear())
{
if (nextYearRule.IsEndDateMarkerForEndOfYear())
@@ -1387,7 +1424,10 @@ namespace System
}
else
{
- DaylightTimeStruct nextdaylightTime = zone.GetDaylightTime(daylightTime.End.Year + 1, nextYearRule);
+ DaylightTimeStruct nextdaylightTime = zone.GetDaylightTime(
+ daylightTime.End.Year + 1,
+ nextYearRule,
+ nextYearRuleIndex);
endTime = nextdaylightTime.End - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta;
}
ignoreYearAdjustment = true;
@@ -1644,14 +1684,15 @@ namespace System
private static TimeSpan GetUtcOffset(DateTime time, TimeZoneInfo zone, TimeZoneInfoOptions flags)
{
TimeSpan baseOffset = zone.BaseUtcOffset;
- AdjustmentRule rule = zone.GetAdjustmentRuleForTime(time);
+ int? ruleIndex;
+ AdjustmentRule rule = zone.GetAdjustmentRuleForTime(time, out ruleIndex);
if (rule != null)
{
baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
if (rule.HasDaylightSaving)
{
- DaylightTimeStruct daylightTime = zone.GetDaylightTime(time.Year, rule);
+ DaylightTimeStruct daylightTime = zone.GetDaylightTime(time.Year, rule, ruleIndex);
bool isDaylightSavings = GetIsDaylightSavings(time, rule, daylightTime, flags);
baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
}
@@ -1690,21 +1731,24 @@ namespace System
isAmbiguousLocalDst = false;
TimeSpan baseOffset = zone.BaseUtcOffset;
int year;
+ int? ruleIndex;
AdjustmentRule rule;
if (time > s_maxDateOnly)
{
- rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue);
+ rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue, out ruleIndex);
year = 9999;
}
else if (time < s_minDateOnly)
{
- rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue);
+ rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue, out ruleIndex);
year = 1;
}
else
{
- rule = zone.GetAdjustmentRuleForTime(time, dateTimeisUtc: true);
+ rule = zone.GetAdjustmentRuleForTime(time, dateTimeisUtc: true, ruleIndex: out ruleIndex);
+ Debug.Assert(rule == null || ruleIndex.HasValue,
+ "If GetAdjustmentRuleForTime returned an AdjustmentRule, ruleIndex should also be set.");
// As we get the associated rule using the adjusted targetTime, we should use the adjusted year (targetTime.Year) too as after adding the baseOffset,
// sometimes the year value can change if the input datetime was very close to the beginning or the end of the year. Examples of such cases:
@@ -1719,7 +1763,7 @@ namespace System
baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
if (rule.HasDaylightSaving)
{
- isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone._baseUtcOffset, rule, out isAmbiguousLocalDst, zone);
+ isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone._baseUtcOffset, rule, ruleIndex, out isAmbiguousLocalDst, zone);
baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
}
}