From 55000685718c7779f8badc90b9b009a0f1c296f2 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Sat, 24 Nov 2018 06:59:54 -0800 Subject: [PATCH] Move AppDomain local data store to AppContext and cleanup AppContext (dotnet/coreclr#21180) Contributes to dotnet/coreclr#21028 Commit migrated from https://github.com/dotnet/coreclr/commit/55d1363ac36735008dee4d11e10934b8ee94e80b --- .../System.Private.CoreLib.csproj | 5 - .../src/System/AppContext/AppContext.cs | 147 ++++++------------- .../AppContextDefaultValues.CoreClrOverrides.cs | 21 --- .../AppContextDefaultValues.Defaults.Central.cs | 33 ----- .../AppContext/AppContextDefaultValues.Defaults.cs | 60 -------- .../System/AppContext/AppContextDefaultValues.cs | 156 --------------------- .../src/System/AppContext/AppContextSwitches.cs | 99 ------------- .../System.Private.CoreLib/src/System/AppDomain.cs | 47 +------ .../src/System/Resources/ResourceManager.cs | 2 +- src/coreclr/src/vm/mscorlib.h | 1 - src/coreclr/src/vm/object.h | 1 - .../src/System.Private.CoreLib.Shared.projitems | 2 + .../src/System/Diagnostics/Tracing/EventSource.cs | 4 +- .../src/System/Globalization/CultureData.cs | 2 +- .../src/System/Globalization/DateTimeFormat.cs | 2 +- .../src/System/Globalization/DateTimeFormatInfo.cs | 4 +- .../src/System/Globalization/DateTimeParse.cs | 2 +- .../Globalization/GregorianCalendarHelper.cs | 2 +- .../src/System/LocalAppContextSwitches.Common.cs | 39 ++++++ .../src/System/LocalAppContextSwitches.cs | 51 +++++++ 20 files changed, 146 insertions(+), 534 deletions(-) delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.CoreClrOverrides.cs delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.Defaults.Central.cs delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.Defaults.cs delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.cs delete mode 100644 src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextSwitches.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.Common.cs create mode 100644 src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs diff --git a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj index c62827d..88f9062 100644 --- a/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -242,11 +242,6 @@ - - - - - diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContext.cs b/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContext.cs index 3e192d5..3fe3909 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContext.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContext.cs @@ -7,29 +7,20 @@ using System.Collections.Generic; 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 s_switchMap = new Dictionary(); + private static Dictionary s_dataStore = new Dictionary(); + private static Dictionary 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 @@ -38,7 +29,7 @@ namespace System { // 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; @@ -62,12 +53,26 @@ namespace System 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 @@ -116,7 +121,6 @@ namespace System } } - #region Switch APIs /// /// Try to get the value of the switch. /// @@ -130,88 +134,24 @@ namespace System 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; } /// @@ -226,25 +166,16 @@ namespace System 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(), null); } - } - /// - /// 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. - /// - internal static void DefineSwitchDefault(string switchName, bool isEnabled) - { - s_switchMap[switchName] = isEnabled ? SwitchValueState.HasTrueValue : SwitchValueState.HasFalseValue; + lock (s_switches) + { + s_switches[switchName] = isEnabled; + } } - #endregion } } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.CoreClrOverrides.cs b/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.CoreClrOverrides.cs deleted file mode 100644 index 89893c6..0000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.CoreClrOverrides.cs +++ /dev/null @@ -1,21 +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. - -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/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.Defaults.Central.cs b/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.Defaults.Central.cs deleted file mode 100644 index 92c9917..0000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.Defaults.Central.cs +++ /dev/null @@ -1,33 +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. - - -// -// 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 - { - /// - /// This method allows reading the override for a switch. - /// The implementation is platform specific - /// - 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/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.Defaults.cs b/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.Defaults.cs deleted file mode 100644 index 4611019..0000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.Defaults.cs +++ /dev/null @@ -1,60 +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; - -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(); - } - } -} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.cs b/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.cs deleted file mode 100644 index bfbb989..0000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextDefaultValues.cs +++ /dev/null @@ -1,156 +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; -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); - } - - /// - /// We have this separate method for getting the parsed elements out of the TargetFrameworkName so we can - /// more easily support this on other platforms. - /// - 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: ", Version=[v|V], 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); - } -} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextSwitches.cs b/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextSwitches.cs deleted file mode 100644 index 57185ea..0000000 --- a/src/coreclr/src/System.Private.CoreLib/src/System/AppContext/AppContextSwitches.cs +++ /dev/null @@ -1,99 +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; -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; - } - } -} diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/AppDomain.cs b/src/coreclr/src/System.Private.CoreLib/src/System/AppDomain.cs index 1d416fe..c1c74d0 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/AppDomain.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/AppDomain.cs @@ -28,7 +28,6 @@ namespace System // of these fields cannot be changed without changing the layout in // the EE- AppDomainBaseObject in this case) - private Dictionary _LocalStore; public event AssemblyLoadEventHandler AssemblyLoad; private ResolveEventHandler _TypeResolve; @@ -142,13 +141,13 @@ namespace System /// 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); } } @@ -215,32 +214,6 @@ namespace System [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(); @@ -399,14 +372,6 @@ namespace System null; } - private Dictionary LocalStore - { - get - { - return _LocalStore ?? (_LocalStore = new Dictionary()); - } - } - [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)] private static extern void nSetNativeDllSearchDirectories(string paths); @@ -439,7 +404,7 @@ namespace System { if (propertyNames[i] != null) { - ad.SetData(propertyNames[i], propertyValues[i]); + AppContext.SetData(propertyNames[i], propertyValues[i]); } } } diff --git a/src/coreclr/src/System.Private.CoreLib/src/System/Resources/ResourceManager.cs b/src/coreclr/src/System.Private.CoreLib/src/System/Resources/ResourceManager.cs index 6c7f6e7..7c94e12 100644 --- a/src/coreclr/src/System.Private.CoreLib/src/System/Resources/ResourceManager.cs +++ b/src/coreclr/src/System.Private.CoreLib/src/System/Resources/ResourceManager.cs @@ -766,7 +766,7 @@ namespace System.Resources 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; diff --git a/src/coreclr/src/vm/mscorlib.h b/src/coreclr/src/vm/mscorlib.h index 7b9831c..768bf8f 100644 --- a/src/coreclr/src/vm/mscorlib.h +++ b/src/coreclr/src/vm/mscorlib.h @@ -67,7 +67,6 @@ DEFINE_FIELD(ACCESS_VIOLATION_EXCEPTION, TARGET, _target) 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) diff --git a/src/coreclr/src/vm/object.h b/src/coreclr/src/vm/object.h index 1e10756..179d9c4 100644 --- a/src/coreclr/src/vm/object.h +++ b/src/coreclr/src/vm/object.h @@ -1575,7 +1575,6 @@ class AppDomainBaseObject : public MarshalByRefObjectBaseObject // 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 diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index ec25efb..3b1eaaa 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -318,6 +318,8 @@ + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs index 7b8898e..cba324c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventSource.cs @@ -1919,9 +1919,9 @@ namespace System.Diagnostics.Tracing #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, diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs index c747f98..9d1cdf1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs @@ -2134,7 +2134,7 @@ namespace System.Globalization // 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. diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index b0c6342..0357086 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -655,7 +655,7 @@ namespace System 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]))) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs index a2665e4..ca6809a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs @@ -2336,7 +2336,7 @@ namespace System.Globalization 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. @@ -2650,7 +2650,7 @@ namespace System.Globalization // 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] || diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index 66d2ba6..c33e5b5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -3836,7 +3836,7 @@ new DS[] { DS.ERROR, DS.TX_NNN, DS.TX_NNN, DS.TX_NNN, DS.ERROR, DS.ERROR, // 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]) diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs index 34206b3..ebe6892 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/GregorianCalendarHelper.cs @@ -153,7 +153,7 @@ namespace System.Globalization { 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 diff --git a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.Common.cs b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.Common.cs new file mode 100644 index 0000000..521848f --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.Common.cs @@ -0,0 +1,39 @@ +// 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; + } + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs new file mode 100644 index 0000000..8194f87 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs @@ -0,0 +1,51 @@ +// 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); + } + } + } +} -- 2.7.4