-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Fix a module-loading regression that caused an infinite loop #6843
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2820,7 +2820,7 @@ internal PSModuleInfo LoadModuleManifest( | |
| { | ||
| bool found = false; | ||
| // Never load nested modules to the global scope. | ||
| bool oldGLobal = this.BaseGlobal; | ||
| bool oldGlobal = this.BaseGlobal; | ||
| this.BaseGlobal = false; | ||
|
|
||
| PSModuleInfo nestedModule = LoadModuleNamedInManifest( | ||
|
|
@@ -2838,7 +2838,7 @@ internal PSModuleInfo LoadModuleManifest( | |
| out found, | ||
| shortModuleName: null); | ||
|
|
||
| this.BaseGlobal = oldGLobal; | ||
| this.BaseGlobal = oldGlobal; | ||
|
|
||
| // If found, add it to the parent's list of NestedModules | ||
| if (found) | ||
|
|
@@ -2976,30 +2976,16 @@ internal PSModuleInfo LoadModuleManifest( | |
| BaseCmdletPatterns = oldCmdletPatterns; | ||
| } | ||
|
|
||
| // If there is an existing session state and the new module info | ||
| // session state is empty, then use the existing session state. | ||
| // This will be the case when ModuleToProcess is a binary module | ||
| // and there were nested modules. | ||
| if (newManifestInfo.SessionState == null && ss != null) | ||
| { | ||
| newManifestInfo.SessionState = ss; | ||
| ss.Internal.Module = newManifestInfo; | ||
| } | ||
| // If the new module info session state is not empty but there is no | ||
| // existing session state, then use the new module info's sessions state; | ||
| // This will be the case if the module to process is a script module | ||
| // but there where no nested modules. | ||
| else if (newManifestInfo.SessionState != null && ss == null) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| { | ||
| ss = newManifestInfo.SessionState; | ||
| } | ||
|
|
||
| // We don't need to care if they are both null which will be the | ||
| // case when there is only a binary module in ModuleToProcess and | ||
| // no nested modules. At this point the moduleInfo and the value in | ||
| // ss should be identical. | ||
| Dbg.Assert(newManifestInfo.SessionState == ss, | ||
| "ss and newManifestInfo.SessionState should be the same after handling ModuleToProcess"); | ||
| // For most cases, 'newManifestInfo.SessionState' should be identical to 'ss': | ||
| // 1. when 'importingModule == true', 'newManifestInfo' uses the same session state as 'ss' because we passed in 'ss' when loading the RootModule via 'LoadModuleNamedInManifest'. | ||
| // 2. when 'importingModule == false', both session states will be null since we are in module analysis mode (Get-Module -ListAvailable). | ||
| // | ||
| // However, there is one exception when the RootModule is also put in NestedModules in the module manifest (ill-organized module structure). | ||
| // For example, module folder 'test' contains two files: 'test.psd1' and 'test.psm1', and 'test.psd1' has the following content: | ||
| // "@{ ModuleVersion = '0.0.1'; RootModule = 'test'; NestedModules = @('test') }" | ||
| // | ||
| // In that case, the nested module will first be loaded with a different session state, and then when trying to load the RootModule via 'LoadModuleNamedInManifest', | ||
| // the same loaded nested module will be reused for the RootModule by 'LoadModuleNamedInManifest'. | ||
|
|
||
| // Change the module name to match the manifest name, not the original name | ||
| newManifestInfo.SetName(manifestInfo.Name); | ||
|
|
@@ -3248,7 +3234,6 @@ internal PSModuleInfo LoadModuleManifest( | |
| { | ||
| if ((exportedCmdlets != null) && (ss != null)) | ||
| { | ||
| Dbg.Assert(ss.Internal.ExportedCmdlets != null, "ss.Internal.ExportedCmdlets should not be null"); | ||
| manifestInfo.ExportedCmdlets.Clear(); | ||
|
|
||
| // Mark stuff for export | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -577,6 +577,15 @@ public ReadOnlyDictionary<string, TypeDefinitionAst> GetExportedTypeDefinitions( | |
| var res = new Dictionary<string, TypeDefinitionAst>(StringComparer.OrdinalIgnoreCase); | ||
| foreach (var nestedModule in this.NestedModules) | ||
| { | ||
| if (nestedModule == this) | ||
| { | ||
| // Circular nested modules could happen with ill-organized module structure. | ||
| // For example, module folder 'test' has two files: 'test.psd1' and 'test.psm1', and 'test.psd1' has the following content: | ||
| // "@{ ModuleVersion = '0.0.1'; RootModule = 'test'; NestedModules = @('test') }" | ||
| // Then, 'Import-Module test.psd1 -PassThru' will return a ModuleInfo object with circular nested modules. | ||
| continue; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Based on Issue which we fix I believe it make sense to inform users about the situation or even terminated the cmdlet.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Turning something that works to an error is a hard breaking change. As for the warning, I'm not sure if we should do it, since technically import-module is doing what it's asked to do ... (have itself as the nested module).
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can ScriptAnalyzer catch this?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point! I think the script analyzer should be able to catch this kind of issue. That's the best place to throw a warning. I just checked in VS Code and the analyzer doesn't catch that now. I can open an issue at the script analyzer repo.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Issue opened: PowerShell/PSScriptAnalyzer#997 |
||
| } | ||
|
|
||
| foreach (var typePairs in nestedModule.GetExportedTypeDefinitions()) | ||
| { | ||
| // The last one name wins! It's the same for command names in nested modules. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When it's not module analysis (
get-module -list),sswill never be null. I verified that binary root module also usesssas the session state. This is why this check is no longer needed.