summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/System/AppContext
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/System/AppContext')
-rw-r--r--src/mscorlib/src/System/AppContext/AppContext.cs189
-rw-r--r--src/mscorlib/src/System/AppContext/AppContextDefaultValues.CoreClrOverrides.cs21
-rw-r--r--src/mscorlib/src/System/AppContext/AppContextDefaultValues.Defaults.Central.cs33
-rw-r--r--src/mscorlib/src/System/AppContext/AppContextDefaultValues.Defaults.cs71
-rw-r--r--src/mscorlib/src/System/AppContext/AppContextDefaultValues.cs164
-rw-r--r--src/mscorlib/src/System/AppContext/AppContextSwitches.cs110
6 files changed, 588 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/AppContext/AppContext.cs b/src/mscorlib/src/System/AppContext/AppContext.cs
new file mode 100644
index 0000000000..0b0643d7b4
--- /dev/null
+++ b/src/mscorlib/src/System/AppContext/AppContext.cs
@@ -0,0 +1,189 @@
+// 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.Collections.Generic;
+
+namespace System
+{
+ public static class AppContext
+ {
+ [Flags]
+ private enum SwitchValueState
+ {
+ HasFalseValue = 0x1,
+ HasTrueValue = 0x2,
+ HasLookedForOverride = 0x4,
+ UnknownValue = 0x8 // Has no default and could not find an override
+ }
+ private static readonly Dictionary<string, SwitchValueState> s_switchMap = new Dictionary<string, SwitchValueState>();
+
+ public static string BaseDirectory
+ {
+#if FEATURE_CORECLR
+ [System.Security.SecuritySafeCritical]
+#endif
+ get
+ {
+ // The value of APP_CONTEXT_BASE_DIRECTORY key has to be a string and it is not allowed to be any other type.
+ // Otherwise the caller will get invalid cast exception
+ return (string) AppDomain.CurrentDomain.GetData("APP_CONTEXT_BASE_DIRECTORY") ?? AppDomain.CurrentDomain.BaseDirectory;
+ }
+ }
+
+ public static string TargetFrameworkName
+ {
+ get
+ {
+ // Forward the value that is set on the current domain.
+ return AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
+ }
+ }
+
+#if FEATURE_CORECLR
+ [System.Security.SecuritySafeCritical]
+#endif
+ public static object GetData(string name)
+ {
+ return AppDomain.CurrentDomain.GetData(name);
+ }
+
+ #region Switch APIs
+ static AppContext()
+ {
+ // populate the AppContext with the default set of values
+ AppContextDefaultValues.PopulateDefaultValues();
+ }
+
+ /// <summary>
+ /// Try to get the value of the switch.
+ /// </summary>
+ /// <param name="switchName">The name of the switch</param>
+ /// <param name="isEnabled">A variable where to place the value of the switch</param>
+ /// <returns>A return value of true represents that the switch was set and <paramref name="isEnabled"/> contains the value of the switch</returns>
+ public static bool TryGetSwitch(string switchName, out bool isEnabled)
+ {
+ if (switchName == null)
+ throw new ArgumentNullException("switchName");
+ if (switchName.Length == 0)
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName");
+
+ // By default, the switch is not enabled.
+ isEnabled = false;
+
+ SwitchValueState switchValue;
+ lock (s_switchMap)
+ {
+ if (s_switchMap.TryGetValue(switchName, out switchValue))
+ {
+ // The value is in the dictionary.
+ // There are 3 cases here:
+ // 1. The value of the switch is 'unknown'. This means that the switch name is not known to the system (either via defaults or checking overrides).
+ // Example: This is the case when, during a servicing event, a switch is added to System.Xml which ships before mscorlib. The value of the switch
+ // Will be unknown to mscorlib.dll and we want to prevent checking the overrides every time we check this switch
+ // 2. The switch has a valid value AND we have read the overrides for it
+ // Example: TryGetSwitch is called for a switch set via SetSwitch
+ // 3. The switch has the default value and we need to check for overrides
+ // Example: TryGetSwitch is called for the first time for a switch that has a default value
+
+ // 1. The value is unknown
+ if (switchValue == SwitchValueState.UnknownValue)
+ {
+ isEnabled = false;
+ return false;
+ }
+
+ // We get the value of isEnabled from the value that we stored in the dictionary
+ isEnabled = (switchValue & SwitchValueState.HasTrueValue) == SwitchValueState.HasTrueValue;
+
+ // 2. The switch has a valid value AND we have checked for overrides
+ if ((switchValue & SwitchValueState.HasLookedForOverride) == SwitchValueState.HasLookedForOverride)
+ {
+ return true;
+ }
+
+ // 3. The switch has a valid value, but we need to check for overrides.
+ // Regardless of whether or not the switch has an override, we need to update the value to reflect
+ // the fact that we checked for overrides.
+ bool overrideValue;
+ if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue))
+ {
+ // we found an override!
+ isEnabled = overrideValue;
+ }
+
+ // Update the switch in the dictionary to mark it as 'checked for override'
+ s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
+ | SwitchValueState.HasLookedForOverride;
+
+ return true;
+ }
+ else
+ {
+ // The value is NOT in the dictionary
+ // In this case we need to see if we have an override defined for the value.
+ // There are 2 cases:
+ // 1. The value has an override specified. In this case we need to add the value to the dictionary
+ // and mark it as checked for overrides
+ // Example: In a servicing event, System.Xml introduces a switch and an override is specified.
+ // The value is not found in mscorlib (as System.Xml ships independent of mscorlib)
+ // 2. The value does not have an override specified
+ // In this case, we want to capture the fact that we looked for a value and found nothing by adding
+ // an entry in the dictionary with the 'sentinel' value of 'SwitchValueState.UnknownValue'.
+ // Example: This will prevent us from trying to find overrides for values that we don't have in the dictionary
+
+ // 1. The value has an override specified.
+ bool overrideValue;
+ if (AppContextDefaultValues.TryGetSwitchOverride(switchName, out overrideValue))
+ {
+ isEnabled = overrideValue;
+
+ // Update the switch in the dictionary to mark it as 'checked for override'
+ s_switchMap[switchName] = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
+ | SwitchValueState.HasLookedForOverride;
+
+ return true;
+ }
+
+ // 2. The value does not have an override.
+ s_switchMap[switchName] = SwitchValueState.UnknownValue;
+ }
+ }
+ return false; // we did not find a value for the switch
+ }
+
+ /// <summary>
+ /// Assign a switch a value
+ /// </summary>
+ /// <param name="switchName">The name of the switch</param>
+ /// <param name="isEnabled">The value to assign</param>
+ public static void SetSwitch(string switchName, bool isEnabled)
+ {
+ if (switchName == null)
+ throw new ArgumentNullException("switchName");
+ if (switchName.Length == 0)
+ throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "switchName");
+
+ SwitchValueState switchValue = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
+ | SwitchValueState.HasLookedForOverride;
+
+ lock (s_switchMap)
+ {
+ // Store the new value and the fact that we checked in the dictionary
+ s_switchMap[switchName] = switchValue;
+ }
+ }
+
+ /// <summary>
+ /// This method is going to be called from the AppContextDefaultValues class when setting up the
+ /// default values for the switches. !!!! This method is called during the static constructor so it does not
+ /// take a lock !!!! If you are planning to use this outside of that, please ensure proper locking.
+ /// </summary>
+ internal static void DefineSwitchDefault(string switchName, bool isEnabled)
+ {
+ s_switchMap[switchName] = isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue;
+ }
+ #endregion
+ }
+}
diff --git a/src/mscorlib/src/System/AppContext/AppContextDefaultValues.CoreClrOverrides.cs b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.CoreClrOverrides.cs
new file mode 100644
index 0000000000..89893c6bee
--- /dev/null
+++ b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.CoreClrOverrides.cs
@@ -0,0 +1,21 @@
+// 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.
+
+namespace System
+{
+ internal static partial class AppContextDefaultValues
+ {
+ static partial void TryGetSwitchOverridePartial(string switchName, ref bool overrideFound, ref bool overrideValue)
+ {
+ overrideFound = false;
+ overrideValue = false;
+
+ string value = AppContext.GetData(switchName) as string;
+ if (value != null)
+ {
+ overrideFound = bool.TryParse(value, out overrideValue);
+ }
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/AppContext/AppContextDefaultValues.Defaults.Central.cs b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.Defaults.Central.cs
new file mode 100644
index 0000000000..92c9917113
--- /dev/null
+++ b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.Defaults.Central.cs
@@ -0,0 +1,33 @@
+// 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.
+
+
+//
+// This file is used to provide an implementation for defining a default value
+// This should be compiled only in mscorlib where the AppContext class is available
+//
+
+namespace System
+{
+ internal static partial class AppContextDefaultValues
+ {
+ /// <summary>
+ /// This method allows reading the override for a switch.
+ /// The implementation is platform specific
+ /// </summary>
+ public static bool TryGetSwitchOverride(string switchName, out bool overrideValue)
+ {
+ // The default value for a switch is 'false'
+ overrideValue = false;
+
+ // Read the override value
+ bool overrideFound = false;
+
+ // This partial method will be removed if there are no implementations of it.
+ TryGetSwitchOverridePartial(switchName, ref overrideFound, ref overrideValue);
+
+ return overrideFound;
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/AppContext/AppContextDefaultValues.Defaults.cs b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.Defaults.cs
new file mode 100644
index 0000000000..c80913e3a6
--- /dev/null
+++ b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.Defaults.cs
@@ -0,0 +1,71 @@
+// 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;
+
+namespace System
+{
+ internal static partial class AppContextDefaultValues
+ {
+
+ internal static readonly string SwitchNoAsyncCurrentCulture = "Switch.System.Globalization.NoAsyncCurrentCulture";
+ internal static readonly string SwitchThrowExceptionIfDisposedCancellationTokenSource = "Switch.System.Threading.ThrowExceptionIfDisposedCancellationTokenSource";
+ internal static readonly string SwitchPreserveEventListnerObjectIdentity = "Switch.System.Diagnostics.EventSource.PreserveEventListnerObjectIdentity";
+#if FEATURE_PATHCOMPAT
+ internal static readonly string SwitchUseLegacyPathHandling = "Switch.System.IO.UseLegacyPathHandling";
+ internal static readonly string SwitchBlockLongPaths = "Switch.System.IO.BlockLongPaths";
+#endif
+
+ // This is a partial method. Platforms can provide an implementation of it that will set override values
+ // from whatever mechanism is available on that platform. If no implementation is provided, the compiler is going to remove the calls
+ // to it from the code
+ // We are going to have an implementation of this method for the Desktop platform that will read the overrides from app.config, registry and
+ // the shim database. Additional implementation can be provided for other platforms.
+ static partial void PopulateOverrideValuesPartial();
+
+ static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version)
+ {
+ // When defining a new switch you should add it to the last known version.
+ // For instance, if you are adding a switch in .NET 4.6 (the release after 4.5.2) you should defined your switch
+ // like this:
+ // if (version <= 40502) ...
+ // This ensures that all previous versions of that platform (up-to 4.5.2) will get the old behavior by default
+ // NOTE: When adding a default value for a switch please make sure that the default value is added to ALL of the existing platforms!
+ // NOTE: When adding a new if statement for the version please ensure that ALL previous switches are enabled (ie. don't use else if)
+ switch (platformIdentifier)
+ {
+ case ".NETCore":
+ case ".NETFramework":
+ {
+ if (version <= 40502)
+ {
+ AppContext.DefineSwitchDefault(SwitchNoAsyncCurrentCulture, true);
+ AppContext.DefineSwitchDefault(SwitchThrowExceptionIfDisposedCancellationTokenSource, true);
+ }
+#if FEATURE_PATHCOMPAT
+ if (version <= 40601)
+ {
+ AppContext.DefineSwitchDefault(SwitchUseLegacyPathHandling, true);
+ AppContext.DefineSwitchDefault(SwitchBlockLongPaths, true);
+ }
+#endif
+ break;
+ }
+ case "WindowsPhone":
+ case "WindowsPhoneApp":
+ {
+ if (version <= 80100)
+ {
+ AppContext.DefineSwitchDefault(SwitchNoAsyncCurrentCulture, true);
+ AppContext.DefineSwitchDefault(SwitchThrowExceptionIfDisposedCancellationTokenSource, true);
+ }
+ break;
+ }
+ }
+
+ // At this point we should read the overrides if any are defined
+ PopulateOverrideValuesPartial();
+ }
+ }
+}
diff --git a/src/mscorlib/src/System/AppContext/AppContextDefaultValues.cs b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.cs
new file mode 100644
index 0000000000..7ab7ffbc04
--- /dev/null
+++ b/src/mscorlib/src/System/AppContext/AppContextDefaultValues.cs
@@ -0,0 +1,164 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+
+namespace System
+{
+ internal static partial class AppContextDefaultValues
+ {
+ public static void PopulateDefaultValues()
+ {
+ string platformIdentifier, profile;
+ int version;
+
+ ParseTargetFrameworkName(out platformIdentifier, out profile, out version);
+
+ // Call into each library to populate their default switches
+ PopulateDefaultValuesPartial(platformIdentifier, profile, version);
+ }
+
+ /// <summary>
+ /// We have this separate method for getting the parsed elements out of the TargetFrameworkName so we can
+ /// more easily support this on other platforms.
+ /// </summary>
+ private static void ParseTargetFrameworkName(out string identifier, out string profile, out int version)
+ {
+ string targetFrameworkMoniker = AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
+
+ if (!TryParseFrameworkName(targetFrameworkMoniker, out identifier, out version, out profile))
+ {
+#if FEATURE_CORECLR
+ // If we can't parse the TFM or we don't have a TFM, default to latest behavior for all
+ // switches (ie. all of them false).
+ // If we want to use the latest behavior it is enough to set the value of the switch to string.Empty.
+ // When the get to the caller of this method (PopulateDefaultValuesPartial) we are going to use the
+ // identifier we just set to decide which switches to turn on. By having an empty string as the
+ // identifier we are simply saying -- don't turn on any switches, and we are going to get the latest
+ // behavior for all the switches
+ identifier = string.Empty;
+#else
+ identifier = ".NETFramework";
+ version = 40000;
+ profile = string.Empty;
+#endif
+ }
+ }
+
+ // This code was a constructor copied from the FrameworkName class, which is located in System.dll.
+ // Parses strings in the following format: "<identifier>, Version=[v|V]<version>, Profile=<profile>"
+ // - The identifier and version is required, profile is optional
+ // - Only three components are allowed.
+ // - The version string must be in the System.Version format; an optional "v" or "V" prefix is allowed
+ private static bool TryParseFrameworkName(String frameworkName, out String identifier, out int version, out String profile)
+ {
+ // For parsing a target Framework moniker, from the FrameworkName class
+ const char c_componentSeparator = ',';
+ const char c_keyValueSeparator = '=';
+ const char c_versionValuePrefix = 'v';
+ const String c_versionKey = "Version";
+ const String c_profileKey = "Profile";
+
+ identifier = profile = string.Empty;
+ version = 0;
+
+ if (frameworkName == null || frameworkName.Length == 0)
+ {
+ return false;
+ }
+
+ String[] components = frameworkName.Split(c_componentSeparator);
+ version = 0;
+
+ // Identifer and Version are required, Profile is optional.
+ if (components.Length < 2 || components.Length > 3)
+ {
+ return false;
+ }
+
+ //
+ // 1) Parse the "Identifier", which must come first. Trim any whitespace
+ //
+ identifier = components[0].Trim();
+
+ if (identifier.Length == 0)
+ {
+ return false;
+ }
+
+ bool versionFound = false;
+ profile = null;
+
+ //
+ // The required "Version" and optional "Profile" component can be in any order
+ //
+ for (int i = 1; i < components.Length; i++)
+ {
+ // Get the key/value pair separated by '='
+ string[] keyValuePair = components[i].Split(c_keyValueSeparator);
+
+ if (keyValuePair.Length != 2)
+ {
+ return false;
+ }
+
+ // Get the key and value, trimming any whitespace
+ string key = keyValuePair[0].Trim();
+ string value = keyValuePair[1].Trim();
+
+ //
+ // 2) Parse the required "Version" key value
+ //
+ if (key.Equals(c_versionKey, StringComparison.OrdinalIgnoreCase))
+ {
+ versionFound = true;
+
+ // Allow the version to include a 'v' or 'V' prefix...
+ if (value.Length > 0 && (value[0] == c_versionValuePrefix || value[0] == 'V'))
+ {
+ value = value.Substring(1);
+ }
+ Version realVersion = new Version(value);
+ // The version class will represent some unset values as -1 internally (instead of 0).
+ version = realVersion.Major * 10000;
+ if (realVersion.Minor > 0)
+ version += realVersion.Minor * 100;
+ if (realVersion.Build > 0)
+ version += realVersion.Build;
+ }
+ //
+ // 3) Parse the optional "Profile" key value
+ //
+ else if (key.Equals(c_profileKey, StringComparison.OrdinalIgnoreCase))
+ {
+ if (!String.IsNullOrEmpty(value))
+ {
+ profile = value;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (!versionFound)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ // This is a partial method. Platforms (such as Desktop) can provide an implementation of it that will read override value
+ // from whatever mechanism is available on that platform. If no implementation is provided, the compiler is going to remove the calls
+ // to it from the code
+ static partial void TryGetSwitchOverridePartial(string switchName, ref bool overrideFound, ref bool overrideValue);
+
+ /// This is a partial method. This method is responsible for populating the default values based on a TFM.
+ /// It is partial because each library should define this method in their code to contain their defaults.
+ static partial void PopulateDefaultValuesPartial(string platformIdentifier, string profile, int version);
+ }
+}
diff --git a/src/mscorlib/src/System/AppContext/AppContextSwitches.cs b/src/mscorlib/src/System/AppContext/AppContextSwitches.cs
new file mode 100644
index 0000000000..3a96ec2159
--- /dev/null
+++ b/src/mscorlib/src/System/AppContext/AppContextSwitches.cs
@@ -0,0 +1,110 @@
+// 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.
+
+namespace System
+{
+ using System;
+ using System.Runtime.CompilerServices;
+
+ internal static class AppContextSwitches
+ {
+ private static int _noAsyncCurrentCulture;
+ public static bool NoAsyncCurrentCulture
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return GetCachedSwitchValue(AppContextDefaultValues.SwitchNoAsyncCurrentCulture, ref _noAsyncCurrentCulture);
+ }
+ }
+
+ private static int _throwExceptionIfDisposedCancellationTokenSource;
+ public static bool ThrowExceptionIfDisposedCancellationTokenSource
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return GetCachedSwitchValue(AppContextDefaultValues.SwitchThrowExceptionIfDisposedCancellationTokenSource, ref _throwExceptionIfDisposedCancellationTokenSource);
+ }
+ }
+
+ private static int _preserveEventListnerObjectIdentity;
+ public static bool PreserveEventListnerObjectIdentity
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return GetCachedSwitchValue(AppContextDefaultValues.SwitchPreserveEventListnerObjectIdentity, ref _preserveEventListnerObjectIdentity);
+ }
+ }
+
+#if FEATURE_PATHCOMPAT
+ private static int _useLegacyPathHandling;
+
+ /// <summary>
+ /// Use legacy path normalization logic and blocking of extended syntax.
+ /// </summary>
+ public static bool UseLegacyPathHandling
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return GetCachedSwitchValue(AppContextDefaultValues.SwitchUseLegacyPathHandling, ref _useLegacyPathHandling);
+ }
+ }
+
+ private static int _blockLongPaths;
+
+ /// <summary>
+ /// Throw PathTooLongException for paths greater than MAX_PATH or directories greater than 248 (as per CreateDirectory Win32 limitations)
+ /// </summary>
+ public static bool BlockLongPaths
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return GetCachedSwitchValue(AppContextDefaultValues.SwitchBlockLongPaths, ref _blockLongPaths);
+ }
+ }
+#endif // FEATURE_PATHCOMPAT
+
+ //
+ // Implementation details
+ //
+
+ private static bool DisableCaching { get; set; }
+
+ static AppContextSwitches()
+ {
+ bool isEnabled;
+ if (AppContext.TryGetSwitch(@"TestSwitch.LocalAppContext.DisableCaching", out isEnabled))
+ {
+ DisableCaching = isEnabled;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool GetCachedSwitchValue(string switchName, ref int switchValue)
+ {
+ if (switchValue < 0) return false;
+ if (switchValue > 0) return true;
+
+ return GetCachedSwitchValueInternal(switchName, ref switchValue);
+ }
+
+ private static bool GetCachedSwitchValueInternal(string switchName, ref int switchValue)
+ {
+ bool isSwitchEnabled;
+ AppContext.TryGetSwitch(switchName, out isSwitchEnabled);
+
+ if (DisableCaching)
+ {
+ return isSwitchEnabled;
+ }
+
+ switchValue = isSwitchEnabled ? 1 /*true*/ : -1 /*false*/;
+ return isSwitchEnabled;
+ }
+ }
+}