</ItemGroup>
<ItemGroup>
<Compile Include="$(BclSourcesRoot)\System\AppContext\AppContext.cs" />
- <Compile Include="$(BclSourcesRoot)\System\AppContext\AppContextSwitches.cs" />
- <Compile Include="$(BclSourcesRoot)\System\AppContext\AppContextDefaultValues.cs" />
- <Compile Include="$(BclSourcesRoot)\System\AppContext\AppContextDefaultValues.CoreClrOverrides.cs" />
- <Compile Include="$(BclSourcesRoot)\System\AppContext\AppContextDefaultValues.Defaults.cs" />
- <Compile Include="$(BclSourcesRoot)\System\AppContext\AppContextDefaultValues.Defaults.Central.cs" />
<Compile Include="$(BclSourcesRoot)\System\Object.cs" />
<Compile Include="$(BclSourcesRoot)\System\Array.cs" />
<Compile Include="$(BclSourcesRoot)\System\ThrowHelper.cs" />
using System.IO;
using System.Reflection;
using System.Runtime.Versioning;
+using System.Threading;
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>();
+ private static Dictionary<string, object> s_dataStore = new Dictionary<string, object>();
+ private static Dictionary<string, bool> s_switches;
static AppContext()
{
// Unloading event must happen before ProcessExit event
AppDomain.CurrentDomain.ProcessExit += OnUnloading;
AppDomain.CurrentDomain.ProcessExit += OnProcessExit;
-
- // populate the AppContext with the default set of values
- AppContextDefaultValues.PopulateDefaultValues();
}
public static string BaseDirectory
{
// 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
- string baseDirectory = (string)AppDomain.CurrentDomain.GetData("APP_CONTEXT_BASE_DIRECTORY");
+ string baseDirectory = (string)GetData("APP_CONTEXT_BASE_DIRECTORY");
if (baseDirectory != null)
return baseDirectory;
public static object GetData(string name)
{
- return AppDomain.CurrentDomain.GetData(name);
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
+ object data;
+ lock (s_dataStore)
+ {
+ s_dataStore.TryGetValue(name, out data);
+ }
+ return data;
}
public static void SetData(string name, object data)
{
- AppDomain.CurrentDomain.SetData(name, data);
+ if (name == null)
+ throw new ArgumentNullException(nameof(name));
+
+ lock (s_dataStore)
+ {
+ s_dataStore[name] = data;
+ }
}
public static event UnhandledExceptionEventHandler UnhandledException
}
}
- #region Switch APIs
/// <summary>
/// Try to get the value of the switch.
/// </summary>
if (switchName.Length == 0)
throw new ArgumentException(SR.Argument_EmptyName, nameof(switchName));
- // By default, the switch is not enabled.
- isEnabled = false;
-
- SwitchValueState switchValue;
- lock (s_switchMap)
+ if (s_switches != null)
{
- if (s_switchMap.TryGetValue(switchName, out switchValue))
+ lock (s_switches)
{
- // 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)
- {
+ if (s_switches.TryGetValue(switchName, out isEnabled))
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;
- }
+ string value = GetData(switchName) as string;
+ if (value != null)
+ {
+ if (bool.TryParse(value, out isEnabled))
+ return true;
}
- return false; // we did not find a value for the switch
+
+ isEnabled = false;
+ return false;
}
/// <summary>
if (switchName.Length == 0)
throw new ArgumentException(SR.Argument_EmptyName, nameof(switchName));
- SwitchValueState switchValue = (isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue)
- | SwitchValueState.HasLookedForOverride;
-
- lock (s_switchMap)
+ if (s_switches == null)
{
- // Store the new value and the fact that we checked in the dictionary
- s_switchMap[switchName] = switchValue;
+ // Compatibility switches are rarely used. Initialize the Dictionary lazily
+ Interlocked.CompareExchange(ref s_switches, new Dictionary<string, bool>(), null);
}
- }
- /// <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;
+ lock (s_switches)
+ {
+ s_switches[switchName] = isEnabled;
+ }
}
- #endregion
}
}
+++ /dev/null
-// 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);
- }
- }
- }
-}
+++ /dev/null
-// 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;
- }
- }
-}
+++ /dev/null
-// 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 SwitchEnforceJapaneseEraYearRanges = "Switch.System.Globalization.EnforceJapaneseEraYearRanges";
- internal static readonly string SwitchFormatJapaneseFirstYearAsANumber = "Switch.System.Globalization.FormatJapaneseFirstYearAsANumber";
- internal static readonly string SwitchEnforceLegacyJapaneseDateParsing = "Switch.System.Globalization.EnforceLegacyJapaneseDateParsing";
- internal static readonly string SwitchPreserveEventListnerObjectIdentity = "Switch.System.Diagnostics.EventSource.PreserveEventListnerObjectIdentity";
-
- // 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);
- }
-
- break;
- }
- case "WindowsPhone":
- case "WindowsPhoneApp":
- {
- if (version <= 80100)
- {
- AppContext.DefineSwitchDefault(SwitchNoAsyncCurrentCulture, true);
- }
- break;
- }
- }
-
- // At this point we should read the overrides if any are defined
- PopulateOverrideValuesPartial();
- }
- }
-}
+++ /dev/null
-// 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 = AppContext.TargetFrameworkName;
-
- if (!TryParseFrameworkName(targetFrameworkMoniker, out identifier, out version, out profile))
- {
- // 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;
- }
- }
-
- // 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...
- Version realVersion = value.Length > 0 && (value[0] == c_versionValuePrefix || value[0] == 'V') ?
- Version.Parse(value.AsSpan(1)) :
- Version.Parse(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);
- }
-}
+++ /dev/null
-// 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.Runtime.CompilerServices;
-
-namespace System
-{
- 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 _enforceJapaneseEraYearRanges;
- public static bool EnforceJapaneseEraYearRanges
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return GetCachedSwitchValue(AppContextDefaultValues.SwitchEnforceJapaneseEraYearRanges, ref _enforceJapaneseEraYearRanges);
- }
- }
-
- private static int _formatJapaneseFirstYearAsANumber;
- public static bool FormatJapaneseFirstYearAsANumber
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return GetCachedSwitchValue(AppContextDefaultValues.SwitchFormatJapaneseFirstYearAsANumber, ref _formatJapaneseFirstYearAsANumber);
- }
- }
- private static int _enforceLegacyJapaneseDateParsing;
- public static bool EnforceLegacyJapaneseDateParsing
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return GetCachedSwitchValue(AppContextDefaultValues.SwitchEnforceLegacyJapaneseDateParsing, ref _enforceLegacyJapaneseDateParsing);
- }
- }
-
- private static int _preserveEventListnerObjectIdentity;
- public static bool PreserveEventListnerObjectIdentity
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return GetCachedSwitchValue(AppContextDefaultValues.SwitchPreserveEventListnerObjectIdentity, ref _preserveEventListnerObjectIdentity);
- }
- }
-
- //
- // 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;
- }
- }
-}
// of these fields cannot be changed without changing the layout in
// the EE- AppDomainBaseObject in this case)
- private Dictionary<string, object> _LocalStore;
public event AssemblyLoadEventHandler AssemblyLoad;
private ResolveEventHandler _TypeResolve;
/// </summary>
private void CreateAppDomainManager()
{
- string trustedPlatformAssemblies = (string)GetData("TRUSTED_PLATFORM_ASSEMBLIES");
+ string trustedPlatformAssemblies = (string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES");
if (trustedPlatformAssemblies != null)
{
- string platformResourceRoots = (string)GetData("PLATFORM_RESOURCE_ROOTS") ?? string.Empty;
- string appPaths = (string)GetData("APP_PATHS") ?? string.Empty;
- string appNiPaths = (string)GetData("APP_NI_PATHS") ?? string.Empty;
- string appLocalWinMD = (string)GetData("APP_LOCAL_WINMETADATA") ?? string.Empty;
+ string platformResourceRoots = (string)AppContext.GetData("PLATFORM_RESOURCE_ROOTS") ?? string.Empty;
+ string appPaths = (string)AppContext.GetData("APP_PATHS") ?? string.Empty;
+ string appNiPaths = (string)AppContext.GetData("APP_NI_PATHS") ?? string.Empty;
+ string appLocalWinMD = (string)AppContext.GetData("APP_LOCAL_WINMETADATA") ?? string.Empty;
SetupBindingPaths(trustedPlatformAssemblies, platformResourceRoots, appPaths, appNiPaths, appLocalWinMD);
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void PublishAnonymouslyHostedDynamicMethodsAssembly(RuntimeAssembly assemblyHandle);
- public void SetData(string name, object data)
- {
- if (name == null)
- throw new ArgumentNullException(nameof(name));
-
- lock (((ICollection)LocalStore).SyncRoot)
- {
- LocalStore[name] = data;
- }
- }
-
- [Pure]
- public object GetData(string name)
- {
- if (name == null)
- throw new ArgumentNullException(nameof(name));
-
- object data;
- lock (((ICollection)LocalStore).SyncRoot)
- {
- LocalStore.TryGetValue(name, out data);
- }
-
- return data;
- }
-
[Obsolete("AppDomain.GetCurrentThreadId has been deprecated because it does not provide a stable Id when managed threads are running on fibers (aka lightweight threads). To get a stable identifier for a managed thread, use the ManagedThreadId property on Thread. http://go.microsoft.com/fwlink/?linkid=14202", false)]
[DllImport(Interop.Libraries.Kernel32)]
public static extern int GetCurrentThreadId();
null;
}
- private Dictionary<string, object> LocalStore
- {
- get
- {
- return _LocalStore ?? (_LocalStore = new Dictionary<string, object>());
- }
- }
-
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void nSetNativeDllSearchDirectories(string paths);
{
if (propertyNames[i] != null)
{
- ad.SetData(propertyNames[i], propertyValues[i]);
+ AppContext.SetData(propertyNames[i], propertyValues[i]);
}
}
}
if (!fUseSatelliteAssemblyResourceLookupUnderAppX)
{
// Check to see if the assembly is under PLATFORM_RESOURCE_ROOTS. If it is, then we should use satellite assembly lookup for it.
- string platformResourceRoots = (string)(AppDomain.CurrentDomain.GetData("PLATFORM_RESOURCE_ROOTS"));
+ string platformResourceRoots = (string)(AppContext.GetData("PLATFORM_RESOURCE_ROOTS"));
if ((platformResourceRoots != null) && (platformResourceRoots != string.Empty))
{
string resourceAssemblyPath = resourcesAssembly.Location;
DEFINE_FIELD(ACCESS_VIOLATION_EXCEPTION, ACCESSTYPE, _accessType)
DEFINE_CLASS_U(System, AppDomain, AppDomainBaseObject)
-DEFINE_FIELD_U(_LocalStore, AppDomainBaseObject, m_LocalStore)
DEFINE_FIELD_U(AssemblyLoad, AppDomainBaseObject, m_pAssemblyEventHandler)
DEFINE_FIELD_U(_TypeResolve, AppDomainBaseObject, m_pTypeEventHandler)
DEFINE_FIELD_U(_ResourceResolve, AppDomainBaseObject, m_pResourceEventHandler)
// READ ME:
// Modifying the order or fields of this object may require other changes to the
// classlib class definition of this object.
- OBJECTREF m_LocalStore;
OBJECTREF m_pAssemblyEventHandler; // Delegate for 'loading assembly' event
OBJECTREF m_pTypeEventHandler; // Delegate for 'resolve type' event
OBJECTREF m_pResourceEventHandler; // Delegate for 'resolve resource' event
<Compile Include="$(MSBuildThisFileDirectory)System\Int64.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\IntPtr.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Lazy.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\LocalAppContextSwitches.Common.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Marvin.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Marvin.OrdinalIgnoreCase.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Math.cs" />
#endif // FEATURE_MANAGED_ETW
if (m_Dispatchers != null && m_eventData[eventId].EnabledForAnyListener)
{
-#if (!ES_BUILD_STANDALONE && !ES_BUILD_PN)
+#if !ES_BUILD_STANDALONE
// Maintain old behavior - object identity is preserved
- if (AppContextSwitches.PreserveEventListnerObjectIdentity)
+ if (LocalAppContextSwitches.PreserveEventListnerObjectIdentity)
{
WriteToAllListeners(
eventId: eventId,
// Date separator (derived from short date format)
internal string DateSeparator(CalendarId calendarId)
{
- if (calendarId == CalendarId.JAPAN && !AppContextSwitches.EnforceLegacyJapaneseDateParsing)
+ if (calendarId == CalendarId.JAPAN && !LocalAppContextSwitches.EnforceLegacyJapaneseDateParsing)
{
// The date separator is derived from the default short date pattern. So far this pattern is using
// '/' as date separator when using the Japanese calendar which make the formatting and parsing work fine.
int year = cal.GetYear(dateTime);
tokenLen = ParseRepeatPattern(format, i, ch);
if (isJapaneseCalendar &&
- !AppContextSwitches.FormatJapaneseFirstYearAsANumber &&
+ !LocalAppContextSwitches.FormatJapaneseFirstYearAsANumber &&
year == 1 &&
((i + tokenLen < format.Length && format[i + tokenLen] == DateTimeFormatInfoScanner.CJKYearSuff[0]) ||
(i + tokenLen < format.Length - 1 && format[i + tokenLen] == '\'' && format[i + tokenLen + 1] == DateTimeFormatInfoScanner.CJKYearSuff[0])))
InsertHash(temp, CJKMinuteSuff, TokenType.SEP_MinuteSuff, 0);
InsertHash(temp, CJKSecondSuff, TokenType.SEP_SecondSuff, 0);
- if (!AppContextSwitches.EnforceLegacyJapaneseDateParsing && Calendar.ID == CalendarId.JAPAN)
+ if (!LocalAppContextSwitches.EnforceLegacyJapaneseDateParsing && Calendar.ID == CalendarId.JAPAN)
{
// We need to support parsing the dates has the start of era symbol which means it is year 1 in the era.
// The start of era symbol has to be followed by the year symbol suffix, otherwise it would be invalid date.
// Allow the parser to recognize the case when having some date part followed by JapaneseEraStart "\u5143"
// without spaces in between. e.g. Era name followed by \u5143 in the date formats ggy.
// Also, allow recognizing the year suffix symbol "\u5e74" followed the JapaneseEraStart "\u5143"
- if (!AppContextSwitches.EnforceLegacyJapaneseDateParsing && Calendar.ID == CalendarId.JAPAN &&
+ if (!LocalAppContextSwitches.EnforceLegacyJapaneseDateParsing && Calendar.ID == CalendarId.JAPAN &&
(
// something like ggy, era followed by year and the year is specified using the JapaneseEraStart "\u5143"
nextCh == JapaneseEraStart[0] ||
// ParseJapaneseEraStart will be called when parsing the year number. We can have dates which not listing
// the year as a number and listing it as JapaneseEraStart symbol (which means year 1).
// This will be legitimate date to recognize.
- if (AppContextSwitches.EnforceLegacyJapaneseDateParsing || dtfi.Calendar.ID != CalendarId.JAPAN || !str.GetNext())
+ if (LocalAppContextSwitches.EnforceLegacyJapaneseDateParsing || dtfi.Calendar.ID != CalendarId.JAPAN || !str.GetNext())
return false;
if (str.m_current != DateTimeFormatInfo.JapaneseEraStart[0])
{
return m_EraInfo[i].yearOffset;
}
- else if (!AppContextSwitches.EnforceJapaneseEraYearRanges)
+ else if (!LocalAppContextSwitches.EnforceJapaneseEraYearRanges)
{
// If we got the year number exceeding the era max year number, this still possible be valid as the date can be created before
// introducing new eras after the era we are checking. we'll loop on the eras after the era we have and ensure the year
--- /dev/null
+// 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.Runtime.CompilerServices;
+
+namespace System
+{
+ // Helper method for local caching of compatibility quirks. Keep this lean and simple - this file is included into
+ // every framework assembly that implements any compatibility quirks.
+ internal static partial class LocalAppContextSwitches
+ {
+ // Returns value of given switch using provided cache.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue)
+ {
+ // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false
+ if (cachedSwitchValue < 0) return false;
+ if (cachedSwitchValue > 0) return true;
+
+ return GetCachedSwitchValueInternal(switchName, ref cachedSwitchValue);
+ }
+
+ private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue)
+ {
+ bool isSwitchEnabled;
+ AppContext.TryGetSwitch(switchName, out isSwitchEnabled);
+
+ AppContext.TryGetSwitch(@"TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching);
+ if (!disableCaching)
+ {
+ cachedSwitchValue = isSwitchEnabled ? 1 /*true*/ : -1 /*false*/;
+ }
+
+ return isSwitchEnabled;
+ }
+ }
+}
--- /dev/null
+// 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.Runtime.CompilerServices;
+
+namespace System
+{
+ internal static partial class LocalAppContextSwitches
+ {
+ private static int s_enforceJapaneseEraYearRanges;
+ public static bool EnforceJapaneseEraYearRanges
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return GetCachedSwitchValue("Switch.System.Globalization.EnforceJapaneseEraYearRanges", ref s_enforceJapaneseEraYearRanges);
+ }
+ }
+
+ private static int s_formatJapaneseFirstYearAsANumber;
+ public static bool FormatJapaneseFirstYearAsANumber
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return GetCachedSwitchValue("Switch.System.Globalization.FormatJapaneseFirstYearAsANumber", ref s_formatJapaneseFirstYearAsANumber);
+ }
+ }
+ private static int s_enforceLegacyJapaneseDateParsing;
+ public static bool EnforceLegacyJapaneseDateParsing
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return GetCachedSwitchValue("Switch.System.Globalization.EnforceLegacyJapaneseDateParsing", ref s_enforceLegacyJapaneseDateParsing);
+ }
+ }
+
+ private static int s_preserveEventListnerObjectIdentity;
+ public static bool PreserveEventListnerObjectIdentity
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return GetCachedSwitchValue("Switch.System.Diagnostics.EventSource.PreserveEventListnerObjectIdentity", ref s_preserveEventListnerObjectIdentity);
+ }
+ }
+ }
+}