diff --git a/.ionide/symbolCache.db b/.ionide/symbolCache.db new file mode 100644 index 00000000000..8a73c4f77ed Binary files /dev/null and b/.ionide/symbolCache.db differ diff --git a/.vscode/launch.json b/.vscode/launch.json index 4ba261df08c..c77c46956a8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,6 +1,17 @@ { "version": "0.2.0", "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/bin/Debug//", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "console": "internalConsole" + }, { "name": ".NET Core Launch", "type": "coreclr", @@ -36,6 +47,28 @@ "${command:SpecifyScriptArgs}" ], "cwd": "${file}" + }, + { + "name": "PWSH .NET Core WSL Launch", + "type": "coreclr", + "request": "launch", + "program": "/home/staffan/.dotnet/dotnet", + "args": [ + "/mnt/d/repos/PowerShell/src/powershell-unix/bin/Debug/netcoreapp3.1/linux-x64/pwsh.dll", + "-file", + "/mnt/d/repos/PowerShell/TestDir/test.ps1" + ], + "cwd": "/mnt/d/repos/PowerShell/src/powershell-unix/bin/Debug/netcoreapp3.1/linux-x64/", + "stopAtEntry": false, + "console": "internalConsole", + "pipeTransport": { + "pipeCwd": "${workspaceRoot}", + "pipeProgram": "bash.exe", + "pipeArgs": [ + "-c" + ], + "debuggerPath": "~/vsdbg/vsdbg" + } } ] } diff --git a/PowerShell.sln b/PowerShell.sln index b164361d7d2..147353fbc1d 100644 --- a/PowerShell.sln +++ b/PowerShell.sln @@ -1,7 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -# https://github.com/dotnet/project-system/blob/master/docs/opening-with-new-project-system.md#project-type-guids -VisualStudioVersion = 15.0.26730.12 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.87 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "powershell-win-core", "src\powershell-win-core\powershell-win-core.csproj", "{8359D422-E0C4-4A0D-94EB-3C9DD16B7932}" EndProject @@ -33,15 +32,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.Markdo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xUnit.tests", "test\xUnit\xUnit.tests.csproj", "{08704934-9764-48CE-86DB-BCF0A1CF7899}" EndProject -# Configuration mapping comment -# All global configurations must be mapped to project configurations -# -# 4BC19063-1F66-467B-87DE-80449C72BCD6 - Microsoft.PowerShell.SDK -# 8359D422-E0C4-4A0D-94EB-3C9DD16B7932 - PowerShell-Win -# Linux is invalid and mapped to Release -# -# 73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690 - powershell-unix -# Only Linux is valid, all configurations mapped to Linux Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeCoverage|Any CPU = CodeCoverage|Any CPU @@ -58,44 +48,44 @@ Global {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Linux|Any CPU.Build.0 = Release|Any CPU {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Release|Any CPU.ActiveCfg = Release|Any CPU {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Release|Any CPU.Build.0 = Release|Any CPU - {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.Build.0 = Linux|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.Build.0 = Release|Any CPU {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Release|Any CPU.Build.0 = Release|Any CPU - {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {EAB203E1-2A68-4166-BE54-5C44DE825229}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EAB203E1-2A68-4166-BE54-5C44DE825229}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.Build.0 = Linux|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.Build.0 = Release|Any CPU {EAB203E1-2A68-4166-BE54-5C44DE825229}.Release|Any CPU.ActiveCfg = Release|Any CPU {EAB203E1-2A68-4166-BE54-5C44DE825229}.Release|Any CPU.Build.0 = Release|Any CPU - {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {981D3972-343D-4E17-935B-037E1C622771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {981D3972-343D-4E17-935B-037E1C622771}.Debug|Any CPU.Build.0 = Debug|Any CPU - {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.Build.0 = Linux|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.Build.0 = Release|Any CPU {981D3972-343D-4E17-935B-037E1C622771}.Release|Any CPU.ActiveCfg = Release|Any CPU {981D3972-343D-4E17-935B-037E1C622771}.Release|Any CPU.Build.0 = Release|Any CPU - {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.Build.0 = Linux|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.Build.0 = Release|Any CPU {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Release|Any CPU.ActiveCfg = Release|Any CPU {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Release|Any CPU.Build.0 = Release|Any CPU - {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.Build.0 = Linux|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.Build.0 = Release|Any CPU {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Release|Any CPU.Build.0 = Release|Any CPU {4BC19063-1F66-467B-87DE-80449C72BCD6}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU @@ -114,60 +104,60 @@ Global {131A8527-92D7-468F-822D-5354229A865C}.Linux|Any CPU.Build.0 = Release|Any CPU {131A8527-92D7-468F-822D-5354229A865C}.Release|Any CPU.ActiveCfg = Release|Any CPU {131A8527-92D7-468F-822D-5354229A865C}.Release|Any CPU.Build.0 = Release|Any CPU - {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {439A24FC-8E0A-48B6-8227-44C297311F49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {439A24FC-8E0A-48B6-8227-44C297311F49}.Debug|Any CPU.Build.0 = Debug|Any CPU - {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.Build.0 = Linux|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.Build.0 = Release|Any CPU {439A24FC-8E0A-48B6-8227-44C297311F49}.Release|Any CPU.ActiveCfg = Release|Any CPU {439A24FC-8E0A-48B6-8227-44C297311F49}.Release|Any CPU.Build.0 = Release|Any CPU - {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {8F63D134-E413-4181-936D-D82F3F5F1D85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8F63D134-E413-4181-936D-D82F3F5F1D85}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.Build.0 = Linux|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.Build.0 = Release|Any CPU {8F63D134-E413-4181-936D-D82F3F5F1D85}.Release|Any CPU.ActiveCfg = Release|Any CPU {8F63D134-E413-4181-936D-D82F3F5F1D85}.Release|Any CPU.Build.0 = Release|Any CPU - {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.Build.0 = Linux|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.Build.0 = Release|Any CPU {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Release|Any CPU.Build.0 = Release|Any CPU - {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.Build.0 = Linux|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.Build.0 = Release|Any CPU {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Release|Any CPU.ActiveCfg = Release|Any CPU {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Release|Any CPU.Build.0 = Release|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.Build.0 = Linux|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.ActiveCfg = Release|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.Build.0 = Release|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Debug|Any CPU.Build.0 = Debug|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU - {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.Build.0 = Release|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.Build.0 = Release|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Linux|Any CPU.Build.0 = Linux|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Linux|Any CPU.Build.0 = Release|Any CPU {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Release|Any CPU.Build.0 = Release|Any CPU - {08704934-9764-48CE-86DB-BCF0A1CF7899}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU - {08704934-9764-48CE-86DB-BCF0A1CF7899}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU {08704934-9764-48CE-86DB-BCF0A1CF7899}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08704934-9764-48CE-86DB-BCF0A1CF7899}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08704934-9764-48CE-86DB-BCF0A1CF7899}.Linux|Any CPU.ActiveCfg = Linux|Any CPU - {08704934-9764-48CE-86DB-BCF0A1CF7899}.Linux|Any CPU.Build.0 = Linux|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.Linux|Any CPU.Build.0 = Release|Any CPU {08704934-9764-48CE-86DB-BCF0A1CF7899}.Release|Any CPU.ActiveCfg = Release|Any CPU {08704934-9764-48CE-86DB-BCF0A1CF7899}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection diff --git a/PowerShell.sln.DotSettings b/PowerShell.sln.DotSettings new file mode 100644 index 00000000000..9b582d5688d --- /dev/null +++ b/PowerShell.sln.DotSettings @@ -0,0 +1,9 @@ + + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + True + True + True + True + True + True + True \ No newline at end of file diff --git a/TestDir/test.ps1 b/TestDir/test.ps1 new file mode 100644 index 00000000000..46ec4c747c9 --- /dev/null +++ b/TestDir/test.ps1 @@ -0,0 +1,3 @@ +import-module /mnt/d/repos/PowerShell/build.psm1 +$Env:POWERSHELL_TELEMETRY_OPTOUT = 1 +Start-PSPester /mnt/d/repos/PowerShell/test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1 -Tag CI -ExcludeTag RequireSudoOnUnix diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs index 5dbe8fcc918..33c8174fcd7 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs @@ -139,98 +139,110 @@ internal void WriteContentObject(object content, long readCount, PathInfo pathIn context != null, "The caller should verify the context."); - PSObject result = PSObject.AsPSObject(content); + if (_currentContentItem == null + || (_currentContentItem.PathInfo != pathInfo + && + string.Compare(pathInfo.Path,_currentContentItem.PathInfo.Path, StringComparison.OrdinalIgnoreCase) != 0) + ) + { + // Generate a new cache item and cache the notes + string parentPath = null; + string childName = null; + try + { + var root = pathInfo.Drive != null ? pathInfo.Drive.Root : string.Empty; + parentPath = SessionState.Path.ParseParent(pathInfo.Path, root, context); + childName = SessionState.Path.ParseChildName(pathInfo.Path, context); + + } + catch (NotSupportedException) + { + // Ignore. The object just won't have ParentPath or ChildName set. + } + _currentContentItem = new ContentPathsCache(pathInfo) + { + PSPath = pathInfo.Path, + ParentPath = parentPath, + ChildName = childName, + Drive = pathInfo.Drive, + Provider = pathInfo.Provider + }; + } + + if (ExperimentalFeature.IsEnabled("PSOptimizedProviderObjects")) + { + WriteObject(new ContentPSObject(content, pathInfo, _currentContentItem.ParentPath, _currentContentItem.ChildName, readCount)); + return; + } + + PSObject result = PSObject.AsPSObject(content); Dbg.Diagnostics.Assert( result != null, "A PSObject should always be constructed."); // Use the cached notes if the cache exists and the path is still the same - PSNoteProperty note; - - if (_currentContentItem != null && - ((_currentContentItem.PathInfo == pathInfo) || - ( - string.Compare( - pathInfo.Path, - _currentContentItem.PathInfo.Path, - StringComparison.OrdinalIgnoreCase) == 0) - ) - ) - { - result = _currentContentItem.AttachNotes(result); - } - else - { - // Generate a new cache item and cache the notes - - _currentContentItem = new ContentPathsCache(pathInfo); + _currentContentItem.AttachNotes(result); + WriteObject(result); + } - // Construct a provider qualified path as the Path note - string psPath = pathInfo.Path; - note = new PSNoteProperty("PSPath", psPath); - result.Properties.Add(note, true); - tracer.WriteLine("Attaching {0} = {1}", "PSPath", psPath); - _currentContentItem.PSPath = psPath; + internal class ContentPSObject : PSObject, IPSObjectExtendedMemberInfo + { + private readonly PathInfo _pathInfo; + private readonly string _parentPath; + private readonly string _childName; + private readonly long _readCount; - try - { - // Now get the parent path and child name + public ContentPSObject(object obj, PathInfo pathInfo, string parentPath, string childName, long readCount) : base(obj) + { + _pathInfo = pathInfo; + _parentPath = parentPath; + _childName = childName; + _readCount = readCount; + } - string parentPath = null; + [PSExtensionMember] + public PSDriveInfo PSDrive => _pathInfo.Drive; - if (pathInfo.Drive != null) - { - parentPath = SessionState.Path.ParseParent(pathInfo.Path, pathInfo.Drive.Root, context); - } - else - { - parentPath = SessionState.Path.ParseParent(pathInfo.Path, string.Empty, context); - } + [PSExtensionMember] + public string PSParentPath => _parentPath; - note = new PSNoteProperty("PSParentPath", parentPath); - result.Properties.Add(note, true); - tracer.WriteLine("Attaching {0} = {1}", "PSParentPath", parentPath); - _currentContentItem.ParentPath = parentPath; + [PSExtensionMember] + public string PSPath => LocationGlobber.GetProviderQualifiedPath(_pathInfo.Path, _pathInfo.Provider); - // Get the child name + [PSExtensionMember] + public string PSChildName => _childName; - string childName = SessionState.Path.ParseChildName(pathInfo.Path, context); - note = new PSNoteProperty("PSChildName", childName); - result.Properties.Add(note, true); - tracer.WriteLine("Attaching {0} = {1}", "PSChildName", childName); - _currentContentItem.ChildName = childName; - } - catch (NotSupportedException) - { - // Ignore. The object just won't have ParentPath or ChildName set. - } + [PSExtensionMember] + public long ReadCount => _readCount; - // PSDriveInfo + [PSExtensionMember] + public ProviderInfo PSProvider => _pathInfo.Provider; - if (pathInfo.Drive != null) + public T GetFirstOrDefault(MemberNamePredicate predicate) where T : PSMemberInfo + { + string GetMatchedPropertyName(MemberNamePredicate pred) { - PSDriveInfo drive = pathInfo.Drive; - note = new PSNoteProperty("PSDrive", drive); - result.Properties.Add(note, true); - tracer.WriteLine("Attaching {0} = {1}", "PSDrive", drive); - _currentContentItem.Drive = drive; + if (pred(nameof(PSParentPath))) + return nameof(PSParentPath); + if (pred(nameof(PSPath))) + return nameof(PSPath); + if (pred(nameof(PSChildName))) + return nameof(PSChildName); + if (pred(nameof(ReadCount))) + return nameof(ReadCount); + if (pred(nameof(PSProvider))) + return nameof(PSProvider); + return null; } - // ProviderInfo - - ProviderInfo provider = pathInfo.Provider; - note = new PSNoteProperty("PSProvider", provider); - result.Properties.Add(note, true); - tracer.WriteLine("Attaching {0} = {1}", "PSProvider", provider); - _currentContentItem.Provider = provider; + var propertyName = GetMatchedPropertyName(predicate); + return propertyName == null ? null : DotNetInstanceAdapter.GetDotNetProperty(this, propertyName); } - // Add the ReadCount note - note = new PSNoteProperty("ReadCount", readCount); - result.Properties.Add(note, true); + T IPSObjectExtendedMemberInfo.GetMember(string name) => DotNetInstanceAdapter.GetDotNetProperty(this, name); - WriteObject(result); + void IPSObjectExtendedMemberInfo.AddExtensionMembers(PSMemberInfoInternalCollection returnValue) => DotNetInstanceAdapter.AddExtensionProperties(this, returnValue); } /// diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs index af9d754861c..74fb1673a71 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs @@ -654,7 +654,10 @@ public override try { - ConsoleControl.SetMode(handle, newMode); + if (newMode != originalMode) + { + ConsoleControl.SetMode(handle, newMode); + } while (true) { int actualNumberOfInput = ConsoleControl.ReadConsoleInput(handle, ref inputRecords); diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/FileSystem_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/FileSystem_format_ps1xml.cs index b65fde9eb64..ff403d0a8dc 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/FileSystem_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/FileSystem_format_ps1xml.cs @@ -11,11 +11,10 @@ internal static IEnumerable GetFormatData() { var FileSystemTypes_GroupingFormat = CustomControl.Create() .StartEntry() - .StartFrame() + .StartFrame(leftIndent: 4) .AddText(FileSystemProviderStrings.DirectoryDisplayGrouping) - .AddScriptBlockExpressionBinding(@" - $_.PSParentPath.Replace(""Microsoft.PowerShell.Core\FileSystem::"", """") - ") + .AddScriptBlockExpressionBinding(@"if ($_ -is [System.IO.FileInfo]) {$_.DirectoryName} else {$_.Parent}") + .AddNewline() .EndFrame() .EndEntry() .EndControl(); diff --git a/src/System.Management.Automation/Properties/launchSettings.json b/src/System.Management.Automation/Properties/launchSettings.json new file mode 100644 index 00000000000..f90053fb726 --- /dev/null +++ b/src/System.Management.Automation/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "System.Management.Automation": { + "commandName": "Executable", + "executablePath": "D:\\repos\\PowerShell\\src\\powershell-win-core\\bin\\Debug\\netcoreapp3.1\\win7-x64\\publish\\pwsh.exe", + "commandLineArgs": "-nop", + "workingDirectory": "d:\\repos\\PowerShell" + } + } +} \ No newline at end of file diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index 86aaa9825f0..3f6dc5425e3 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -4,6 +4,7 @@ PowerShell's System.Management.Automation project $(NoWarn);CS1570;CS1734 System.Management.Automation + false @@ -29,10 +30,12 @@ + $(DefineConstants);CORECLR + $(DefineConstants);RESHARPER_ATTRIBUTES diff --git a/src/System.Management.Automation/engine/CommandParameter.cs b/src/System.Management.Automation/engine/CommandParameter.cs index 2a93440ff10..ba2682280a8 100644 --- a/src/System.Management.Automation/engine/CommandParameter.cs +++ b/src/System.Management.Automation/engine/CommandParameter.cs @@ -9,7 +9,7 @@ namespace System.Management.Automation /// /// Represents a parameter to the Command. /// - [DebuggerDisplay("{ParameterName}")] + [DebuggerDisplay("{DebuggerDisplay}")] internal sealed class CommandParameterInternal { private class Parameter @@ -56,6 +56,14 @@ internal string ParameterName } } + private string DebuggerDisplay + { + get => + ParameterNameSpecified + ? ArgumentSpecified ? $"{ParameterText} {ArgumentValue}" : ParameterText + : ArgumentSpecified ? ArgumentValue.ToString() : ""; + } + /// /// The text of the parameter, which typically includes the leading '-' (dash) and, if specified, the trailing ':'. /// diff --git a/src/System.Management.Automation/engine/CoreAdapter.cs b/src/System.Management.Automation/engine/CoreAdapter.cs index f78777c840b..b26b61dc7b6 100644 --- a/src/System.Management.Automation/engine/CoreAdapter.cs +++ b/src/System.Management.Automation/engine/CoreAdapter.cs @@ -3457,7 +3457,8 @@ private static Dictionary GetStaticEventReflectionTable /// of type. /// /// Type with properties to load in typeTable. - private static CacheTable GetInstancePropertyReflectionTable(Type type) + /// true if one properties declared on the type should be returned. + private static CacheTable GetInstancePropertyReflectionTable(Type type, bool declaredOnly = false) { lock (s_instancePropertyCacheTable) { @@ -3468,7 +3469,8 @@ private static CacheTable GetInstancePropertyReflectionTable(Type type) } typeTable = new CacheTable(); - PopulatePropertyReflectionTable(type, typeTable, instanceBindingFlags); + var flags = declaredOnly ? instanceBindingFlags | BindingFlags.DeclaredOnly : instanceBindingFlags; + PopulatePropertyReflectionTable(type, typeTable, flags); s_instancePropertyCacheTable[type] = typeTable; return typeTable; } @@ -3734,6 +3736,41 @@ internal void AddAllProperties(object obj, PSMemberInfoInternalCollection } } + public void AddExtensionProperties(object obj, PSMemberInfoInternalCollection members) where T : PSMemberInfo + { + bool lookingForProperties = typeof(T).IsAssignableFrom(typeof(PSProperty)); + + if (!lookingForProperties) + { + return; + } + + CacheTable table = GetInstancePropertyReflectionTable(obj.GetType(), declaredOnly: true); + + for (int i = 0; i < table.memberCollection.Count; i++) + { + if (table.memberCollection[i] is PropertyCacheEntry propertyEntry) + { + MemberInfo propertyEntryMember = propertyEntry.member; + if (!propertyEntryMember.GetCustomAttributes(typeof(PSExtensionMemberAttribute), false).Any()) + { + continue; + } + if (members[propertyEntryMember.Name] == null) + { + var isHidden = propertyEntryMember.GetCustomAttributes(typeof(HiddenAttribute), false).Any(); + + var member = new PSProperty(propertyEntryMember.Name, this, obj, propertyEntry) + { + IsHidden = isHidden, + isExtensionProperty = true, + } as T; + members.Add(member); + } + } + } + } + internal void AddAllMethods(object obj, PSMemberInfoInternalCollection members, bool ignoreDuplicates) where T : PSMemberInfo { if (!typeof(T).IsAssignableFrom(typeof(PSMethod))) diff --git a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs index b9f3bb4d160..d0e6f468026 100644 --- a/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs +++ b/src/System.Management.Automation/engine/ExperimentalFeature/ExperimentalFeature.cs @@ -46,6 +46,7 @@ public class ExperimentalFeature /// public bool Enabled { get; private set; } + /// /// Initializes a new instance of the class. /// @@ -127,6 +128,9 @@ static ExperimentalFeature() new ExperimentalFeature( name: "PSPipelineChainOperators", description: "Allow use of && and || as operators between pipeline invocations"), + new ExperimentalFeature( + name: "PSOptimizedProviderObjects", + description: "Low memory usage and CPU cost for FileSystem and Registry PSProperties"), }; EngineExperimentalFeatures = new ReadOnlyCollection(engineFeatures); diff --git a/src/System.Management.Automation/engine/MshMemberInfo.cs b/src/System.Management.Automation/engine/MshMemberInfo.cs index df4e78eff30..ce6bf0aa443 100644 --- a/src/System.Management.Automation/engine/MshMemberInfo.cs +++ b/src/System.Management.Automation/engine/MshMemberInfo.cs @@ -1042,6 +1042,7 @@ public override string ToString() internal object serializedValue; internal bool isDeserialized; + internal bool isExtensionProperty; /// /// This will be either instance.adapter or instance.clrAdapter. @@ -1103,7 +1104,7 @@ public override PSMemberInfo Copy() /// /// Gets the member type. /// - public override PSMemberTypes MemberType => PSMemberTypes.Property; + public override PSMemberTypes MemberType => isExtensionProperty ? PSMemberTypes.CodeProperty : PSMemberTypes.Property; private object GetAdaptedValue() { @@ -4825,7 +4826,9 @@ public override T this[string name] } } - return null; + return _mshOwner is IPSObjectExtendedMemberInfo memberInfo + ? memberInfo.GetMember(name) + : null; } } } @@ -4894,6 +4897,11 @@ private PSMemberInfoInternalCollection GetIntegratedMembers(MshMemberMatchOpt } } + if (_mshOwner is IPSObjectExtendedMemberInfo memberInfo) + { + memberInfo.AddExtensionMembers(returnValue); + } + return returnValue; } } @@ -5015,7 +5023,9 @@ internal override T FirstOrDefault(MemberNamePredicate predicate) } } - return null; + return _mshOwner is IPSObjectExtendedMemberInfo psMemberInfo + ? psMemberInfo.GetFirstOrDefault(predicate) as T + : null; } #endregion overrides diff --git a/src/System.Management.Automation/engine/MshObject.cs b/src/System.Management.Automation/engine/MshObject.cs index 80d5b90e106..c52c9f1dae0 100644 --- a/src/System.Management.Automation/engine/MshObject.cs +++ b/src/System.Management.Automation/engine/MshObject.cs @@ -2373,7 +2373,6 @@ internal bool ImmediateBaseObjectIsEmpty } } - /// /// If 'this' is non-null, return this string as the ToString() for this wrapped object. /// internal string TokenText { get; set; } @@ -2453,6 +2452,18 @@ internal enum WriteStreamType : byte Information } + internal interface IPSObjectExtendedMemberInfo + { + T GetFirstOrDefault(MemberNamePredicate predicate) where T : PSMemberInfo; + T GetMember(string name) where T : PSMemberInfo; + void AddExtensionMembers(PSMemberInfoInternalCollection returnValue) where T : PSMemberInfo; + } + + [AttributeUsage(AttributeTargets.Property)] + internal class PSExtensionMemberAttribute : Attribute + { + } + /// /// Serves as a placeholder BaseObject when PSObject's /// constructor with no parameters is used. diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index af0a196bd7d..a65bd8391ef 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -5263,7 +5263,7 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy { // For static property access, the target expr must be null. For non-static, we must convert // because target.Expression is typeof(object) because this is a dynamic site. - var targetExpr = _static ? null : GetTargetExpr(target, adapterData.member.DeclaringType); + var targetExpr = _static ? null : GetTargetExpr(target, adapterData.member.DeclaringType, property.baseObject is IPSObjectExtendedMemberInfo); var propertyAccessor = adapterData.member as PropertyInfo; if (propertyAccessor != null) { @@ -5388,14 +5388,14 @@ internal static bool IsGenericDictionary(object value, ref Type genericTypeArg) /// Get the actual value, as an expression, of the object represented by target. This /// will get the base object if it's a psobject, plus correctly handle Nullable. /// - internal static Expression GetTargetExpr(DynamicMetaObject target, Type castToType = null) + internal static Expression GetTargetExpr(DynamicMetaObject target, Type castToType = null, bool useOriginalTargetExpression = false) { var expr = target.Expression; var value = target.Value; // If the target value is actually a deserialized PSObject, we should use the original value var psobj = value as PSObject; - if (psobj != null && psobj != AutomationNull.Value && !psobj.IsDeserialized) + if (psobj != null && psobj != AutomationNull.Value && !psobj.IsDeserialized && !useOriginalTargetExpression) { expr = Expression.Call(CachedReflectionInfo.PSObject_Base, expr); value = PSObject.Base(value); @@ -5678,6 +5678,12 @@ internal PSMemberInfo GetPSMemberInfo(DynamicMetaObject target, memberInfo = adapterSet.DotNetAdapter.BaseGetMember(value, Name); } + if (memberInfo == null && psobj is IPSObjectExtendedMemberInfo psMemberInfo) + { + memberInfo = psMemberInfo.GetMember(Name); + canOptimize = true; + } + // The member came from the type table or an adapter and isn't instance based, so the restriction will start // with a version check restrictions = versionRestriction; diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index 7201271631c..877e1b60362 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -1732,8 +1732,8 @@ private void Dir( // Write out the items foreach (IEnumerable childList in target) { - // On some systems, this is already sorted. For consistency, always sort again. - IEnumerable sortedChildList = childList.OrderBy(c => c.Name, StringComparer.CurrentCultureIgnoreCase); + // On some systems, this is already sorted. + IEnumerable sortedChildList = Platform.IsWindows ? childList : childList.OrderBy(c => c.Name, StringComparer.CurrentCultureIgnoreCase); foreach (FileSystemInfo filesystemInfo in sortedChildList) { @@ -7294,6 +7294,85 @@ internal bool TryVisitPath(string path) } #endregion + + /// + private protected override bool TryGetProviderPSObject(object item, PSDriveInfo driveInfo, out PSObject result) + { + if (item is FileSystemInfo fileSystemInfo) + { + result = new FileSystemPSObject(fileSystemInfo, driveInfo, this); + return true; + } + + result = null; + return false; + } + + internal class FileSystemPSObject : PSObject, IPSObjectExtendedMemberInfo + { + private readonly PSDriveInfo _driveInfo; + private readonly FileSystemProvider _provider; + private string _parentPath; + private string _path; + + public FileSystemPSObject(FileSystemInfo fileSystemInfo, PSDriveInfo driveInfo, FileSystemProvider provider) : base(fileSystemInfo) + { + _driveInfo = driveInfo; + _provider = provider; + } + + private string GetParentPath() => + LocationGlobber.GetProviderQualifiedPath( + _provider.GetParentPath(ObjAsFileSystemInfo.FullName, _driveInfo.Root), + _provider.ProviderInfo); + + private FileSystemInfo ObjAsFileSystemInfo => (FileSystemInfo) ImmediateBaseObject; + + [PSExtensionMember] + public string PSParentPath => _parentPath ??= GetParentPath(); + + [PSExtensionMember] + public string PSPath => _path ??= LocationGlobber.GetProviderQualifiedPath(ObjAsFileSystemInfo.FullName, _provider.ProviderInfo); + + [PSExtensionMember] + public string PSChildName => ObjAsFileSystemInfo.Name; + + [PSExtensionMember] + public bool PSIsContainer => ObjAsFileSystemInfo is DirectoryInfo; + + [PSExtensionMember] + public ProviderInfo PSProvider => _provider.ProviderInfo; + + [PSExtensionMember] + public PSDriveInfo PSDrive => _driveInfo; + + T IPSObjectExtendedMemberInfo.GetFirstOrDefault(MemberNamePredicate predicate) + { + string GetMatchedPropertyName(MemberNamePredicate pred) + { + if (pred(nameof(PSParentPath))) + return nameof(PSParentPath); + if (pred(nameof(PSPath))) + return nameof(PSPath); + if (pred(nameof(PSChildName))) + return nameof(PSChildName); + if (pred(nameof(PSIsContainer))) + return nameof(PSIsContainer); + if (pred(nameof(PSProvider))) + return nameof(PSProvider); + if (pred(nameof(PSDrive))) + return nameof(PSDrive); + return null; + } + + var propertyName = GetMatchedPropertyName(predicate); + return propertyName == null ? null : DotNetInstanceAdapter.GetDotNetProperty(this, propertyName); + } + + T IPSObjectExtendedMemberInfo.GetMember(string name) => DotNetInstanceAdapter.GetDotNetProperty(this, name); + + void IPSObjectExtendedMemberInfo.AddExtensionMembers(PSMemberInfoInternalCollection returnValue) => DotNetInstanceAdapter.AddExtensionProperties(this, returnValue); + } } internal static class SafeInvokeCommand diff --git a/src/System.Management.Automation/namespaces/ProviderBase.cs b/src/System.Management.Automation/namespaces/ProviderBase.cs index 5d706b10d35..847625973b6 100644 --- a/src/System.Management.Automation/namespaces/ProviderBase.cs +++ b/src/System.Management.Automation/namespaces/ProviderBase.cs @@ -92,7 +92,7 @@ public abstract partial class CmdletProvider : IResourceSupplier /// The provider information that is stored by the Monad engine. /// /// - /// If is null. + /// If is null. /// internal void SetProviderInformation(ProviderInfo providerInfoToSet) { @@ -1741,12 +1741,7 @@ private void WriteObject( string path, bool isContainer) { - PSObject result = WrapOutputInPSObject(item, path); - - // Now add the IsContainer - - result.AddOrSetProperty("PSIsContainer", isContainer ? Boxed.True : Boxed.False); - providerBaseTracer.WriteLine("Attaching {0} = {1}", "PSIsContainer", isContainer); + PSObject result = WrapOutputInPSObject(item, path, isContainer); Diagnostics.Assert( Context != null, @@ -1769,7 +1764,7 @@ private void WriteObject( object item, string path) { - PSObject result = WrapOutputInPSObject(item, path); + PSObject result = WrapOutputInPSObject(item, path, isContainer: null); Diagnostics.Assert( Context != null, @@ -1782,12 +1777,9 @@ private void WriteObject( /// Wraps the item in a PSObject and attaches some notes to the /// object that deal with path information. /// - /// - /// The item to be wrapped. - /// - /// - /// The path to the item. - /// + /// The item to be wrapped. + /// The path to the item. + /// True if item is a container. Null if it is unspecified. /// /// A PSObject that wraps the item and has path information attached /// as notes. @@ -1795,15 +1787,19 @@ private void WriteObject( /// /// if is null. /// - private PSObject WrapOutputInPSObject( - object item, - string path) + private PSObject WrapOutputInPSObject(object item, string path, bool? isContainer) { if (item == null) { throw PSTraceSource.NewArgumentNullException("item"); } + if (ExperimentalFeature.IsEnabled("PSOptimizedProviderObjects") + && TryGetProviderPSObject(item, PSDriveInfo, out var providerResult)) + { + return providerResult; + } + PSObject result = new PSObject(item); Diagnostics.Assert( @@ -1813,59 +1809,41 @@ private PSObject WrapOutputInPSObject( // Move the TypeNames to the wrapping object if the wrapped object // was an PSObject - PSObject mshObj = item as PSObject; - if (mshObj != null) + if (item is PSObject mshObj) { result.InternalTypeNames = new ConsolidatedString(mshObj.InternalTypeNames); } + // The provider doesn't support optimized property storage. // Construct a provider qualified path as the Path note - - string providerQualifiedPath = - LocationGlobber.GetProviderQualifiedPath(path, ProviderInfo); + var providerQualifiedPath = LocationGlobber.GetProviderQualifiedPath(path, ProviderInfo); result.AddOrSetProperty("PSPath", providerQualifiedPath); providerBaseTracer.WriteLine("Attaching {0} = {1}", "PSPath", providerQualifiedPath); // Now get the parent path and child name - - NavigationCmdletProvider navProvider = this as NavigationCmdletProvider; - if (navProvider != null && path != null) + if (this is NavigationCmdletProvider navProvider && path != null) { // Get the parent path + var root = PSDriveInfo?.Root ?? string.Empty; - string parentPath = null; - - if (PSDriveInfo != null) - { - parentPath = navProvider.GetParentPath(path, PSDriveInfo.Root, Context); - } - else - { - parentPath = navProvider.GetParentPath(path, string.Empty, Context); - } - - string providerQualifiedParentPath = string.Empty; + var parentPath = navProvider.GetParentPath(path, root, Context); - if (!string.IsNullOrEmpty(parentPath)) - { - providerQualifiedParentPath = - LocationGlobber.GetProviderQualifiedPath(parentPath, ProviderInfo); - } + var providerQualifiedParentPath = !string.IsNullOrEmpty(parentPath) + ? LocationGlobber.GetProviderQualifiedPath(parentPath, ProviderInfo) + : string.Empty; result.AddOrSetProperty("PSParentPath", providerQualifiedParentPath); providerBaseTracer.WriteLine("Attaching {0} = {1}", "PSParentPath", providerQualifiedParentPath); // Get the child name - - string childName = navProvider.GetChildName(path, Context); + var childName = navProvider.GetChildName(path, Context); result.AddOrSetProperty("PSChildName", childName); providerBaseTracer.WriteLine("Attaching {0} = {1}", "PSChildName", childName); } // PSDriveInfo - if (PSDriveInfo != null) { result.AddOrSetProperty(this.PSDriveInfo.GetNotePropertyForProviderCmdlets("PSDrive")); @@ -1873,13 +1851,31 @@ private PSObject WrapOutputInPSObject( } // ProviderInfo - result.AddOrSetProperty(this.ProviderInfo.GetNotePropertyForProviderCmdlets("PSProvider")); providerBaseTracer.WriteLine("Attaching {0} = {1}", "PSProvider", this.ProviderInfo); + if (isContainer.HasValue) + { + result.AddOrSetProperty("PSIsContainer", isContainer.Value ? Boxed.True : Boxed.False); + providerBaseTracer.WriteLine("Attaching {0} = {1}", "PSIsContainer", isContainer); + } + return result; } + /// + /// Gives providers a way of creating optimized PSObjects, and opt out of attaching note-properties. + /// + /// The object to extend. + /// The drive that the belongs to. + /// An instance of an optimized PSObject implementation. + /// true if the result was set, otherwise false. + private protected virtual bool TryGetProviderPSObject(object item, PSDriveInfo driveInfo, out PSObject result) + { + result = null; + return false; + } + /// /// Writes an item to the output as a PSObject with extra data attached /// as notes. diff --git a/src/System.Management.Automation/namespaces/RegistryProvider.cs b/src/System.Management.Automation/namespaces/RegistryProvider.cs index 7aa18ebee6c..e312755b240 100644 --- a/src/System.Management.Automation/namespaces/RegistryProvider.cs +++ b/src/System.Management.Automation/namespaces/RegistryProvider.cs @@ -140,23 +140,11 @@ protected override PSDriveInfo NewDrive(PSDriveInfo drive) /// protected override Collection InitializeDefaultDrives() { - Collection drives = new Collection(); - - drives.Add( - new PSDriveInfo( - "HKLM", - ProviderInfo, - "HKEY_LOCAL_MACHINE", - RegistryProviderStrings.HKLMDriveDescription, - null)); - - drives.Add( - new PSDriveInfo( - "HKCU", - ProviderInfo, - "HKEY_CURRENT_USER", - RegistryProviderStrings.HKCUDriveDescription, - null)); + var drives = new Collection + { + new PSDriveInfo("HKLM", ProviderInfo, "HKEY_LOCAL_MACHINE", RegistryProviderStrings.HKLMDriveDescription, credential: null), + new PSDriveInfo("HKCU", ProviderInfo, "HKEY_CURRENT_USER", RegistryProviderStrings.HKCUDriveDescription, credential: null) + }; return drives; } @@ -4017,9 +4005,6 @@ private void WriteRegistryItemObject( // Escape any wildcard characters in the path path = EscapeSpecialChars(path); - // Wrap the key in an PSObject - PSObject outputObject = PSObject.AsPSObject(key.RegistryKey); - // Add the registry values to the PSObject string[] valueNames = key.GetValueNames(); @@ -4033,7 +4018,14 @@ private void WriteRegistryItemObject( } } - outputObject.AddOrSetProperty("Property", valueNames); + + // Wrap the key in an PSObject + PSObject outputObject = new RegistryPSObject( + (RegistryKey) key.RegistryKey, + PSDriveInfo, + this, + valueNames); + WriteItemObject(outputObject, path, true); } @@ -4145,6 +4137,84 @@ private string GetPropertyName(string userEnteredPropertyName) return result; } #endregion Private members + + private protected override bool TryGetProviderPSObject(object item, PSDriveInfo driveInfo, out PSObject result) + { + if (item is RegistryPSObject obj) + { + result = obj; + return true; + } + + result = null; + return false; + } + + class RegistryPSObject : PSObject, IPSObjectExtendedMemberInfo + { + private readonly RegistryKey _key; + private readonly PSDriveInfo _driveInfo; + private readonly RegistryProvider _provider; + private string _parentPath; + private string _path; + private string _name; + private readonly string[] _properties; + + public RegistryPSObject(RegistryKey key, PSDriveInfo driveInfo, RegistryProvider provider, string[] properties) : base(key) + { + _key = key; + _driveInfo = driveInfo; + _provider = provider; + _properties = properties; + } + + [PSExtensionMember] + public string PSParentPath => _parentPath ??= _provider.GetParentPath(_key.Name, _driveInfo.Root); + + [PSExtensionMember] + public string PSPath => _path ??= LocationGlobber.GetProviderQualifiedPath(_key.Name, _provider.ProviderInfo); + + [PSExtensionMember] + public string PSChildName => _name ??= _provider.GetChildName(_key.Name); + + [PSExtensionMember] + public bool PSIsContainer => true; + + [PSExtensionMember] + public ProviderInfo PSProvider => _driveInfo.Provider; + + [PSExtensionMember] + public PSDriveInfo PSDrive => _driveInfo; + + [PSExtensionMember] + public string[] Property => _properties; + + T IPSObjectExtendedMemberInfo.GetFirstOrDefault(MemberNamePredicate predicate) + { + string GetMatchedPropertyName(MemberNamePredicate pred) + { + if (pred(nameof(PSParentPath))) + return nameof(PSParentPath); + if (pred(nameof(PSPath))) + return nameof(PSPath); + if (pred(nameof(PSChildName))) + return nameof(PSChildName); + if (pred(nameof(PSIsContainer))) + return nameof(PSIsContainer); + if (pred(nameof(PSProvider))) + return nameof(PSProvider); + if (pred(nameof(PSDrive))) + return nameof(PSDrive); + return null; + } + + var propertyName = GetMatchedPropertyName(predicate); + return propertyName == null ? null : DotNetInstanceAdapter.GetDotNetProperty(this, propertyName); + } + + T IPSObjectExtendedMemberInfo.GetMember(string name) => DotNetInstanceAdapter.GetDotNetProperty(this, name); + void IPSObjectExtendedMemberInfo.AddExtensionMembers(PSMemberInfoInternalCollection returnValue) => DotNetInstanceAdapter.AddExtensionProperties(this, returnValue); + } } /// diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1 index 01f866aaaa0..f84b39b6a2c 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/FileSystem.Tests.ps1 @@ -61,6 +61,24 @@ Describe "Basic FileSystem Provider Tests" -Tags "CI" { Remove-Item -Path * -Recurse -Force -ErrorAction SilentlyContinue } + + It "Get-Content on Unix succeeds with folder and file with colon: " -Skip:($IsWindows) + { + try { + $path = ":bar" + $testPath = "$testdrive/hello:world" + New-Item -Path "$testPath" -ItemType Directory > $null + Set-Content -Path "$testPath$path" -Value "Hello" + $files = Get-ChildItem "$testPath" + $files.Count | Should -Be 1 + $files.Name | Should -BeExactly $path.Substring(1,$path.Length-1) + $files | Get-Content | Should -BeExactly "Hello" + } + finally { + Remove-Item -Path $testPath -Recurse -Force -ErrorAction SilentlyContinue + } + } + It "Verify New-Item for directory" { $newDir = New-Item -Path $newTestDir -ItemType Directory $directoryExists = Test-Path $newTestDir @@ -242,8 +260,8 @@ Describe "Basic FileSystem Provider Tests" -Tags "CI" { Set-Content -Path "$testPath$path" -Value "Hello" $files = Get-ChildItem "$testPath" $files.Count | Should -Be 1 - $files[0].Name | Should -BeExactly $path.Substring(1,$path.Length-1) - $files[0] | Get-Content | Should -BeExactly "Hello" + $files.Name | Should -BeExactly $path.Substring(1,$path.Length-1) + $files | Get-Content | Should -BeExactly "Hello" } finally { Remove-Item -Path $testPath -Recurse -Force -ErrorAction SilentlyContinue