diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs index bec70e8b70..e0f1ecbbbd 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionAnalysis.cs @@ -1756,15 +1756,15 @@ namespace System.Management.Automation { PropertyInfo[] propertyInfos = attributeType.GetProperties(BindingFlags.Public | BindingFlags.Instance); List result = new List(); - foreach (PropertyInfo pro in propertyInfos) + foreach (PropertyInfo property in propertyInfos) { // Ignore getter-only properties, including 'TypeId' (all attributes inherit it). - if (!pro.CanWrite) { continue; } + if (!property.CanWrite) { continue; } - if (pro.Name.StartsWith(argName, StringComparison.OrdinalIgnoreCase)) + if (property.Name.StartsWith(argName, StringComparison.OrdinalIgnoreCase)) { - result.Add(new CompletionResult(pro.Name, pro.Name, CompletionResultType.Property, - pro.PropertyType.ToString() + " " + pro.Name)); + result.Add(new CompletionResult(property.Name, property.Name, CompletionResultType.Property, + property.PropertyType.ToString() + " " + property.Name)); } } return result; diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index cabccc1ce2..9434d73375 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -115,7 +115,7 @@ namespace System.Management.Automation /// private static ImmutableHashSet ProcessEnabledFeatures(string[] enabledFeatures) { - if (enabledFeatures.Length == 0) { return ImmutableHashSet.Create(); } + if (enabledFeatures.Length == 0) { return ImmutableHashSet.Empty; } var list = new List(enabledFeatures.Length); foreach (string name in enabledFeatures) @@ -169,7 +169,7 @@ namespace System.Management.Automation /// internal static bool IsEngineFeatureName(string featureName) { - return featureName.IndexOf('.') == -1 && featureName.Length > 2 && featureName.StartsWith("PS", StringComparison.Ordinal); + return featureName.Length > 2 && featureName.IndexOf('.') == -1 && featureName.StartsWith("PS", StringComparison.Ordinal); } /// @@ -205,7 +205,7 @@ namespace System.Management.Automation } ExperimentAction action = experimentAction; - if (!HasEnabled(experimentName)) + if (!IsEnabled(experimentName)) { action = (action == ExperimentAction.Hide) ? ExperimentAction.Show : ExperimentAction.Hide; } @@ -216,7 +216,7 @@ namespace System.Management.Automation /// Check if the specified experimental feature has been enabled. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool HasEnabled(string featureName) + public static bool IsEnabled(string featureName) { return EnabledExperimentalFeatureNames.Contains(featureName); } @@ -298,7 +298,8 @@ namespace System.Management.Automation /// private ExperimentAction EffectiveAction { - get { + get + { if (_effectiveAction == ExperimentAction.None) { _effectiveAction = ExperimentalFeature.GetActionToTake(ExperimentName, ExperimentAction); @@ -306,6 +307,6 @@ namespace System.Management.Automation return _effectiveAction; } } - private ExperimentAction _effectiveAction = default(ExperimentAction); + private ExperimentAction _effectiveAction = ExperimentAction.None; } -} \ No newline at end of file +} diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index 90bec19566..b480ade349 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -3764,7 +3764,7 @@ namespace System.Management.Automation.Runspaces s_PSSnapInTracer.WriteLine("Loading assembly for psSnapIn {0} succeeded", psSnapInInfo.Name); - PSSnapInHelpers.AnalyzePSSnapInAssembly(assembly, psSnapInInfo.Name, psSnapInInfo, null, true, out cmdlets, out aliases, out providers, out helpFile); + PSSnapInHelpers.AnalyzePSSnapInAssembly(assembly, psSnapInInfo.Name, psSnapInInfo, moduleInfo: null, out cmdlets, out aliases, out providers, out helpFile); } // We skip checking if the file exists when it's in $PSHOME because of magic @@ -4053,8 +4053,7 @@ namespace System.Management.Automation.Runspaces Dictionary providers = null; string assemblyPath = assembly.Location; - string throwAwayHelpFile = null; - PSSnapInHelpers.AnalyzePSSnapInAssembly(assembly, assemblyPath, null, module, true, out cmdlets, out aliases, out providers, out throwAwayHelpFile); + PSSnapInHelpers.AnalyzePSSnapInAssembly(assembly, assemblyPath, psSnapInInfo: null, module, out cmdlets, out aliases, out providers, helpFile: out _); // If this is an in-memory assembly, don't added it to the list of AssemblyEntries // since it can't be loaded by path or name @@ -4957,14 +4956,14 @@ end return assembly; } - private static bool GetCustomAttribute(Type decoratedType, out T attribute) where T : Attribute + private static bool TryGetCustomAttribute(Type decoratedType, out T attribute) where T : Attribute { var attributes = decoratedType.GetCustomAttributes(inherit: false); attribute = attributes.FirstOrDefault(); return attribute != null; } - internal static void AnalyzePSSnapInAssembly(Assembly assembly, string name, PSSnapInInfo psSnapInInfo, PSModuleInfo moduleInfo, bool isModuleLoad, + internal static void AnalyzePSSnapInAssembly(Assembly assembly, string name, PSSnapInInfo psSnapInInfo, PSModuleInfo moduleInfo, out Dictionary cmdlets, out Dictionary> aliases, out Dictionary providers, out string helpFile) { @@ -5048,8 +5047,18 @@ end string assemblyPath = assembly.Location; if (cmdlets != null || providers != null) { - s_PSSnapInTracer.WriteLine("Returning cached cmdlet and provider entries for {0}", assemblyPath); - return; + 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); + var assemblyTypes = GetAssemblyTypes(assembly, name); + ExecuteModuleInitializer(assembly, assemblyTypes); + return; + } } s_PSSnapInTracer.WriteLine("Analyzing assembly {0} for cmdlet and providers", assemblyPath); @@ -5066,7 +5075,7 @@ end Dictionary cmdletsCheck = null; Dictionary providersCheck = null; Dictionary> aliasesCheck = null; - AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, isModuleLoad, ref cmdletsCheck, ref aliasesCheck, ref providersCheck, helpFile); + AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, helpFile, ref cmdletsCheck, ref aliasesCheck, ref providersCheck); Diagnostics.Assert(aliasesCheck == null, "InitializeCoreCmdletsAndProviders assumes no aliases are defined in System.Management.Automation.dll"); Diagnostics.Assert(providersCheck.Count == providers.Count, "new Provider added to System.Management.Automation.dll - update InitializeCoreCmdletsAndProviders"); @@ -5110,7 +5119,7 @@ end } else { - AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, isModuleLoad, ref cmdlets, ref aliases, ref providers, helpFile); + AnalyzeModuleAssemblyWithReflection(assembly, name, psSnapInInfo, moduleInfo, helpFile, ref cmdlets, ref aliases, ref providers); } // Cache the cmdlet and provider info for this assembly... @@ -5153,23 +5162,22 @@ end } private static void AnalyzeModuleAssemblyWithReflection(Assembly assembly, string name, PSSnapInInfo psSnapInInfo, - PSModuleInfo moduleInfo, bool isModuleLoad, + PSModuleInfo moduleInfo, string helpFile, ref Dictionary cmdlets, ref Dictionary> aliases, - ref Dictionary providers, - string helpFile) + ref Dictionary providers) { var assemblyTypes = GetAssemblyTypes(assembly, name); - ExecuteModuleInitializer(assembly, assemblyTypes, isModuleLoad); + ExecuteModuleInitializer(assembly, assemblyTypes); foreach (Type type in assemblyTypes) { if (!HasDefaultConstructor(type)) { continue; } // Check for cmdlets - if (IsCmdletClass(type) && GetCustomAttribute(type, out CmdletAttribute cmdletAttribute)) + if (IsCmdletClass(type) && TryGetCustomAttribute(type, out CmdletAttribute cmdletAttribute)) { - if (GetCustomAttribute(type, out ExperimentalAttribute expAttribute) && expAttribute.ToHide) + if (TryGetCustomAttribute(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. @@ -5191,7 +5199,7 @@ end cmdlets = cmdlets ?? new Dictionary(StringComparer.OrdinalIgnoreCase); cmdlets.Add(cmdletName, cmdlet); - if (GetCustomAttribute(type, out AliasAttribute aliasAttribute)) + if (TryGetCustomAttribute(type, out AliasAttribute aliasAttribute)) { aliases = aliases ?? new Dictionary>(StringComparer.OrdinalIgnoreCase); @@ -5212,9 +5220,9 @@ end s_PSSnapInTracer.WriteLine("{0} from type {1} is added as a cmdlet. ", cmdletName, type.FullName); } // Check for providers - else if (IsProviderClass(type) && GetCustomAttribute(type, out CmdletProviderAttribute providerAttribute)) + else if (IsProviderClass(type) && TryGetCustomAttribute(type, out CmdletProviderAttribute providerAttribute)) { - if (GetCustomAttribute(type, out ExperimentalAttribute expAttribute) && expAttribute.ToHide) + if (TryGetCustomAttribute(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. @@ -5342,13 +5350,14 @@ end } } - private static void ExecuteModuleInitializer(Assembly assembly, IEnumerable assemblyTypes, bool isModuleLoad) + private static void ExecuteModuleInitializer(Assembly assembly, IEnumerable assemblyTypes) { foreach (Type type in assemblyTypes) { - if (isModuleLoad && typeof(IModuleAssemblyInitializer).IsAssignableFrom(type)) + if (typeof(IModuleAssemblyInitializer).IsAssignableFrom(type)) { - IModuleAssemblyInitializer moduleInitializer = (IModuleAssemblyInitializer)Activator.CreateInstance(type, true); + s_assembliesWithModuleInitializerCache.Value[assembly] = true; + var moduleInitializer = (IModuleAssemblyInitializer)Activator.CreateInstance(type, true); moduleInitializer.OnImport(); } } @@ -5382,6 +5391,8 @@ end new Lazy>>>>(); private static Lazy>> s_providerCache = new Lazy>>(); + // 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> s_assembliesWithModuleInitializerCache = new Lazy>(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool IsCmdletClass(Type type) diff --git a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs index 412b3446db..ca66ffdc0a 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleCmdletBase.cs @@ -2047,20 +2047,18 @@ namespace Microsoft.PowerShell.Commands string moduleName = ModuleIntrinsics.GetModuleName(moduleManifestPath); expFeatureList = new List(features.Length); - foreach (var feature in features) + foreach (Hashtable feature in features) { string featureName = feature["Name"] as string; - if (String.IsNullOrEmpty(featureName)) + if (string.IsNullOrEmpty(featureName)) { nameMissingOrEmpty = true; - continue; } - - if (ExperimentalFeature.IsModuleFeatureName(featureName, moduleName)) + else if (ExperimentalFeature.IsModuleFeatureName(featureName, moduleName)) { string featureDescription = feature["Description"] as string; expFeatureList.Add(new ExperimentalFeature(featureName, featureDescription, moduleManifestPath, - ExperimentalFeature.HasEnabled(featureName))); + ExperimentalFeature.IsEnabled(featureName))); } else { diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index 99fcdb89eb..d2187f6aca 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -489,7 +489,7 @@ namespace System.Management.Automation { string featureDescription = feature["Description"] as string; expFeatureList.Add(new ExperimentalFeature(featureName, featureDescription, manifestPath, - ExperimentalFeature.HasEnabled(featureName))); + ExperimentalFeature.IsEnabled(featureName))); } } return expFeatureList.ToArray(); diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 239525d2c3..153d409b6c 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -1001,8 +1001,8 @@ namespace System.Management.Automation.Language internal static RuntimeDefinedParameterDictionary GetParameterMetaData(ReadOnlyCollection parameters, bool automaticPositions, ref bool usesCmdletBinding) { - var md = new RuntimeDefinedParameterDictionary(); - var listMd = new List(parameters.Count); + var runtimeDefinedParamDict = new RuntimeDefinedParameterDictionary(); + var runtimeDefinedParamList = new List(parameters.Count); var customParameterSet = false; for (int index = 0; index < parameters.Count; index++) { @@ -1010,17 +1010,17 @@ namespace System.Management.Automation.Language var rdp = GetRuntimeDefinedParameter(param, ref customParameterSet, ref usesCmdletBinding); if (rdp != null) { - listMd.Add(rdp); - md.Add(param.Name.VariablePath.UserPath, rdp); + runtimeDefinedParamList.Add(rdp); + runtimeDefinedParamDict.Add(param.Name.VariablePath.UserPath, rdp); } } int pos = 0; if (automaticPositions && !customParameterSet) { - for (int index = 0; index < listMd.Count; index++) + for (int index = 0; index < runtimeDefinedParamList.Count; index++) { - var rdp = listMd[index]; + var rdp = runtimeDefinedParamList[index]; var paramAttribute = (ParameterAttribute)rdp.Attributes.First(attr => attr is ParameterAttribute); if (!(rdp.ParameterType == typeof(SwitchParameter))) { @@ -1029,8 +1029,8 @@ namespace System.Management.Automation.Language } } - md.Data = listMd.ToArray(); - return md; + runtimeDefinedParamDict.Data = runtimeDefinedParamList.ToArray(); + return runtimeDefinedParamDict; } private static readonly Dictionary s_attributeGeneratorCache = new Dictionary(); @@ -1165,9 +1165,8 @@ namespace System.Management.Automation.Language int positionalArgCount = ast.PositionalArguments.Count; if (positionalArgCount != 2) { - throw InterpreterError.NewInterpreterException(null, typeof(MethodException), ast.Extent, - "MethodCountCouldNotFindBest", ExtendedTypeSystem.MethodArgumentCountException, ".ctor", - positionalArgCount); + throw InterpreterError.NewInterpreterException(targetObject: null, typeof(MethodException), ast.Extent, + "MethodCountCouldNotFindBest", ExtendedTypeSystem.MethodArgumentCountException, ".ctor", positionalArgCount); } var (name, action) = GetFeatureNameAndAction(ast); @@ -1188,9 +1187,8 @@ namespace System.Management.Automation.Language result = new ParameterAttribute(name, action); break; default: - throw InterpreterError.NewInterpreterException(null, typeof(MethodException), ast.Extent, - "MethodCountCouldNotFindBest", ExtendedTypeSystem.MethodArgumentCountException, ".ctor", - positionalArgCount); + throw InterpreterError.NewInterpreterException(targetObject: null, typeof(MethodException), ast.Extent, + "MethodCountCouldNotFindBest", ExtendedTypeSystem.MethodArgumentCountException, ".ctor", positionalArgCount); } foreach (var namedArg in ast.NamedArguments)