diff options
Diffstat (limited to 'src/mscorlib/src/System/Runtime/Versioning')
7 files changed, 933 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs b/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs new file mode 100644 index 0000000000..99e30b5488 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs @@ -0,0 +1,485 @@ +// 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. + +/*============================================================ +** +** +** +** +** Purpose: This class is used to determine which binary compatibility +** behaviors are enabled at runtime. A type for +** tracking which target Framework an app was built against, or an +** appdomain-wide setting from the host telling us which .NET +** Framework version we should emulate. +** +** +===========================================================*/ +using System; +using System.Diagnostics.Contracts; +using System.Globalization; +using System.Runtime.CompilerServices; + +namespace System.Runtime.Versioning +{ + // Provides a simple way to test whether an application was built against specific .NET Framework + // flavors and versions, with the intent of allowing Framework developers to mimic behavior of older + // Framework releases. This allows us to make behavioral breaking changes in a binary compatible way, + // for an application. This works at the per-AppDomain level, not process nor per-Assembly. + // + // To opt into newer behavior, applications must specify a TargetFrameworkAttribute on their assembly + // saying what version they targeted, or a host must set this when creating an AppDomain. Note + // that command line apps don't have this attribute! + // + // To use this class: + // Developers need to figure out whether they're working on the phone, desktop, or Silverlight, and + // what version they are introducing a breaking change in. Pick one predicate below, and use that + // to decide whether to run the new or old behavior. Example: + // + // if (BinaryCompatibility.TargetsAtLeast_Phone_V7_1) { + // // new behavior for phone 7.1 and other releases where we will integrate this change, like .NET Framework 4.5 + // } + // else { + // // Legacy behavior + // } + // + // If you are making a breaking change in one specific branch that won't be integrated normally to + // all other branches (ie, say you're making breaking changes to Windows Phone 8 after .NET Framework v4.5 + // has locked down for release), then add in specific predicates for each relevant platform. + // + // Maintainers of this class: + // Revisit the table once per release, perhaps at the end of the last coding milestone, to verify a + // default policy saying whether all quirks from a particular flavor & release should be enabled in + // other releases (ie, should all Windows Phone 8.0 quirks be enabled in .NET Framework v5)? + // + // History: + // Here is the order in which releases were made along with some basic integration information. The idea + // is to track what set of compatibility features are present in each other. + // While we cannot guarantee this list is perfectly linear (ie, a feature could be implemented in the last + // few weeks before shipping and make it into only one of three concommittent releases due to triaging), + // this is a good high level summary of code flow. + // + // Desktop Silverlight Windows Phone + // .NET Framework 3.0 -> Silverlight 2 + // .NET Framework 3.5 + // Silverlight 3 + // Silverlight 4 + // .NET Framework 4 Phone 8.0 + // .NET Framework 4.5 Phone 8.1 + // .NET Framework 4.5.1 Phone 8.1 + // + // (Note: Windows Phone 7.0 was built using the .NET Compact Framework, which forked around v1 or v1.1) + // + // Compatibility Policy decisions: + // If we cannot determine that an app was built for a newer .NET Framework (ie, the app has no + // TargetFrameworkAttribute), then quirks will be enabled to emulate older behavior. + // As such, your test code should define the TargetFrameworkAttribute (which VS does for you) + // if you want to see the new behavior! + [FriendAccessAllowed] + internal static class BinaryCompatibility + { + // Use this for new behavior introduced in the phone branch. It will do the right thing for desktop & SL. + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Phone_V7_1 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Phone_V7_1; } } + + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Phone_V8_0 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Phone_V8_0; } } + + // Use this for new behavior introduced in the Desktop branch. It will do the right thing for Phone & SL. + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5_1 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5_1; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5_2 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5_2; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5_3 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5_3; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V4_5_4 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V4_5_4; } } + + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Desktop_V5_0 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Desktop_V5_0; } } + + // Use this for new behavior introduced in the Silverlight branch. It will do the right thing for desktop & Phone. + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Silverlight_V4 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Silverlight_V4; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Silverlight_V5 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Silverlight_V5; } } + [FriendAccessAllowed] + internal static bool TargetsAtLeast_Silverlight_V6 { [FriendAccessAllowed] get { return s_map.TargetsAtLeast_Silverlight_V6; } } + + [FriendAccessAllowed] + internal static TargetFrameworkId AppWasBuiltForFramework { + [FriendAccessAllowed] + get { + Contract.Ensures(Contract.Result<TargetFrameworkId>() > TargetFrameworkId.NotYetChecked); + + if (s_AppWasBuiltForFramework == TargetFrameworkId.NotYetChecked) + ReadTargetFrameworkId(); + + return s_AppWasBuiltForFramework; + } + } + + // Version number is major * 10000 + minor * 100 + build (ie, 4.5.1.0 would be version 40501). + [FriendAccessAllowed] + internal static int AppWasBuiltForVersion { + [FriendAccessAllowed] + get { + Contract.Ensures(Contract.Result<int>() > 0 || s_AppWasBuiltForFramework == TargetFrameworkId.Unspecified); + + if (s_AppWasBuiltForFramework == TargetFrameworkId.NotYetChecked) + ReadTargetFrameworkId(); + + Contract.Assert(s_AppWasBuiltForFramework != TargetFrameworkId.Unrecognized); + + return s_AppWasBuiltForVersion; + } + } + + #region private + private static TargetFrameworkId s_AppWasBuiltForFramework; + // Version number is major * 10000 + minor * 100 + build (ie, 4.5.1.0 would be version 40501). + private static int s_AppWasBuiltForVersion; + + readonly static BinaryCompatibilityMap s_map = new BinaryCompatibilityMap(); + + // For parsing a target Framework moniker, from the FrameworkName class + private const char c_componentSeparator = ','; + private const char c_keyValueSeparator = '='; + private const char c_versionValuePrefix = 'v'; + private const String c_versionKey = "Version"; + private const String c_profileKey = "Profile"; + + /// <summary> + /// BinaryCompatibilityMap is basically a bitvector. There is a boolean field for each of the + /// properties in BinaryCompatibility + /// </summary> + private sealed class BinaryCompatibilityMap + { + // A bit for each property + internal bool TargetsAtLeast_Phone_V7_1; + internal bool TargetsAtLeast_Phone_V8_0; + internal bool TargetsAtLeast_Phone_V8_1; + internal bool TargetsAtLeast_Desktop_V4_5; + internal bool TargetsAtLeast_Desktop_V4_5_1; + internal bool TargetsAtLeast_Desktop_V4_5_2; + internal bool TargetsAtLeast_Desktop_V4_5_3; + internal bool TargetsAtLeast_Desktop_V4_5_4; + internal bool TargetsAtLeast_Desktop_V5_0; + internal bool TargetsAtLeast_Silverlight_V4; + internal bool TargetsAtLeast_Silverlight_V5; + internal bool TargetsAtLeast_Silverlight_V6; + + internal BinaryCompatibilityMap() + { + AddQuirksForFramework(AppWasBuiltForFramework, AppWasBuiltForVersion); + } + + // The purpose of this method is to capture information about integrations & behavioral compatibility + // between our multiple different release vehicles. IE, if a behavior shows up in Silverlight version 5, + // does it show up in the .NET Framework version 4.5 and Windows Phone 8? + // Version number is major * 10000 + minor * 100 + build (ie, 4.5.1.0 would be version 40501). + private void AddQuirksForFramework(TargetFrameworkId builtAgainstFramework, int buildAgainstVersion) + { + Contract.Requires(buildAgainstVersion > 0 || builtAgainstFramework == TargetFrameworkId.Unspecified); + + switch (builtAgainstFramework) + { + case TargetFrameworkId.NetFramework: + case TargetFrameworkId.NetCore: // Treat Windows 8 tailored apps as normal desktop apps - same product + if (buildAgainstVersion >= 50000) + TargetsAtLeast_Desktop_V5_0 = true; + + // Potential 4.5 servicing releases + if (buildAgainstVersion >= 40504) + TargetsAtLeast_Desktop_V4_5_4 = true; + if (buildAgainstVersion >= 40503) + TargetsAtLeast_Desktop_V4_5_3 = true; + if (buildAgainstVersion >= 40502) + TargetsAtLeast_Desktop_V4_5_2 = true; + if (buildAgainstVersion >= 40501) + TargetsAtLeast_Desktop_V4_5_1 = true; + + if (buildAgainstVersion >= 40500) + { + TargetsAtLeast_Desktop_V4_5 = true; + // On XX/XX/XX we integrated all changes from the phone V7_1 into the branch from which contains Desktop V4_5, thus + // Any application built for V4_5 (or above) should have all the quirks for Phone V7_1 turned on. + AddQuirksForFramework(TargetFrameworkId.Phone, 70100); + // All Silverlight 5 behavior should be in the .NET Framework version 4.5 + AddQuirksForFramework(TargetFrameworkId.Silverlight, 50000); + } + break; + + case TargetFrameworkId.Phone: + if (buildAgainstVersion >= 80000) + { + // This is for Apollo apps. For Apollo apps we don't want to enable any of the 4.5 or 4.5.1 quirks + TargetsAtLeast_Phone_V8_0 = true; + //TargetsAtLeast_Desktop_V4_5 = true; + } + if (buildAgainstVersion >= 80100) + { + // For WindowsPhone 8.1 and SL 8.1 scenarios we want to enable both 4.5 and 4.5.1 quirks. + TargetsAtLeast_Desktop_V4_5 = true; + TargetsAtLeast_Desktop_V4_5_1 = true; + } + + if (buildAgainstVersion >= 710) + TargetsAtLeast_Phone_V7_1 = true; + break; + + case TargetFrameworkId.Silverlight: + if (buildAgainstVersion >= 40000) + TargetsAtLeast_Silverlight_V4 = true; + + if (buildAgainstVersion >= 50000) + TargetsAtLeast_Silverlight_V5 = true; + + if (buildAgainstVersion >= 60000) + { + TargetsAtLeast_Silverlight_V6 = true; + } + break; + + case TargetFrameworkId.Unspecified: + break; + + case TargetFrameworkId.NotYetChecked: + case TargetFrameworkId.Unrecognized: + Contract.Assert(false, "Bad framework kind"); + break; + default: + Contract.Assert(false, "Error: we introduced a new Target Framework but did not update our binary compatibility map"); + break; + } + } + } + + #region String Parsing + + // If this doesn't work, perhaps we could fall back to parsing the metadata version number. + private static bool ParseTargetFrameworkMonikerIntoEnum(String targetFrameworkMoniker, out TargetFrameworkId targetFramework, out int targetFrameworkVersion) + { + Contract.Requires(!String.IsNullOrEmpty(targetFrameworkMoniker)); + + targetFramework = TargetFrameworkId.NotYetChecked; + targetFrameworkVersion = 0; + + String identifier = null; + String profile = null; + ParseFrameworkName(targetFrameworkMoniker, out identifier, out targetFrameworkVersion, out profile); + + switch (identifier) + { + case ".NETFramework": + targetFramework = TargetFrameworkId.NetFramework; + break; + + case ".NETPortable": + targetFramework = TargetFrameworkId.Portable; + break; + + case ".NETCore": + targetFramework = TargetFrameworkId.NetCore; + break; + + case "WindowsPhone": + if (targetFrameworkVersion >= 80100) + { + // A TFM of the form WindowsPhone,Version=v8.1 corresponds to SL 8.1 scenario + // and gets the same quirks as WindowsPhoneApp\v8.1 store apps. + targetFramework = TargetFrameworkId.Phone; + } + else + { + // There is no TFM for Apollo or below and hence we assign the targetFramework to Unspecified. + targetFramework = TargetFrameworkId.Unspecified; + } + break; + + case "WindowsPhoneApp": + targetFramework = TargetFrameworkId.Phone; + break; + + case "Silverlight": + targetFramework = TargetFrameworkId.Silverlight; + // Windows Phone 7 is Silverlight,Version=v4.0,Profile=WindowsPhone + // Windows Phone 7.1 is Silverlight,Version=v4.0,Profile=WindowsPhone71 + if (!String.IsNullOrEmpty(profile)) + { + if (profile == "WindowsPhone") + { + targetFramework = TargetFrameworkId.Phone; + targetFrameworkVersion = 70000; + } + else if (profile == "WindowsPhone71") + { + targetFramework = TargetFrameworkId.Phone; + targetFrameworkVersion = 70100; + } + else if (profile == "WindowsPhone8") + { + targetFramework = TargetFrameworkId.Phone; + targetFrameworkVersion = 80000; + } + else if (profile.StartsWith("WindowsPhone", StringComparison.Ordinal)) + { + Contract.Assert(false, "This is a phone app, but we can't tell what version this is!"); + targetFramework = TargetFrameworkId.Unrecognized; + targetFrameworkVersion = 70100; + } + else + { + Contract.Assert(false, String.Format(CultureInfo.InvariantCulture, "Unrecognized Silverlight profile \"{0}\". What is this, an XBox app?", profile)); + targetFramework = TargetFrameworkId.Unrecognized; + } + } + break; + + default: + Contract.Assert(false, String.Format(CultureInfo.InvariantCulture, "Unrecognized Target Framework Moniker in our Binary Compatibility class. Framework name: \"{0}\"", targetFrameworkMoniker)); + targetFramework = TargetFrameworkId.Unrecognized; + break; + } + + return true; + } + + // 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 void ParseFrameworkName(String frameworkName, out String identifier, out int version, out String profile) + { + if (frameworkName == null) + { + throw new ArgumentNullException("frameworkName"); + } + if (frameworkName.Length == 0) + { + throw new ArgumentException(Environment.GetResourceString("Argument_StringZeroLength"), "frameworkName"); + } + Contract.EndContractBlock(); + + String[] components = frameworkName.Split(c_componentSeparator); + version = 0; + + // Identifer and Version are required, Profile is optional. + if (components.Length < 2 || components.Length > 3) + { + throw new ArgumentException(Environment.GetResourceString("Argument_FrameworkNameTooShort"), "frameworkName"); + } + + // + // 1) Parse the "Identifier", which must come first. Trim any whitespace + // + identifier = components[0].Trim(); + + if (identifier.Length == 0) + { + throw new ArgumentException(Environment.GetResourceString("Argument_FrameworkNameInvalid"), "frameworkName"); + } + + 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) + { + throw new ArgumentException(Environment.GetResourceString("SR.Argument_FrameworkNameInvalid"), "frameworkName"); + } + + // 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 + { + throw new ArgumentException(Environment.GetResourceString("Argument_FrameworkNameInvalid"), "frameworkName"); + } + } + + if (!versionFound) + { + throw new ArgumentException(Environment.GetResourceString("Argument_FrameworkNameMissingVersion"), "frameworkName"); + } + } + + [System.Security.SecuritySafeCritical] + private static void ReadTargetFrameworkId() + { + String targetFrameworkName = AppDomain.CurrentDomain.GetTargetFrameworkName(); + + var overrideValue = System.Runtime.Versioning.CompatibilitySwitch.GetValueInternal("TargetFrameworkMoniker"); + if (!string.IsNullOrEmpty(overrideValue)) + { + targetFrameworkName = overrideValue; + } + + // Write to a local then to _targetFramework, after writing the version number. + TargetFrameworkId fxId; + int fxVersion = 0; + if (targetFrameworkName == null) + { +#if FEATURE_CORECLR + // if we don't have a value for targetFrameworkName we need to figure out if we should give the newest behavior or not. + if (CompatibilitySwitches.UseLatestBehaviorWhenTFMNotSpecified) + { + fxId = TargetFrameworkId.NetFramework; + fxVersion = 50000; // We are going to default to the latest value for version that we have in our code. + } + else +#endif + fxId = TargetFrameworkId.Unspecified; + } + else if (!ParseTargetFrameworkMonikerIntoEnum(targetFrameworkName, out fxId, out fxVersion)) + fxId = TargetFrameworkId.Unrecognized; + + s_AppWasBuiltForFramework = fxId; + s_AppWasBuiltForVersion = fxVersion; + } + #endregion String Parsing + + #endregion private + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/CompatibilitySwitch.cs b/src/mscorlib/src/System/Runtime/Versioning/CompatibilitySwitch.cs new file mode 100644 index 0000000000..b06c42437c --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/CompatibilitySwitch.cs @@ -0,0 +1,61 @@ +// 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.Globalization; +using System.Runtime.CompilerServices; + +namespace System.Runtime.Versioning +{ + public static class CompatibilitySwitch + { + /* This class contains 3 sets of api: + * 1. internal apis : These apis are supposed to be used by mscorlib.dll and other assemblies which use the <runtime> section in config + * These apis query for the value of quirk not only in windows quirk DB but also in runtime section of config files, + * registry and environment vars. + * 2. public apis : These apis are supposed to be used by FX assemblies which do not read the runtime section of config files and have + * have their own section in config files or do not use configs at all. + * + * 3. specialized apis: These apis are defined in order to retrieve a specific value defined in CLR Config. That value can have specific look-up rules + * for the order and location of the config sources used. + * + * These apis are for internal use only for FX assmeblies. It has not been decided if they can be used by OOB components due to EULA restrictions + */ + [System.Security.SecurityCritical] + public static bool IsEnabled(string compatibilitySwitchName) + { + return IsEnabledInternalCall(compatibilitySwitchName, true); + } + + [System.Security.SecurityCritical] + public static string GetValue(string compatibilitySwitchName) + { + // This is used by AppContext.TryGetSwitch to check switch overrides in the Windows Quirk DB + // If this method is updated to check other locations than the DB, please ensure compat with + // the AppContext class. + return GetValueInternalCall(compatibilitySwitchName, true); + } + + [System.Security.SecurityCritical] + internal static bool IsEnabledInternal(string compatibilitySwitchName) + { + return IsEnabledInternalCall(compatibilitySwitchName, false); + } + + [System.Security.SecurityCritical] + internal static string GetValueInternal(string compatibilitySwitchName) + { + return GetValueInternalCall(compatibilitySwitchName, false); + } + + [System.Security.SecurityCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern string GetAppContextOverridesInternalCall(); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern bool IsEnabledInternalCall(string compatibilitySwitchName, bool onlyDB); + + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern string GetValueInternalCall(string compatibilitySwitchName, bool onlyDB); + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/ComponentGuaranteesAttribute.cs b/src/mscorlib/src/System/Runtime/Versioning/ComponentGuaranteesAttribute.cs new file mode 100644 index 0000000000..0f906d518a --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/ComponentGuaranteesAttribute.cs @@ -0,0 +1,44 @@ +// 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. + +/*============================================================ +** +** +** +** Purpose: Tracking whether a component signs up for a +** a strong contract spanning multiple versions. +** +===========================================================*/ +using System; + +namespace System.Runtime.Versioning { + + [Flags] + [Serializable] + public enum ComponentGuaranteesOptions + { + None = 0, + Exchange = 0x1, + Stable = 0x2, + SideBySide = 0x4, + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | + AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Delegate | + AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | + AttributeTargets.Constructor | AttributeTargets.Event, + AllowMultiple = false, Inherited = false)] + public sealed class ComponentGuaranteesAttribute : Attribute { + private ComponentGuaranteesOptions _guarantees; + + public ComponentGuaranteesAttribute(ComponentGuaranteesOptions guarantees) + { + _guarantees = guarantees; + } + + public ComponentGuaranteesOptions Guarantees { + get { return _guarantees; } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs b/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.cs new file mode 100644 index 0000000000..0a9845d9c2 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/NonVersionableAttribute.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. + +/*============================================================ +** +** Class: NonVersionableAttribute +** +** +** The [NonVersionable] attribute is applied to indicate that the implementation +** of a particular member or layout of a struct cannot be changed for given platform in incompatible way. +** This allows cross-module inlining of methods and data structures whose implementation +** is never changed in ReadyToRun native images. Any changes to such members or types would be +** breaking changes for ReadyToRun. +** +===========================================================*/ +using System; +using System.Diagnostics; + +namespace System.Runtime.Versioning { + + // This Conditional is here to strip the annotations for targets where ReadyToRun is not supported. + // If this attribute is ever made public, this Conditional should be removed. + [Conditional("FEATURE_READYTORUN")] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor, + AllowMultiple = false, Inherited = false)] + sealed class NonVersionableAttribute : Attribute { + + public NonVersionableAttribute() + { + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/ResourceAttributes.cs b/src/mscorlib/src/System/Runtime/Versioning/ResourceAttributes.cs new file mode 100644 index 0000000000..78a9ddbd07 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/ResourceAttributes.cs @@ -0,0 +1,237 @@ +// 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. + +/*============================================================ +** +** Purpose: Resource annotation rules. +** +===========================================================*/ +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Text; +using Microsoft.Win32; +using System.Diagnostics.Contracts; + +namespace System.Runtime.Versioning +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] + [Conditional("RESOURCE_ANNOTATION_WORK")] + public sealed class ResourceConsumptionAttribute : Attribute + { + private ResourceScope _consumptionScope; + private ResourceScope _resourceScope; + + public ResourceConsumptionAttribute(ResourceScope resourceScope) + { + _resourceScope = resourceScope; + _consumptionScope = _resourceScope; + } + + public ResourceConsumptionAttribute(ResourceScope resourceScope, ResourceScope consumptionScope) + { + _resourceScope = resourceScope; + _consumptionScope = consumptionScope; + } + + public ResourceScope ResourceScope { + get { return _resourceScope; } + } + + public ResourceScope ConsumptionScope { + get { return _consumptionScope; } + } + } + + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Constructor, Inherited = false)] + [Conditional("RESOURCE_ANNOTATION_WORK")] + public sealed class ResourceExposureAttribute : Attribute + { + private ResourceScope _resourceExposureLevel; + + public ResourceExposureAttribute(ResourceScope exposureLevel) + { + _resourceExposureLevel = exposureLevel; + } + + public ResourceScope ResourceExposureLevel { + get { return _resourceExposureLevel; } + } + } + + + // Default visibility is Public, which isn't specified in this enum. + // Public == the lack of Private or Assembly + // Does this actually work? Need to investigate that. + [Flags] + public enum ResourceScope + { + None = 0, + // Resource type + Machine = 0x1, + Process = 0x2, + AppDomain = 0x4, + Library = 0x8, + // Visibility + Private = 0x10, // Private to this one class. + Assembly = 0x20, // Assembly-level, like C#'s "internal" + } + + + [Flags] + internal enum SxSRequirements + { + None = 0, + AppDomainID = 0x1, + ProcessID = 0x2, + CLRInstanceID = 0x4, // for multiple CLR's within the process + AssemblyName = 0x8, + TypeName = 0x10 + } + + public static class VersioningHelper + { + // These depend on the exact values given to members of the ResourceScope enum. + private const ResourceScope ResTypeMask = ResourceScope.Machine | ResourceScope.Process | ResourceScope.AppDomain | ResourceScope.Library; + private const ResourceScope VisibilityMask = ResourceScope.Private | ResourceScope.Assembly; + + [System.Security.SecuritySafeCritical] + [MethodImpl(MethodImplOptions.InternalCall)] + private static extern int GetRuntimeId(); + + public static String MakeVersionSafeName(String name, ResourceScope from, ResourceScope to) + { + return MakeVersionSafeName(name, from, to, null); + } + + [System.Security.SecuritySafeCritical] // auto-generated + public static String MakeVersionSafeName(String name, ResourceScope from, ResourceScope to, Type type) + { + ResourceScope fromResType = from & ResTypeMask; + ResourceScope toResType = to & ResTypeMask; + if (fromResType > toResType) + throw new ArgumentException(Environment.GetResourceString("Argument_ResourceScopeWrongDirection", fromResType, toResType), "from"); + + SxSRequirements requires = GetRequirements(to, from); + + if ((requires & (SxSRequirements.AssemblyName | SxSRequirements.TypeName)) != 0 && type == null) + throw new ArgumentNullException("type", Environment.GetResourceString("ArgumentNull_TypeRequiredByResourceScope")); + + // Add in process ID, CLR base address, and appdomain ID's. Also, use a character identifier + // to ensure that these can never accidentally overlap (ie, you create enough appdomains and your + // appdomain ID might equal your process ID). + StringBuilder safeName = new StringBuilder(name); + char separator = '_'; + if ((requires & SxSRequirements.ProcessID) != 0) { + safeName.Append(separator); + safeName.Append('p'); + safeName.Append(Win32Native.GetCurrentProcessId()); + } + if ((requires & SxSRequirements.CLRInstanceID) != 0) { + String clrID = GetCLRInstanceString(); + safeName.Append(separator); + safeName.Append('r'); + safeName.Append(clrID); + } + if ((requires & SxSRequirements.AppDomainID) != 0) { + safeName.Append(separator); + safeName.Append("ad"); + safeName.Append(AppDomain.CurrentDomain.Id); + } + if ((requires & SxSRequirements.TypeName) != 0) { + safeName.Append(separator); + safeName.Append(type.Name); + } + if ((requires & SxSRequirements.AssemblyName) != 0) { + safeName.Append(separator); + safeName.Append(type.Assembly.FullName); + } + return safeName.ToString(); + } + + private static String GetCLRInstanceString() + { + int id = GetRuntimeId(); + return id.ToString(CultureInfo.InvariantCulture); + } + + private static SxSRequirements GetRequirements(ResourceScope consumeAsScope, ResourceScope calleeScope) + { + SxSRequirements requires = SxSRequirements.None; + + switch(calleeScope & ResTypeMask) { + case ResourceScope.Machine: + switch(consumeAsScope & ResTypeMask) { + case ResourceScope.Machine: + // No work + break; + + case ResourceScope.Process: + requires |= SxSRequirements.ProcessID; + break; + + case ResourceScope.AppDomain: + requires |= SxSRequirements.AppDomainID | SxSRequirements.CLRInstanceID | SxSRequirements.ProcessID; + break; + + default: + throw new ArgumentException(Environment.GetResourceString("Argument_BadResourceScopeTypeBits", consumeAsScope), "consumeAsScope"); + } + break; + + case ResourceScope.Process: + if ((consumeAsScope & ResourceScope.AppDomain) != 0) + requires |= SxSRequirements.AppDomainID | SxSRequirements.CLRInstanceID; + break; + + case ResourceScope.AppDomain: + // No work + break; + + default: + throw new ArgumentException(Environment.GetResourceString("Argument_BadResourceScopeTypeBits", calleeScope), "calleeScope"); + } + + switch(calleeScope & VisibilityMask) { + case ResourceScope.None: // Public - implied + switch(consumeAsScope & VisibilityMask) { + case ResourceScope.None: // Public - implied + // No work + break; + + case ResourceScope.Assembly: + requires |= SxSRequirements.AssemblyName; + break; + + case ResourceScope.Private: + requires |= SxSRequirements.TypeName | SxSRequirements.AssemblyName; + break; + + default: + throw new ArgumentException(Environment.GetResourceString("Argument_BadResourceScopeVisibilityBits", consumeAsScope), "consumeAsScope"); + } + break; + + case ResourceScope.Assembly: + if ((consumeAsScope & ResourceScope.Private) != 0) + requires |= SxSRequirements.TypeName; + break; + + case ResourceScope.Private: + // No work + break; + + default: + throw new ArgumentException(Environment.GetResourceString("Argument_BadResourceScopeVisibilityBits", calleeScope), "calleeScope"); + } + + if (consumeAsScope == calleeScope) { + Contract.Assert(requires == SxSRequirements.None, "Computed a strange set of required resource scoping. It's probably wrong."); + } + + return requires; + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkAttribute.cs b/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkAttribute.cs new file mode 100644 index 0000000000..dbf98c08d8 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkAttribute.cs @@ -0,0 +1,45 @@ +// 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. + +// +/*============================================================ +** +** +** +** Purpose: Identifies which SKU and version of the .NET +** Framework that a particular library was compiled against. +** Emitted by VS, and can help catch deployment problems. +** +===========================================================*/ +using System; +using System.Diagnostics.Contracts; + +namespace System.Runtime.Versioning { + + [AttributeUsageAttribute(AttributeTargets.Assembly, AllowMultiple = false, Inherited = false)] + public sealed class TargetFrameworkAttribute : Attribute { + private String _frameworkName; // A target framework moniker + private String _frameworkDisplayName; + + // The frameworkName parameter is intended to be the string form of a FrameworkName instance. + public TargetFrameworkAttribute(String frameworkName) + { + if (frameworkName == null) + throw new ArgumentNullException("frameworkName"); + Contract.EndContractBlock(); + _frameworkName = frameworkName; + } + + // The target framework moniker that this assembly was compiled against. + // Use the FrameworkName class to interpret target framework monikers. + public String FrameworkName { + get { return _frameworkName; } + } + + public String FrameworkDisplayName { + get { return _frameworkDisplayName; } + set { _frameworkDisplayName = value; } + } + } +} diff --git a/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkId.cs b/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkId.cs new file mode 100644 index 0000000000..0a5e668a92 --- /dev/null +++ b/src/mscorlib/src/System/Runtime/Versioning/TargetFrameworkId.cs @@ -0,0 +1,28 @@ +// 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. + +/*============================================================ +** +** Purpose: Describe the target framework of the application or AppDomain. +** +===========================================================*/ +using System; +using System.Runtime.CompilerServices; + +namespace System.Runtime.Versioning +{ + // What type of .NET Framework was this application compiled against? + [FriendAccessAllowed] + internal enum TargetFrameworkId + { + NotYetChecked = 0, + Unrecognized = 1, // Unknown type, such as a new SKU (like watches or cars) + Unspecified = 2, // The TargetFrameworkAttribute was created in v4.0. And apps compiled outside VS will not have this attribute. + NetFramework = 3, // Desktop - Client or Server or ServerCore. + Portable = 4, // Portable Library v1 Note: We do not expect people to build executables against portable libraries! + NetCore = 5, // .NET Core = Windows 8 Immersive and Portable Library v2+ + Silverlight = 6, // Silverlight but not the Phone + Phone = 7, // Windows Phone 7 or higher + } +} |