First batch: Basic experimental feature flag and attributes enforced

This commit is contained in:
Dongbo Wang
2018-07-06 13:28:18 -07:00
parent b1a525923a
commit eed9842534
19 changed files with 667 additions and 233 deletions
@@ -620,12 +620,63 @@ namespace System.Management.Automation
{
}
/// <summary>
/// Initializes a new instance that is associated with an experimental feature.
/// </summary>
public ParameterAttribute(string experimentName, ExperimentAction experimentAction)
{
if (string.IsNullOrEmpty(experimentName))
{
throw PSTraceSource.NewArgumentException(nameof(experimentName));
}
if (experimentAction == ExperimentAction.None)
{
throw PSTraceSource.NewArgumentException(nameof(experimentAction));
}
ExperimentName = experimentName;
ExperimentAction = experimentAction;
}
private string _parameterSetName = ParameterAttribute.AllParameterSets;
private string _helpMessage;
private string _helpMessageBaseName;
private string _helpMessageResourceId;
#region Experimental Feature Related Properties
/// <summary>
/// Name of the experimental feature this attribute is associated with.
/// </summary>
public string ExperimentName { get; }
/// <summary>
/// Action for engine to take when the experimental feature is enabled.
/// </summary>
public ExperimentAction ExperimentAction { get; }
internal bool ToHide => EffectiveAction == ExperimentAction.Hide;
internal bool ToShow => EffectiveAction == ExperimentAction.Show;
/// <summary>
/// Effective action to take at run time.
/// </summary>
private ExperimentAction EffectiveAction
{
get {
if (_effectiveAction == ExperimentAction.None)
{
_effectiveAction = ExperimentalFeature.GetActionToTake(ExperimentName, ExperimentAction);
}
return _effectiveAction;
}
}
private ExperimentAction _effectiveAction = default(ExperimentAction);
#endregion
/// <summary>
/// Gets and sets the parameter position. If not set, the parameter is named.
/// </summary>
@@ -1758,8 +1758,10 @@ namespace System.Management.Automation
List<CompletionResult> result = new List<CompletionResult>();
foreach (PropertyInfo pro in propertyInfos)
{
//Ignore TypeId (all attributes inherit it)
if (pro.Name != "TypeId" && (pro.Name.StartsWith(argName, StringComparison.OrdinalIgnoreCase)))
// Ignore getter-only properties, including 'TypeId' (all attributes inherit it).
if (!pro.CanWrite) { continue; }
if (pro.Name.StartsWith(argName, StringComparison.OrdinalIgnoreCase))
{
result.Add(new CompletionResult(pro.Name, pro.Name, CompletionResultType.Property,
pro.PropertyType.ToString() + " " + pro.Name));
@@ -1698,12 +1698,10 @@ namespace System.Management.Automation
foreach (ValidateArgumentsAttribute att in parameter.Parameter.ValidationAttributes)
{
if (att is ValidateSetAttribute)
if (att is ValidateSetAttribute setAtt)
{
RemoveLastNullCompletionResult(result);
var setAtt = (ValidateSetAttribute)att;
string wordToComplete = context.WordToComplete;
string quote = HandleDoubleAndSingleQuote(ref wordToComplete);
@@ -1712,6 +1710,8 @@ namespace System.Management.Automation
foreach (string value in setAtt.ValidValues)
{
if (value == string.Empty) { continue; }
if (wordToComplete.Equals(value, StringComparison.OrdinalIgnoreCase))
{
string completionText = quote == string.Empty ? value : quote + value + quote;
@@ -18,7 +18,7 @@ namespace System.Management.Automation
/// </summary>
internal class CommandProcessor : CommandProcessorBase
{
#region ctor
#region ctor
static CommandProcessor()
{
@@ -87,9 +87,10 @@ namespace System.Management.Automation
// CommandProcessor
#endregion ctor
#endregion ctor
#region internal members
#region internal members
/// <summary>
/// Returns a CmdletParameterBinderController for the specified command
/// </summary>
@@ -393,9 +394,9 @@ namespace System.Management.Automation
}
}
#endregion public_methods
#endregion public_methods
#region helper_methods
#region helper_methods
/// <summary>
/// Tells whether it is the first call to Read
@@ -849,7 +850,7 @@ namespace System.Management.Automation
return base.IsHelpRequested(out helpTarget, out helpCategory);
}
#endregion helper_methods
#endregion helper_methods
}
}
@@ -34,14 +34,31 @@ namespace System.Management.Automation
/// The metadata about the command to run.
/// </param>
///
internal CommandProcessorBase(
CommandInfo commandInfo)
internal CommandProcessorBase(CommandInfo commandInfo)
{
if (commandInfo == null)
{
throw PSTraceSource.NewArgumentNullException("commandInfo");
}
if (commandInfo is IScriptCommandInfo scriptCommand)
{
var expAttribute = scriptCommand.ScriptBlock.ExperimentalAttribute;
if (expAttribute != null && expAttribute.ToHide)
{
string errorTemplate = expAttribute.ExperimentAction == ExperimentAction.Hide
? DiscoveryExceptions.ScriptDisabledWhenFeatureOn
: DiscoveryExceptions.ScriptDisabledWhenFeatureOff;
string errorMsg = StringUtil.Format(errorTemplate, expAttribute.ExperimentName);
ErrorRecord errorRecord = new ErrorRecord(
new InvalidOperationException(errorMsg),
"ScriptCommandDisabled",
ErrorCategory.InvalidOperation,
commandInfo);
throw new CmdletInvocationException(errorRecord);
}
}
CommandInfo = commandInfo;
}
@@ -64,6 +64,18 @@ namespace System.Management.Automation
// First, process attributes that aren't type conversions
foreach (Attribute attribute in runtimeDefinedParameter.Attributes)
{
if (processingDynamicParameters)
{
// When processing dynamic parameters, the attribute list may contain experimental attributes
// and disabled parameter attributes. We should ignore those attributes.
// When processing non-dynamic parameters, the experimental attributes and disabled parameter
// attributes have already been filtered out when constructing the RuntimeDefinedParameter.
if (attribute is ExperimentalAttribute || attribute is ParameterAttribute param && param.ToHide)
{
continue;
}
}
if (!(attribute is ArgumentTypeConverterAttribute))
{
ProcessAttribute(runtimeDefinedParameter.Name, attribute, ref validationAttributes, ref argTransformationAttributes, ref aliases);
@@ -78,7 +90,7 @@ namespace System.Management.Automation
}
// Now process type converters
foreach (ArgumentTypeConverterAttribute attribute in runtimeDefinedParameter.Attributes.OfType<ArgumentTypeConverterAttribute>())
foreach (var attribute in runtimeDefinedParameter.Attributes.OfType<ArgumentTypeConverterAttribute>())
{
ProcessAttribute(runtimeDefinedParameter.Name, attribute, ref validationAttributes, ref argTransformationAttributes, ref aliases);
}
@@ -169,7 +181,15 @@ namespace System.Management.Automation
foreach (Attribute attr in memberAttributes)
{
ProcessAttribute(member.Name, attr, ref validationAttributes, ref argTransformationAttributes, ref aliases);
switch (attr)
{
case ExperimentalAttribute _:
case ParameterAttribute param when param.ToHide:
break;
default:
ProcessAttribute(member.Name, attr, ref validationAttributes, ref argTransformationAttributes, ref aliases);
break;
}
}
this.ValidationAttributes = validationAttributes == null
@@ -436,7 +456,6 @@ namespace System.Management.Automation
ref Collection<ArgumentTransformationAttribute> argTransformationAttributes,
ref string[] aliases)
{
// NTRAID#Windows Out Of Band Releases-926374-2005/12/22-JonN
if (attribute == null)
return;
@@ -0,0 +1,309 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Immutable;
using System.Linq;
using System.Management.Automation.Configuration;
using System.Management.Automation.Internal;
using System.Runtime.CompilerServices;
namespace System.Management.Automation
{
/// <summary>
/// Support experimental features in PowerShell
/// </summary>
public class ExperimentalFeature
{
#region Const Members
internal const string EngineSource = "PSEngine";
#endregion
#region Instance Members
/// <summary>
/// Name of an experimental feature.
/// </summary>
public string Name { get; }
/// <summary>
/// Description of an experimental feature.
/// </summary>
public string Description { get; }
/// <summary>
/// Source of an experimental feature.
/// </summary>
public string Source { get; }
/// <summary>
/// Indicate whether the feature is enabled.
/// </summary>
public bool IsEnabled { get; private set; }
/// <summary>
/// Constructor for ExperimentalFeature.
/// </summary>
internal ExperimentalFeature(string name, string description, string source, bool isEnabled)
{
Name = name;
Description = description;
Source = source;
IsEnabled = isEnabled;
}
#endregion
#region Static Members
/// <summary>
/// All available engine experimental features.
/// </summary>
internal static readonly ReadOnlyCollection<ExperimentalFeature> EngineExperimentalFeatures;
/// <summary>
/// A dictionary of all available engine experimental features. Feature name is the key.
/// </summary>
internal static readonly ReadOnlyDictionary<string, ExperimentalFeature> EngineExperimentalFeatureMap;
/// <summary>
/// Experimental feature names that are enabled in the config file.
/// </summary>
internal static readonly ImmutableHashSet<string> EnabledExperimentalFeatureNames;
/// <summary>
/// Type initializer. Initialize the engine experimental feature list.
/// </summary>
static ExperimentalFeature()
{
// Initialize the readonly collection 'EngineExperimentalFeatures'.
var engineFeatures = new ExperimentalFeature[] {
/* Register engine experimental features here. Follow the same pattern as the example:
new ExperimentalFeature(name: "PSFileSystemProviderV2",
description: "Replace the old FileSystemProvider with cleaner design and faster code",
source: EngineSource,
isEnabled: false))
*/
};
EngineExperimentalFeatures = new ReadOnlyCollection<ExperimentalFeature>(engineFeatures);
// Initialize the readonly dictionary 'EngineExperimentalFeatureMap'.
var engineExpFeatureMap = engineFeatures.ToDictionary(f => f.Name, StringComparer.OrdinalIgnoreCase);
EngineExperimentalFeatureMap = new ReadOnlyDictionary<string, ExperimentalFeature>(engineExpFeatureMap);
// Initialize the immutable hashset 'EnabledExperimentalFeatureNames'.
// The initialization of 'EnabledExperimentalFeatureNames' is deliberately made in the type initializer so that:
// 1. 'EnabledExperimentalFeatureNames' can be declared as readonly;
// 2. No need to deal with initialization from multiple threads;
// 3. We don't need to decide where/when to read the config file for the enabled experimental features,
// instead, it will be done when the type is used for the first time, which is always earlier than
// any experimental features take effect.
string[] enabledFeatures = Utils.EmptyArray<string>();
try { enabledFeatures = PowerShellConfig.Instance.GetExperimentalFeatures(); }
catch (Exception ex) when (LogException(ex, "Placeholder")) { }
EnabledExperimentalFeatureNames = ProcessEnabledFeatures(enabledFeatures);
}
/// <summary>
/// Check if the specified experimental feature has been enabled.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool HasEnabled(string featureName)
{
return EnabledExperimentalFeatureNames.Contains(featureName);
}
/// <summary>
/// Determine the action to take for the specified experiment name and action.
/// </summary>
internal static ExperimentAction GetActionToTake(string experimentName, ExperimentAction experimentAction)
{
if (experimentName == null || experimentAction == ExperimentAction.None)
{
// If either the experiment name or action is not defined, then return 'Show' by default.
// This could happen to 'ParameterAttribute' when no experimental related field is declared.
return ExperimentAction.Show;
}
ExperimentAction action = experimentAction;
if (!HasEnabled(experimentName))
{
action = (action == ExperimentAction.Hide) ? ExperimentAction.Show : ExperimentAction.Hide;
}
return action;
}
/// <summary>
/// Check if the name follows the engine experimental feature name convention.
/// Convention: prefix 'PS' to the feature name -- PSFeatureName
/// </summary>
internal static bool IsEngineFeatureName(string featureName)
{
return featureName.IndexOf('.') == -1 && featureName.Length > 2 && featureName.StartsWith("PS", StringComparison.Ordinal);
}
/// <summary>
/// Check if the name follows the module experimental feature name convention.
/// Convention: ModuleName.FeatureName
/// </summary>
/// <param name="featureName">The feature name to check.</param>
/// <param name="moduleName">When specified, we check if the feature name matches the module name</param>
internal static bool IsModuleFeatureName(string featureName, string moduleName = null)
{
int firstDotIndex = featureName.IndexOf('.');
int lastDotIndex = featureName.LastIndexOf('.');
bool legit = firstDotIndex > 0 && lastDotIndex < featureName.Length - 1;
if (legit && moduleName != null)
{
var moduleNamePart = featureName.AsSpan(0, lastDotIndex);
return moduleNamePart.Equals(moduleName.AsSpan(), StringComparison.OrdinalIgnoreCase);
}
return legit;
}
/// <summary>
/// Log the exception without rewinding the stack.
/// </summary>
private static bool LogException(Exception ex, string errorTemplate)
{
// TODO: Logging
return false;
}
/// <summary>
/// Log an error message.
/// </summary>
private static void LogError(string message)
{
// TODO: Logging
}
/// <summary>
/// Process the array of enabled feature names retrieved from configuration.
/// Ignore invalid feature names and unavailable engine feature names, and
/// return an ImmutableHashSet of the valid enabled feature names.
/// </summary>
private static ImmutableHashSet<string> ProcessEnabledFeatures(string[] enabledFeatures)
{
if (enabledFeatures.Length == 0) { return ImmutableHashSet.Create<string>(); }
var list = new List<string>(enabledFeatures.Length);
foreach (string name in enabledFeatures)
{
if (IsModuleFeatureName(name))
{
list.Add(name);
}
else if (IsEngineFeatureName(name))
{
if (EngineExperimentalFeatureMap.TryGetValue(name, out ExperimentalFeature feature))
{
feature.IsEnabled = true;
list.Add(name);
}
else
{
LogError("No such engine feature registered. Ignore it.");
}
}
else
{
LogError("Invalid experimental feature name. Ignore it");
}
}
return ImmutableHashSet.CreateRange(StringComparer.OrdinalIgnoreCase, list);
}
#endregion
}
/// <summary>
/// Indicates the action to take on the cmdlet/parameter that has the attribute declared.
/// </summary>
public enum ExperimentAction
{
/// <summary>
/// Represent an undefined action, used as the default value.
/// </summary>
None = 0,
/// <summary>
/// Hide the cmdlet/parameter when the corresponding experimental feature is enabled.
/// </summary>
Hide = 1,
/// <summary>
/// Show the cmdlet/parameter when the corresponding experimental feature is enabled.
/// </summary>
Show = 2
}
/// <summary>
/// The attribute that applies to cmdlet/function/parameter to define what the engine should do with it.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property)]
public sealed class ExperimentalAttribute : ParsingBaseAttribute
{
/// <summary>
/// Specify the experimental feature this attribute is associated with.
/// </summary>
public string ExperimentName { get; }
/// <summary>
/// Specify the action for engine to take when the experimental feature is enabled.
/// </summary>
public ExperimentAction ExperimentAction { get; }
/// <summary>
/// Initializes a new instance of the ExperimentalAttribute class.
/// </summary>
public ExperimentalAttribute(string experimentName, ExperimentAction experimentAction)
{
if (string.IsNullOrEmpty(experimentName))
{
throw PSTraceSource.NewArgumentException(nameof(experimentName));
}
if (experimentAction == ExperimentAction.None)
{
throw PSTraceSource.NewArgumentException(nameof(experimentAction));
}
ExperimentName = experimentName;
ExperimentAction = experimentAction;
}
/// <summary>
/// Initialize an instance that represents the none-value.
/// </summary>
private ExperimentalAttribute() {}
/// <summary>
/// An instance that represents the none-value.
/// </summary>
internal readonly static ExperimentalAttribute None = new ExperimentalAttribute();
internal bool ToHide => EffectiveAction == ExperimentAction.Hide;
internal bool ToShow => EffectiveAction == ExperimentAction.Show;
/// <summary>
/// Effective action to take at run time.
/// </summary>
private ExperimentAction EffectiveAction
{
get {
if (_effectiveAction == ExperimentAction.None)
{
_effectiveAction = ExperimentalFeature.GetActionToTake(ExperimentName, ExperimentAction);
}
return _effectiveAction;
}
}
private ExperimentAction _effectiveAction = default(ExperimentAction);
}
}
@@ -13,12 +13,13 @@ using System.Management.Automation.Internal;
using System.Management.Automation.Provider;
using System.Management.Automation.Language;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.PowerShell.Commands;
using Debug = System.Management.Automation.Diagnostics;
using System.Management.Automation.Host;
using System.Text;
using System.Threading.Tasks;
using Debug = System.Management.Automation.Diagnostics;
namespace System.Management.Automation.Runspaces
{
@@ -3752,7 +3753,7 @@ namespace System.Management.Automation.Runspaces
{
s_PSSnapInTracer.WriteLine("Loading assembly for psSnapIn {0}", psSnapInInfo.Name);
assembly = PSSnapInHelpers.LoadPSSnapInAssembly(psSnapInInfo, out cmdlets, out providers);
assembly = PSSnapInHelpers.LoadPSSnapInAssembly(psSnapInInfo);
if (assembly == null)
{
@@ -4897,13 +4898,9 @@ end
internal static class PSSnapInHelpers
{
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")]
internal static Assembly LoadPSSnapInAssembly(PSSnapInInfo psSnapInInfo,
out Dictionary<string, SessionStateCmdletEntry> cmdlets, out Dictionary<string, SessionStateProviderEntry> providers)
internal static Assembly LoadPSSnapInAssembly(PSSnapInInfo psSnapInInfo)
{
Assembly assembly = null;
cmdlets = null;
providers = null;
s_PSSnapInTracer.WriteLine("Loading assembly from GAC. Assembly Name: {0}", psSnapInInfo.AssemblyName);
try
@@ -4960,13 +4957,11 @@ end
return assembly;
}
private static T GetCustomAttribute<T>(Type decoratedType) where T : Attribute
private static bool GetCustomAttribute<T>(Type decoratedType, out T attribute) where T : Attribute
{
var attributes = decoratedType.GetCustomAttributes<T>(false);
var customAttrs = attributes.ToArray();
Debug.Assert(customAttrs.Length <= 1, "CmdletAttribute and/or CmdletProviderAttribute cannot normally appear more than once");
return customAttrs.Length == 0 ? null : customAttrs[0];
var attributes = decoratedType.GetCustomAttributes<T>(inherit: false);
attribute = attributes.FirstOrDefault();
return attribute != null;
}
internal static void AnalyzePSSnapInAssembly(Assembly assembly, string name, PSSnapInInfo psSnapInInfo, PSModuleInfo moduleInfo, bool isModuleLoad,
@@ -4976,7 +4971,7 @@ end
helpFile = null;
if (assembly == null)
{
throw new ArgumentNullException("assembly");
throw new ArgumentNullException(nameof(assembly));
}
cmdlets = null;
@@ -4988,8 +4983,8 @@ end
Dictionary<string, Tuple<SessionStateCmdletEntry, List<SessionStateAliasEntry>>> cachedCmdlets;
if (s_cmdletCache.Value.TryGetValue(assembly, out cachedCmdlets))
{
cmdlets = new Dictionary<string, SessionStateCmdletEntry>(s_cmdletCache.Value.Count, StringComparer.OrdinalIgnoreCase);
aliases = new Dictionary<string, List<SessionStateAliasEntry>>(StringComparer.OrdinalIgnoreCase);
cmdlets = new Dictionary<string, SessionStateCmdletEntry>(cachedCmdlets.Count, StringComparer.OrdinalIgnoreCase);
aliases = new Dictionary<string, List<SessionStateAliasEntry>>(cachedCmdlets.Count, StringComparer.OrdinalIgnoreCase);
foreach (var pair in cachedCmdlets)
{
@@ -5051,30 +5046,15 @@ end
}
string assemblyPath = assembly.Location;
Type[] assemblyTypes;
if (cmdlets != null || providers != null)
{
if (!s_assembliesWithModuleInitializerCache.Value.ContainsKey(assembly))
{
s_PSSnapInTracer.WriteLine("Returning cached cmdlet and provider entries for {0}", assemblyPath);
return;
}
else
{
s_PSSnapInTracer.WriteLine("Executing IModuleAssemblyInitializer.Import for {0}", assemblyPath);
assemblyTypes = GetAssemblyTypes(assembly, name);
ExecuteModuleInitializer(assembly, assemblyTypes, isModuleLoad);
return;
}
s_PSSnapInTracer.WriteLine("Returning cached cmdlet and provider entries for {0}", assemblyPath);
return;
}
s_PSSnapInTracer.WriteLine("Analyzing assembly {0} for cmdlet and providers", assemblyPath);
helpFile = GetHelpFile(assemblyPath);
Type randomCmdletToCheckLinkDemand = null;
Type randomProviderToCheckLinkDemand = null;
if (psSnapInInfo != null && psSnapInInfo.Name.Equals(InitialSessionState.CoreSnapin, StringComparison.OrdinalIgnoreCase))
{
InitializeCoreCmdletsAndProviders(psSnapInInfo, out cmdlets, out providers, helpFile);
@@ -5086,13 +5066,10 @@ end
Dictionary<string, SessionStateCmdletEntry> cmdletsCheck = null;
Dictionary<string, SessionStateProviderEntry> providersCheck = null;
Dictionary<string, List<SessionStateAliasEntry>> aliasesCheck = null;
Type unused1 = null;
Type unused2 = null;
AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, isModuleLoad,
ref cmdletsCheck, ref aliasesCheck, ref providersCheck, helpFile, ref unused1, ref unused2);
AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, isModuleLoad, ref cmdletsCheck, ref aliasesCheck, ref providersCheck, helpFile);
Diagnostics.Assert(aliasesCheck == null, "InitializeCoreCmdletsAndProviders assumes no aliases are defined in System.Management.Automation.dll");
Diagnostics.Assert(providersCheck.Keys.Count == providers.Keys.Count, "new Provider added to System.Management.Automation.dll - update InitializeCoreCmdletsAndProviders");
Diagnostics.Assert(providersCheck.Count == providers.Count, "new Provider added to System.Management.Automation.dll - update InitializeCoreCmdletsAndProviders");
foreach (var pair in providersCheck)
{
SessionStateProviderEntry other;
@@ -5110,7 +5087,7 @@ end
Diagnostics.Assert(false, "Missing provider: " + pair.Key);
}
}
Diagnostics.Assert(cmdletsCheck.Keys.Count == cmdlets.Keys.Count, "new Cmdlet added to System.Management.Automation.dll - update InitializeCoreCmdletsAndProviders");
Diagnostics.Assert(cmdletsCheck.Count == cmdlets.Count, "new Cmdlet added to System.Management.Automation.dll - update InitializeCoreCmdletsAndProviders");
foreach (var pair in cmdletsCheck)
{
@@ -5133,39 +5110,7 @@ end
}
else
{
AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, isModuleLoad,
ref cmdlets, ref aliases, ref providers, helpFile, ref randomCmdletToCheckLinkDemand, ref randomProviderToCheckLinkDemand);
}
// force a LinkDemand check to get an explicit exception if
// Cmdlet[Provider]Attributes are silently swallowed by Type.GetCustomAttributes
// bug Win7:705573
if ((providers == null || providers.Count == 0) && (cmdlets == null || cmdlets.Count == 0))
{
try
{
if (randomCmdletToCheckLinkDemand != null)
{
ConstructorInfo constructor = randomCmdletToCheckLinkDemand.GetConstructor(PSTypeExtensions.EmptyTypes);
if (constructor != null)
{
constructor.Invoke(null); // this is how we artificially force a LinkDemand check
}
}
if (randomProviderToCheckLinkDemand != null)
{
ConstructorInfo constructor = randomProviderToCheckLinkDemand.GetConstructor(PSTypeExtensions.EmptyTypes);
if (constructor != null)
{
constructor.Invoke(null); // this is how we artificially force a LinkDemand check
}
}
}
catch (TargetInvocationException e)
{
throw e.InnerException;
}
AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, isModuleLoad, ref cmdlets, ref aliases, ref providers, helpFile);
}
// Cache the cmdlet and provider info for this assembly...
@@ -5212,118 +5157,83 @@ end
ref Dictionary<string, SessionStateCmdletEntry> cmdlets,
ref Dictionary<string, List<SessionStateAliasEntry>> aliases,
ref Dictionary<string, SessionStateProviderEntry> providers,
string helpFile,
ref Type randomCmdletToCheckLinkDemand,
ref Type randomProviderToCheckLinkDemand)
string helpFile)
{
var assemblyTypes = GetAssemblyTypes(assembly, name);
ExecuteModuleInitializer(assembly, assemblyTypes, isModuleLoad);
foreach (Type type in assemblyTypes)
{
if (!(type.IsPublic || type.IsNestedPublic) || type.IsAbstract)
continue;
if (!HasDefaultConstructor(type)) { continue; }
// Check for cmdlets
if (IsCmdletClass(type) && HasDefaultConstructor(type))
if (IsCmdletClass(type) && GetCustomAttribute(type, out CmdletAttribute cmdletAttribute))
{
randomCmdletToCheckLinkDemand = type;
CmdletAttribute cmdletAttribute = GetCustomAttribute<CmdletAttribute>(type);
if (cmdletAttribute == null)
{
continue;
}
string cmdletName = GetCmdletName(cmdletAttribute);
if (string.IsNullOrEmpty(cmdletName))
if (GetCustomAttribute(type, out ExperimentalAttribute expAttribute) && expAttribute.ToHide)
{
// If 'ExperimentalAttribute' is specified on the cmdlet type and the
// effective action at run time is 'Hide', then we ignore the type.
continue;
}
string cmdletName = cmdletAttribute.VerbName + "-" + cmdletAttribute.NounName;
if (cmdlets != null && cmdlets.ContainsKey(cmdletName))
{
string message = StringUtil.Format(ConsoleInfoErrorStrings.PSSnapInDuplicateCmdlets, cmdletName, name);
s_PSSnapInTracer.TraceError(message);
throw new PSSnapInException(name, message);
}
SessionStateCmdletEntry cmdlet = new SessionStateCmdletEntry(cmdletName, type, helpFile);
cmdlet.SetPSSnapIn(psSnapInInfo);
if (cmdlets == null)
{
cmdlets = new Dictionary<string, SessionStateCmdletEntry>(StringComparer.OrdinalIgnoreCase);
}
if (psSnapInInfo != null) { cmdlet.SetPSSnapIn(psSnapInInfo); }
if (moduleInfo != null) { cmdlet.SetModule(moduleInfo); }
cmdlets = cmdlets ?? new Dictionary<string, SessionStateCmdletEntry>(StringComparer.OrdinalIgnoreCase);
cmdlets.Add(cmdletName, cmdlet);
var aliasAttribute = GetCustomAttribute<AliasAttribute>(type);
if (aliasAttribute != null)
if (GetCustomAttribute(type, out AliasAttribute aliasAttribute))
{
if (aliases == null)
{
aliases = new Dictionary<string, List<SessionStateAliasEntry>>(StringComparer.OrdinalIgnoreCase);
}
aliases = aliases ?? new Dictionary<string, List<SessionStateAliasEntry>>(StringComparer.OrdinalIgnoreCase);
var aliasList = new List<SessionStateAliasEntry>();
foreach (var alias in aliasAttribute.AliasNames)
{
// Alias declared by AliasAttribute is set with the option 'ScopedItemOptions.None',
// because we believe a user of the cmdlet, instead of the author of it,
// should be the one to decide the option
// ('ScopedItemOptions.ReadOnly' and/or 'ScopedItemOptions.AllScopes') of the alias usage."
var aliasEntry = new SessionStateAliasEntry(alias, cmdletName, string.Empty, ScopedItemOptions.None);
if (psSnapInInfo != null)
{
aliasEntry.SetPSSnapIn(psSnapInInfo);
}
// Alias declared by 'AliasAttribute' is set with the option 'ScopedItemOptions.None', because we believe
// the users of the cmdlet, instead of the author, should have control of what options applied to an alias
// ('ScopedItemOptions.ReadOnly' and/or 'ScopedItemOptions.AllScopes').
var aliasEntry = new SessionStateAliasEntry(alias, cmdletName, description: string.Empty, ScopedItemOptions.None);
if (psSnapInInfo != null) { aliasEntry.SetPSSnapIn(psSnapInInfo); }
if (moduleInfo != null) { aliasEntry.SetModule(moduleInfo); }
aliasList.Add(aliasEntry);
}
aliases.Add(cmdletName, aliasList);
}
s_PSSnapInTracer.WriteLine("{0} from type {1} is added as a cmdlet. ", cmdletName, type.FullName);
continue;
}
// Check for providers
if (IsProviderClass(type) && HasDefaultConstructor(type))
else if (IsProviderClass(type) && GetCustomAttribute(type, out CmdletProviderAttribute providerAttribute))
{
randomProviderToCheckLinkDemand = type;
CmdletProviderAttribute providerAttribute = GetCustomAttribute<CmdletProviderAttribute>(type);
if (providerAttribute == null)
{
continue;
}
string providerName = GetProviderName(providerAttribute);
if (string.IsNullOrEmpty(providerName))
if (GetCustomAttribute(type, out ExperimentalAttribute expAttribute) && expAttribute.ToHide)
{
// If 'ExperimentalAttribute' is specified on the provider type and
// the effective action at run time is 'Hide', then we ignore the type.
continue;
}
string providerName = providerAttribute.ProviderName;
if (providers != null && providers.ContainsKey(providerName))
{
string message = StringUtil.Format(ConsoleInfoErrorStrings.PSSnapInDuplicateProviders, providerName, psSnapInInfo.Name);
s_PSSnapInTracer.TraceError(message);
throw new PSSnapInException(psSnapInInfo.Name, message);
}
SessionStateProviderEntry provider = new SessionStateProviderEntry(providerName, type, helpFile);
provider.SetPSSnapIn(psSnapInInfo);
if (psSnapInInfo != null) { provider.SetPSSnapIn(psSnapInInfo); }
if (moduleInfo != null) { provider.SetModule(moduleInfo); }
// After converting core snapins to load as modules, the providers will have Module property populated
if (moduleInfo != null)
{
provider.SetModule(moduleInfo);
}
if (providers == null)
{
providers = new Dictionary<string, SessionStateProviderEntry>(StringComparer.OrdinalIgnoreCase);
}
providers = providers ?? new Dictionary<string, SessionStateProviderEntry>(StringComparer.OrdinalIgnoreCase);
providers.Add(providerName, provider);
s_PSSnapInTracer.WriteLine("{0} from type {1} is added as a provider. ", providerName, type.FullName);
@@ -5431,39 +5341,28 @@ end
}
}
private static void ExecuteModuleInitializer(Assembly assembly, Type[] assemblyTypes, bool isModuleLoad)
private static void ExecuteModuleInitializer(Assembly assembly, IEnumerable<Type> assemblyTypes, bool isModuleLoad)
{
for (int i = 0; i < assemblyTypes.Length; i++)
foreach (Type type in assemblyTypes)
{
Type type = assemblyTypes[i];
if (!(type.IsPublic || type.IsNestedPublic) || type.IsAbstract) { continue; }
if (isModuleLoad && typeof(IModuleAssemblyInitializer).IsAssignableFrom(type) && type != typeof(IModuleAssemblyInitializer))
if (isModuleLoad && typeof(IModuleAssemblyInitializer).IsAssignableFrom(type))
{
s_assembliesWithModuleInitializerCache.Value[assembly] = true;
IModuleAssemblyInitializer moduleInitializer = (IModuleAssemblyInitializer)Activator.CreateInstance(type, true);
moduleInitializer.OnImport();
}
}
}
internal static Type[] GetAssemblyTypes(Assembly assembly, string name)
internal static IEnumerable<Type> GetAssemblyTypes(Assembly assembly, string name)
{
Type[] assemblyTypes = null;
try
{
var exportedTypes = assembly.ExportedTypes;
assemblyTypes = exportedTypes as Type[] ?? exportedTypes.ToArray();
// Return types that are public, non-abstract, non-interface and non-valueType.
return assembly.ExportedTypes.Where(t => !t.IsAbstract && !t.IsInterface && !t.IsValueType);
}
catch (ReflectionTypeLoadException e)
{
string message;
message = e.Message;
message += "\nLoader Exceptions: \n";
string message = e.Message + "\nLoader Exceptions: \n";
if (e.LoaderExceptions != null)
{
foreach (Exception exception in e.LoaderExceptions)
@@ -5473,10 +5372,8 @@ end
}
s_PSSnapInTracer.TraceError(message);
throw new PSSnapInException(name, message);
}
return assemblyTypes;
}
// cmdletCache holds the list of cmdlets along with its aliases per each assembly.
@@ -5484,54 +5381,26 @@ end
new Lazy<ConcurrentDictionary<Assembly, Dictionary<string, Tuple<SessionStateCmdletEntry, List<SessionStateAliasEntry>>>>>();
private static Lazy<ConcurrentDictionary<Assembly, Dictionary<string, SessionStateProviderEntry>>> s_providerCache =
new Lazy<ConcurrentDictionary<Assembly, Dictionary<string, SessionStateProviderEntry>>>();
// Using a ConcurrentDictionary for this so that we can avoid having a private lock variable. We use only the keys for checking.
private static Lazy<ConcurrentDictionary<Assembly, bool>> s_assembliesWithModuleInitializerCache = new Lazy<ConcurrentDictionary<Assembly, bool>>();
private static string GetCmdletName(CmdletAttribute cmdletAttribute)
{
string verb = cmdletAttribute.VerbName;
string noun = cmdletAttribute.NounName;
return verb + "-" + noun;
}
private static string GetProviderName(CmdletProviderAttribute providerAttribute)
{
return providerAttribute.ProviderName;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsCmdletClass(Type type)
{
if (type == null)
return false;
return type.IsSubclassOf(typeof(System.Management.Automation.Cmdlet));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsProviderClass(Type type)
{
if (type == null)
return false;
return type.IsSubclassOf(typeof(System.Management.Automation.Provider.CmdletProvider));
}
internal static bool IsModuleAssemblyInitializerClass(Type type)
{
if (type == null)
{
return false;
}
return type.IsSubclassOf(typeof(System.Management.Automation.IModuleAssemblyInitializer));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool HasDefaultConstructor(Type type)
{
return !(type.GetConstructor(PSTypeExtensions.EmptyTypes) == null);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string GetHelpFile(string assemblyPath)
{
// Help files exist only for original module assemblies, not for generated Ngen binaries
@@ -4672,7 +4672,7 @@ namespace Microsoft.PowerShell.Commands
var exportedTypes = PSSnapInHelpers.GetAssemblyTypes(module.ImplementingAssembly, module.Name);
foreach (var type in exportedTypes)
{
if (typeof(IModuleAssemblyCleanup).IsAssignableFrom(type) && type != typeof(IModuleAssemblyCleanup))
if (typeof(IModuleAssemblyCleanup).IsAssignableFrom(type))
{
var moduleCleanup = (IModuleAssemblyCleanup)Activator.CreateInstance(type, true);
moduleCleanup.OnRemove(module);
@@ -193,6 +193,14 @@ namespace System.Management.Automation.Configuration
WriteValueToFile<bool>(ConfigScope.SystemWide, "DisablePromptToUpdateHelp", prompt);
}
/// <summary>
/// Get the names of experimental features enabled in the config file.
/// </summary>
internal string[] GetExperimentalFeatures()
{
return ReadValueFromFile<string[]>(ConfigScope.SystemWide, "ExperimentalFeatures", Utils.EmptyArray<string>());
}
/// <summary>
/// Corresponding settings of the original Group Policies
/// </summary>
@@ -164,7 +164,36 @@ namespace System.Management.Automation
/// This can be any attribute that can be applied to a normal parameter.
/// </remarks>
public Collection<Attribute> Attributes { get; } = new Collection<Attribute>();
} // class RuntimeDefinedParameter
/// <summary>
/// Check if the parameter is disabled due to the associated experimental feature.
/// </summary>
internal bool IsDisabled()
{
bool hasParameterAttribute = false;
bool hasEnabledParamAttribute = false;
bool hasSeenExpAttribute = false;
foreach (var attr in Attributes)
{
if (!hasSeenExpAttribute && attr is ExperimentalAttribute expAttribute)
{
if (expAttribute.ToHide) { return true; }
hasSeenExpAttribute = true;
}
else if (attr is ParameterAttribute paramAttribute)
{
hasParameterAttribute = true;
if (paramAttribute.ToHide) { continue; }
hasEnabledParamAttribute = true;
}
}
// If one or more parameter attributes are declared but none is enabled,
// then we consider the parameter is disabled.
return hasParameterAttribute && !hasEnabledParamAttribute;
}
}
/// <summary>
/// Represents a collection of runtime-defined parameters that are keyed based on the name
@@ -17,10 +17,9 @@ namespace System.Management.Automation
internal abstract class ScriptCommandProcessorBase : CommandProcessorBase
{
protected ScriptCommandProcessorBase(ScriptBlock scriptBlock, ExecutionContext context, bool useLocalScope, CommandOrigin origin, SessionStateInternal sessionState)
: base(new ScriptInfo(String.Empty, scriptBlock, context))
{
this._dontUseScopeCommandOrigin = false;
this.CommandInfo = new ScriptInfo(String.Empty, scriptBlock, context);
this._fromScriptFile = false;
CommonInitialization(scriptBlock, context, useLocalScope, origin, sessionState);
@@ -1308,10 +1308,12 @@ namespace System.Management.Automation
foreach (RuntimeDefinedParameter parameterDefinition in runtimeDefinedParameters.Values)
{
// Create the compiled parameter and add it to the bindable parameters collection
// NTRAID#Windows Out Of Band Releases-926374-2005/12/22-JonN
if (parameterDefinition == null)
continue;
if (processingDynamicParameters)
{
// When processing dynamic parameters, parameter definitions come from the user,
// Invalid data could be passed in, or the parameter could be actually disabled.
if (parameterDefinition == null || parameterDefinition.IsDisabled()) { continue; }
}
CompiledCommandParameter parameter = new CompiledCommandParameter(parameterDefinition, processingDynamicParameters);
AddParameter(parameter, checkNames);
@@ -1379,7 +1381,6 @@ namespace System.Management.Automation
}
}
// NTRAID#Windows Out Of Band Releases-906345-2005/06/30-JeffJon
// This call verifies that the parameter is unique or
// can be deemed unique. If not, an exception is thrown.
// If it is unique (or deemed unique), then it is added
@@ -1458,7 +1459,6 @@ namespace System.Management.Automation
foreach (string alias in parameter.Aliases)
{
// NTRAID#Windows Out Of Band Releases-917356-JonN
if (AliasedParameters.ContainsKey(alias))
{
throw new MetadataException(
@@ -1501,16 +1501,20 @@ namespace System.Management.Automation
///
private static bool IsMemberAParameter(MemberInfo member)
{
bool result = false;
try
{
// MemberInfo.GetCustomAttributes returns IEnumerable<Attribute> in CoreCLR
var attributes = member.GetCustomAttributes(typeof(ParameterAttribute), false);
if (attributes.Any())
var expAttribute = member.GetCustomAttributes<ExperimentalAttribute>(false).FirstOrDefault();
if (expAttribute != null && expAttribute.ToHide) { return false; }
var hasAnyVisibleParamAttributes = false;
var paramAttributes = member.GetCustomAttributes<ParameterAttribute>(false);
foreach (var paramAttribute in paramAttributes)
{
result = true;
if (paramAttribute.ToHide) { continue; }
hasAnyVisibleParamAttributes = true;
break;
}
return hasAnyVisibleParamAttributes;
}
catch (MetadataException metadataException)
{
@@ -1530,8 +1534,6 @@ namespace System.Management.Automation
member.Name,
argumentException.Message);
}
return result;
} // IsMemberAParameter
#endregion helper methods
@@ -1001,15 +1001,17 @@ namespace System.Management.Automation.Language
internal static RuntimeDefinedParameterDictionary GetParameterMetaData(ReadOnlyCollection<ParameterAst> parameters, bool automaticPositions, ref bool usesCmdletBinding)
{
var md = new RuntimeDefinedParameterDictionary();
var listMd = new List<RuntimeDefinedParameter>();
var listMd = new List<RuntimeDefinedParameter>(parameters.Count);
var customParameterSet = false;
for (int index = 0; index < parameters.Count; index++)
{
var param = parameters[index];
var rdp = GetRuntimeDefinedParameter(param, ref customParameterSet, ref usesCmdletBinding);
listMd.Add(rdp);
md.Add(param.Name.VariablePath.UserPath, rdp);
if (rdp != null)
{
listMd.Add(rdp);
md.Add(param.Name.VariablePath.UserPath, rdp);
}
}
int pos = 0;
@@ -1447,28 +1449,47 @@ namespace System.Management.Automation.Language
private static RuntimeDefinedParameter GetRuntimeDefinedParameter(ParameterAst parameterAst, ref bool customParameterSet, ref bool usesCmdletBinding)
{
List<Attribute> attributes = new List<Attribute>();
List<Attribute> attributes = new List<Attribute>(parameterAst.Attributes.Count);
bool hasParameterAttribute = false;
bool hasEnabledParamAttribute = false;
bool hasSeenExpAttribute = false;
for (int index = 0; index < parameterAst.Attributes.Count; index++)
{
var attributeAst = parameterAst.Attributes[index];
var attribute = attributeAst.GetAttribute();
attributes.Add(attribute);
var parameterAttribute = attribute as ParameterAttribute;
if (parameterAttribute != null)
if (attribute is ExperimentalAttribute expAttribute)
{
// Only honor the first seen experimental attribute, ignore the others.
if (!hasSeenExpAttribute && expAttribute.ToHide) { return null; }
// Do not add experimental attributes to the attribute list.
hasSeenExpAttribute = true;
continue;
}
else if (attribute is ParameterAttribute paramAttribute)
{
hasParameterAttribute = true;
if (paramAttribute.ToHide) { continue; }
hasEnabledParamAttribute = true;
usesCmdletBinding = true;
if (parameterAttribute.Position != int.MinValue ||
!parameterAttribute.ParameterSetName.Equals(ParameterAttribute.AllParameterSets,
if (paramAttribute.Position != int.MinValue ||
!paramAttribute.ParameterSetName.Equals(ParameterAttribute.AllParameterSets,
StringComparison.OrdinalIgnoreCase))
{
customParameterSet = true;
}
}
attributes.Add(attribute);
}
// If all 'ParameterAttribute' declared for the parameter are hidden due to
// an experimental feature, then the parameter should be ignored.
if (hasParameterAttribute && !hasEnabledParamAttribute) { return null; }
attributes.Reverse();
if (!hasParameterAttribute)
{
@@ -1476,7 +1497,7 @@ namespace System.Management.Automation.Language
}
var result = new RuntimeDefinedParameter(parameterAst.Name.VariablePath.UserPath, parameterAst.StaticType,
new Collection<Attribute>(attributes.ToArray()));
new Collection<Attribute>(attributes));
if (parameterAst.DefaultValue != null)
{
@@ -732,6 +732,7 @@ namespace System.Management.Automation
{ typeof(decimal), new[] { "decimal" } },
{ typeof(double), new[] { "double" } },
{ typeof(DscResourceAttribute), new[] { "DscResource"} },
{ typeof(ExperimentalAttribute), new[] { "Experimental" } },
{ typeof(float), new[] { "float", "single" } },
{ typeof(Guid), new[] { "guid" } },
{ typeof(Hashtable), new[] { "hashtable" } },
@@ -55,6 +55,7 @@ namespace System.Management.Automation.Language
bool HasAnyScriptBlockAttributes();
RuntimeDefinedParameterDictionary GetParameterMetadata(bool automaticPositions, ref bool usesCmdletBinding);
IEnumerable<Attribute> GetScriptBlockAttributes();
IEnumerable<ExperimentalAttribute> GetExperimentalAttributes();
bool UsesCmdletBinding();
ReadOnlyCollection<ParameterAst> Parameters { get; }
@@ -1370,6 +1371,61 @@ namespace System.Management.Automation.Language
}
}
IEnumerable<ExperimentalAttribute> IParameterMetadataProvider.GetExperimentalAttributes()
{
for (int index = 0; index < Attributes.Count; index++)
{
var attributeAst = Attributes[index];
var expAttr = GetExpAttributeHelper(attributeAst);
if (expAttr != null) { yield return expAttr; }
}
if (ParamBlock != null)
{
for (int index = 0; index < ParamBlock.Attributes.Count; index++)
{
var attributeAst = ParamBlock.Attributes[index];
var expAttr = GetExpAttributeHelper(attributeAst);
if (expAttr != null) { yield return expAttr; }
}
}
ExperimentalAttribute GetExpAttributeHelper(AttributeAst attributeAst)
{
AttributeAst potentialExpAttr = null;
string expAttrTypeName = typeof(ExperimentalAttribute).FullName;
string attrAstTypeName = attributeAst.TypeName.Name;
if (TypeAccelerators.Get.TryGetValue(attrAstTypeName, out Type attrType) && attrType == typeof(ExperimentalAttribute))
{
potentialExpAttr = attributeAst;
}
else if (expAttrTypeName.EndsWith(attrAstTypeName, StringComparison.OrdinalIgnoreCase))
{
// Handle two cases:
// 1. declare the attribute using full type name;
// 2. declare the attribute using partial type name due to 'using namespace'.
int expAttrLength = expAttrTypeName.Length;
int attrAstLength = attrAstTypeName.Length;
if (expAttrLength == attrAstLength || expAttrTypeName[expAttrLength - attrAstLength - 1] == '.')
{
potentialExpAttr = attributeAst;
}
}
if (potentialExpAttr != null)
{
try
{
return Compiler.GetAttribute(potentialExpAttr) as ExperimentalAttribute;
}
catch (Exception) { /* catch all and assume it's not a declaration of ExperimentalAttribute */ }
}
return null;
}
}
ReadOnlyCollection<ParameterAst> IParameterMetadataProvider.Parameters
{
get { return (ParamBlock != null) ? this.ParamBlock.Parameters : null; }
@@ -3299,6 +3355,11 @@ namespace System.Management.Automation.Language
return ((IParameterMetadataProvider)_functionDefinitionAst).GetScriptBlockAttributes();
}
IEnumerable<ExperimentalAttribute> IParameterMetadataProvider.GetExperimentalAttributes()
{
return ((IParameterMetadataProvider)_functionDefinitionAst).GetExperimentalAttributes();
}
bool IParameterMetadataProvider.UsesCmdletBinding()
{
return ((IParameterMetadataProvider)_functionDefinitionAst).UsesCmdletBinding();
@@ -3412,6 +3473,11 @@ namespace System.Management.Automation.Language
return ((IParameterMetadataProvider)Body).GetScriptBlockAttributes();
}
public IEnumerable<ExperimentalAttribute> GetExperimentalAttributes()
{
return ((IParameterMetadataProvider)Body).GetExperimentalAttributes();
}
public bool UsesCmdletBinding()
{
return false;
@@ -3693,6 +3759,11 @@ namespace System.Management.Automation.Language
return ((IParameterMetadataProvider)Body).GetScriptBlockAttributes();
}
IEnumerable<ExperimentalAttribute> IParameterMetadataProvider.GetExperimentalAttributes()
{
return ((IParameterMetadataProvider)Body).GetExperimentalAttributes();
}
ReadOnlyCollection<ParameterAst> IParameterMetadataProvider.Parameters
{
get { return Parameters ?? (Body.ParamBlock != null ? Body.ParamBlock.Parameters : null); }
@@ -348,6 +348,26 @@ namespace System.Management.Automation
}
}
internal ExperimentalAttribute ExperimentalAttribute
{
get
{
if (_expAttribute == ExperimentalAttribute.None)
{
lock (this)
{
if (_expAttribute == ExperimentalAttribute.None)
{
_expAttribute = Ast.GetExperimentalAttributes().FirstOrDefault();
}
}
}
return _expAttribute;
}
}
private ExperimentalAttribute _expAttribute = ExperimentalAttribute.None;
public MergedCommandParameterMetadata GetParameterMetadata(ScriptBlock scriptBlock)
{
if (_parameterMetadata == null)
@@ -1228,6 +1248,11 @@ namespace System.Management.Automation
get { return _scriptBlockData.ObsoleteAttribute; }
}
internal ExperimentalAttribute ExperimentalAttribute
{
get { return _scriptBlockData.ExperimentalAttribute; }
}
internal bool Compile(bool optimized)
{
return _scriptBlockData.Compile(optimized);
@@ -1212,8 +1212,12 @@ namespace System.Management.Automation
ScriptBlock scriptBlock = scriptBlockExpressionWrapper.GetScriptBlock(
context, functionDefinitionAst.IsFilter);
context.EngineSessionState.SetFunctionRaw(functionDefinitionAst.Name,
scriptBlock, context.EngineSessionState.CurrentScope.ScopeOrigin);
var expAttribute = scriptBlock.ExperimentalAttribute;
if (expAttribute == null || expAttribute.ToShow)
{
context.EngineSessionState.SetFunctionRaw(functionDefinitionAst.Name,
scriptBlock, context.EngineSessionState.CurrentScope.ScopeOrigin);
}
}
catch (Exception exception)
{
@@ -218,4 +218,10 @@ The #requires statement must be in one of the following formats:
<data name="GetCommandShowCommandInfoParamError" xml:space="preserve">
<value>The ShowCommandInfo and Syntax parameters cannot be specified together.</value>
</data>
<data name="ScriptDisabledWhenFeatureOn" xml:space="preserve">
<value>This script command is disabled when the experimental feature '{0}' has been turned on.</value>
</data>
<data name="ScriptDisabledWhenFeatureOff" xml:space="preserve">
<value>This script command is disabled when the experimental feature '{0}' has been turned off.</value>
</data>
</root>